1 /* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 /* 17 * Copyright (c) 2015-2017, The Linux Foundation. 18 */ 19 20 /* 21 * Copyright 2012 Giesecke & Devrient GmbH. 22 * 23 * Licensed under the Apache License, Version 2.0 (the "License"); you may not 24 * use this file except in compliance with the License. You may obtain a copy of 25 * the License at 26 * 27 * http://www.apache.org/licenses/LICENSE-2.0 28 * 29 * Unless required by applicable law or agreed to in writing, software 30 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 31 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 32 * License for the specific language governing permissions and limitations under 33 * the License. 34 */ 35 36 package com.android.se.security; 37 38 import android.os.Build; 39 import android.util.Log; 40 41 import com.android.se.security.gpac.AID_REF_DO; 42 import com.android.se.security.gpac.AR_DO; 43 import com.android.se.security.gpac.Hash_REF_DO; 44 import com.android.se.security.gpac.REF_DO; 45 46 import java.io.PrintWriter; 47 import java.security.AccessControlException; 48 import java.util.ArrayList; 49 import java.util.Arrays; 50 import java.util.HashMap; 51 import java.util.Iterator; 52 import java.util.List; 53 import java.util.Map; 54 import java.util.Set; 55 56 /** Stores all the access rules from the Secure Element */ 57 public class AccessRuleCache { 58 private static final boolean DEBUG = Build.IS_DEBUGGABLE; 59 private final String mTag = "SecureElement-AccessRuleCache"; 60 // Previous "RefreshTag" 61 // 2012-09-25 62 // the refresh tag has to be valid as long as AxxController is valid 63 // a pure static element would cause that rules are not read any longer once the 64 // AxxController is 65 // recreated. 66 private byte[] mRefreshTag = null; 67 private Map<REF_DO, ChannelAccess> mRuleCache = new HashMap<REF_DO, ChannelAccess>(); 68 getAidRefDo(byte[] aid)69 private static AID_REF_DO getAidRefDo(byte[] aid) { 70 byte[] defaultAid = new byte[]{0x00, 0x00, 0x00, 0x00, 0x00}; 71 if (aid == null || Arrays.equals(aid, defaultAid)) { 72 return new AID_REF_DO(AID_REF_DO.TAG_DEFAULT_APPLICATION); 73 } else { 74 return new AID_REF_DO(AID_REF_DO.TAG, aid); 75 } 76 } 77 mapArDo2ChannelAccess(AR_DO arDo)78 private static ChannelAccess mapArDo2ChannelAccess(AR_DO arDo) { 79 ChannelAccess channelAccess = new ChannelAccess(); 80 81 // check apdu access allowance 82 if (arDo.getApduArDo() != null) { 83 // first if there is a rule for access, reset the general deny flag. 84 channelAccess.setAccess(ChannelAccess.ACCESS.ALLOWED, ""); 85 channelAccess.setUseApduFilter(false); 86 87 if (arDo.getApduArDo().isApduAllowed()) { 88 // check the apdu filter 89 ArrayList<byte[]> apduHeaders = arDo.getApduArDo().getApduHeaderList(); 90 ArrayList<byte[]> filterMasks = arDo.getApduArDo().getFilterMaskList(); 91 if (apduHeaders != null && filterMasks != null && apduHeaders.size() > 0 92 && apduHeaders.size() == filterMasks.size()) { 93 ApduFilter[] accessConditions = new ApduFilter[apduHeaders.size()]; 94 for (int i = 0; i < apduHeaders.size(); i++) { 95 accessConditions[i] = new ApduFilter(apduHeaders.get(i), 96 filterMasks.get(i)); 97 } 98 channelAccess.setUseApduFilter(true); 99 channelAccess.setApduFilter(accessConditions); 100 } else { 101 // general APDU access 102 channelAccess.setApduAccess(ChannelAccess.ACCESS.ALLOWED); 103 } 104 } else { 105 // apdu access is not allowed at all. 106 channelAccess.setApduAccess(ChannelAccess.ACCESS.DENIED); 107 } 108 } else { 109 channelAccess.setAccess(ChannelAccess.ACCESS.DENIED, "No APDU access rule available.!"); 110 } 111 112 // check for NFC Event allowance 113 if (arDo.getNfcArDo() != null) { 114 channelAccess.setNFCEventAccess( 115 arDo.getNfcArDo().isNfcAllowed() 116 ? ChannelAccess.ACCESS.ALLOWED 117 : ChannelAccess.ACCESS.DENIED); 118 } else { 119 // GP says that by default NFC should have the same right as for APDU access 120 channelAccess.setNFCEventAccess(channelAccess.getApduAccess()); 121 } 122 return channelAccess; 123 } 124 125 /** Clears access rule cache and refresh tag. */ reset()126 public void reset() { 127 mRefreshTag = null; 128 mRuleCache.clear(); 129 } 130 131 /** Clears access rule cache only. */ clearCache()132 public void clearCache() { 133 mRuleCache.clear(); 134 } 135 136 /** Adds the Rule to the Cache */ putWithMerge(REF_DO refDo, AR_DO arDo)137 public void putWithMerge(REF_DO refDo, AR_DO arDo) { 138 if (refDo.isCarrierPrivilegeRefDo()) { 139 // Ignore Carrier Privilege Rules 140 return; 141 } 142 ChannelAccess channelAccess = mapArDo2ChannelAccess(arDo); 143 putWithMerge(refDo, channelAccess); 144 } 145 146 /** Adds the Rule to the Cache */ putWithMerge(REF_DO refDo, ChannelAccess channelAccess)147 public void putWithMerge(REF_DO refDo, ChannelAccess channelAccess) { 148 if (refDo.isCarrierPrivilegeRefDo()) { 149 // Ignore Carrier Privilege Rules 150 return; 151 } 152 if (mRuleCache.containsKey(refDo)) { 153 ChannelAccess ca = mRuleCache.get(refDo); 154 155 // if new ac condition is more restrictive then use their settings 156 157 if ((channelAccess.getAccess() == ChannelAccess.ACCESS.DENIED) 158 || (ca.getAccess() == ChannelAccess.ACCESS.DENIED)) { 159 ca.setAccess(ChannelAccess.ACCESS.DENIED, channelAccess.getReason()); 160 } else if ((channelAccess.getAccess() == ChannelAccess.ACCESS.UNDEFINED) 161 && (ca.getAccess() != ChannelAccess.ACCESS.UNDEFINED)) { 162 ca.setAccess(ca.getAccess(), ca.getReason()); 163 } else if ((channelAccess.getAccess() != ChannelAccess.ACCESS.UNDEFINED) 164 && (ca.getAccess() == ChannelAccess.ACCESS.UNDEFINED)) { 165 ca.setAccess(channelAccess.getAccess(), channelAccess.getReason()); 166 } else { 167 ca.setAccess(ChannelAccess.ACCESS.ALLOWED, ca.getReason()); 168 } 169 170 // if new rule says NFC is denied then use it 171 // if current rule as undefined NFC rule then use setting of new rule. 172 // current NFC new NFC resulting NFC 173 // UNDEFINED x x 174 // ALLOWED !DENIED ALLOWED 175 // ALLOWED DENIED DENIED 176 // DENIED !DENIED DENIED 177 // DENIED DENIED DENIED 178 179 if ((channelAccess.getNFCEventAccess() == ChannelAccess.ACCESS.DENIED) 180 || (ca.getNFCEventAccess() == ChannelAccess.ACCESS.DENIED)) { 181 ca.setNFCEventAccess(ChannelAccess.ACCESS.DENIED); 182 } else if ((channelAccess.getNFCEventAccess() == ChannelAccess.ACCESS.UNDEFINED) 183 && (ca.getNFCEventAccess() != ChannelAccess.ACCESS.UNDEFINED)) { 184 ca.setNFCEventAccess(ca.getNFCEventAccess()); 185 } else if ((channelAccess.getNFCEventAccess() != ChannelAccess.ACCESS.UNDEFINED) 186 && (ca.getNFCEventAccess() == ChannelAccess.ACCESS.UNDEFINED)) { 187 ca.setNFCEventAccess(channelAccess.getNFCEventAccess()); 188 } else { 189 ca.setNFCEventAccess(ChannelAccess.ACCESS.ALLOWED); 190 } 191 // if new rule says APUD is denied then use it 192 // if current rule as undefined APDU rule then use setting of new rule. 193 // current APDU new APDU resulting APDU 194 // UNDEFINED x x 195 // ALLOWED !DENIED ALLOWED 196 // ALLOWED DENIED DENIED 197 // DENIED !DENIED DENIED 198 // DENEID DENIED DENIED 199 200 if ((channelAccess.getApduAccess() == ChannelAccess.ACCESS.DENIED) 201 || (ca.getApduAccess() == ChannelAccess.ACCESS.DENIED)) { 202 ca.setApduAccess(ChannelAccess.ACCESS.DENIED); 203 } else if ((channelAccess.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED) 204 && (ca.getApduAccess() != ChannelAccess.ACCESS.UNDEFINED)) { 205 ca.setApduAccess(ca.getApduAccess()); 206 } else if ((channelAccess.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED) 207 && (ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED) 208 && !channelAccess.isUseApduFilter()) { 209 ca.setApduAccess(ChannelAccess.ACCESS.DENIED); 210 } else if ((channelAccess.getApduAccess() != ChannelAccess.ACCESS.UNDEFINED) 211 && (ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED)) { 212 ca.setApduAccess(channelAccess.getApduAccess()); 213 } else { 214 ca.setApduAccess(ChannelAccess.ACCESS.ALLOWED); 215 } 216 217 // put APDU filter together if resulting APDU access is allowed. 218 if ((ca.getApduAccess() == ChannelAccess.ACCESS.ALLOWED) 219 || (ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED)) { 220 Log.i(mTag, "Merged Access Rule: APDU filter together"); 221 if (channelAccess.isUseApduFilter()) { 222 ca.setUseApduFilter(true); 223 ApduFilter[] filter = ca.getApduFilter(); 224 ApduFilter[] filter2 = channelAccess.getApduFilter(); 225 if (filter == null || filter.length == 0) { 226 ca.setApduFilter(filter2); 227 } else if (filter2 == null || filter2.length == 0) { 228 ca.setApduFilter(filter); 229 } else { 230 ApduFilter[] sum = new ApduFilter[filter.length + filter2.length]; 231 int i = 0; 232 for (ApduFilter f : filter) { 233 sum[i++] = f; 234 } 235 for (ApduFilter f : filter2) { 236 sum[i++] = f; 237 } 238 ca.setApduFilter(sum); 239 } 240 } 241 } else { 242 // if APDU access is not allowed the remove also all apdu filter. 243 ca.setUseApduFilter(false); 244 ca.setApduFilter(null); 245 } 246 if (DEBUG) { 247 Log.i(mTag, "Merged Access Rule: " + refDo.toString() + ", " + ca.toString()); 248 } 249 return; 250 } 251 if (DEBUG) { 252 Log.i(mTag, "Add Access Rule: " + refDo.toString() + ", " + channelAccess.toString()); 253 } 254 mRuleCache.put(refDo, channelAccess); 255 } 256 257 /** Find Access Rule for the given AID and Application */ findAccessRule(byte[] aid, List<byte[]> appCertHashes)258 public ChannelAccess findAccessRule(byte[] aid, List<byte[]> appCertHashes) 259 throws AccessControlException { 260 ChannelAccess ca = findAccessRuleInternal(aid, appCertHashes); 261 if (ca != null) { 262 if ((ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED) && !ca.isUseApduFilter()) { 263 // Rule for APDU access does not exist. 264 // All the APDU access requests shall never be allowed in this case. 265 // This missing rule resolution is valid for both ARA and ARF 266 // if the supported GP SEAC version is v1.1 or later. 267 ca.setApduAccess(ChannelAccess.ACCESS.DENIED); 268 } 269 if (ca.getNFCEventAccess() == ChannelAccess.ACCESS.UNDEFINED) { 270 // Missing NFC access rule shall be treated as ALLOWED 271 // if relevant APDU access rule is ALLOWED or APDU filter is specified. 272 if (ca.isUseApduFilter()) { 273 ca.setNFCEventAccess(ChannelAccess.ACCESS.ALLOWED); 274 } else { 275 ca.setNFCEventAccess(ca.getApduAccess()); 276 } 277 } 278 // Note that the GP SEAC v1.1 has not been supported as GSMA TS.26 does not require it. 279 } 280 return ca; 281 } 282 findAccessRuleInternal(byte[] aid, List<byte[]> appCertHashes)283 private ChannelAccess findAccessRuleInternal(byte[] aid, List<byte[]> appCertHashes) 284 throws AccessControlException { 285 286 // TODO: check difference between DeviceCertHash and Certificate Chain (EndEntityCertHash, 287 // IntermediateCertHash (1..n), RootCertHash) 288 // The DeviceCertificate is equal to the EndEntityCertificate. 289 // The android systems seems always to deliver only the EndEntityCertificate, but this 290 // seems not 291 // to be sure. 292 // thats why we implement the whole chain. 293 294 295 /* Search Rule A ( Certificate(s); AID ) */ 296 AID_REF_DO aid_ref_do = getAidRefDo(aid); 297 REF_DO ref_do; 298 Hash_REF_DO hash_ref_do; 299 for (byte[] appCertHash : appCertHashes) { 300 hash_ref_do = new Hash_REF_DO(appCertHash); 301 ref_do = new REF_DO(aid_ref_do, hash_ref_do); 302 303 if (mRuleCache.containsKey(ref_do)) { 304 if (DEBUG) { 305 Log.i(mTag, "findAccessRule() Case A " + ref_do.toString() + ", " 306 + mRuleCache.get(ref_do).toString()); 307 } 308 return mRuleCache.get(ref_do); 309 } 310 } 311 // no rule found, 312 // now we have to check if the given AID 313 // is used together with another specific hash value (another device application) 314 if (searchForRulesWithSpecificAidButOtherHash(aid_ref_do) != null) { 315 if (DEBUG) { 316 Log.i(mTag, "Conflict Resolution Case A returning access rule \'NEVER\'."); 317 } 318 ChannelAccess ca = new ChannelAccess(); 319 ca.setApduAccess(ChannelAccess.ACCESS.DENIED); 320 ca.setAccess(ChannelAccess.ACCESS.DENIED, 321 "AID has a specific access rule with a different hash. (Case A)"); 322 ca.setNFCEventAccess(ChannelAccess.ACCESS.DENIED); 323 return ca; 324 } 325 326 // SearchRule B ( <AllDeviceApplications>; AID) 327 aid_ref_do = getAidRefDo(aid); 328 hash_ref_do = new Hash_REF_DO(); // empty hash ref 329 ref_do = new REF_DO(aid_ref_do, hash_ref_do); 330 331 if (mRuleCache.containsKey(ref_do)) { 332 if (DEBUG) { 333 Log.i(mTag, "findAccessRule() Case B " + ref_do.toString() + ", " 334 + mRuleCache.get(ref_do).toString()); 335 } 336 return mRuleCache.get(ref_do); 337 } 338 339 // Search Rule C ( Certificate(s); <AllSEApplications> ) 340 aid_ref_do = new AID_REF_DO(AID_REF_DO.TAG); 341 for (byte[] appCertHash : appCertHashes) { 342 hash_ref_do = new Hash_REF_DO(appCertHash); 343 ref_do = new REF_DO(aid_ref_do, hash_ref_do); 344 345 if (mRuleCache.containsKey(ref_do)) { 346 if (DEBUG) { 347 Log.i(mTag, "findAccessRule() Case C " + ref_do.toString() + ", " 348 + mRuleCache.get(ref_do).toString()); 349 } 350 return mRuleCache.get(ref_do); 351 } 352 } 353 354 // no rule found, 355 // now we have to check if the all AID DO 356 // is used together with another Hash 357 if (searchForRulesWithAllAidButOtherHash() != null) { 358 if (DEBUG) { 359 Log.i(mTag, "Conflict Resolution Case C returning access rule \'NEVER\'."); 360 } 361 ChannelAccess ca = new ChannelAccess(); 362 ca.setApduAccess(ChannelAccess.ACCESS.DENIED); 363 ca.setAccess( 364 ChannelAccess.ACCESS.DENIED, 365 "An access rule with a different hash and all AIDs was found. (Case C)"); 366 ca.setNFCEventAccess(ChannelAccess.ACCESS.DENIED); 367 return ca; 368 } 369 370 // SearchRule D ( <AllDeviceApplications>; <AllSEApplications>) 371 aid_ref_do = new AID_REF_DO(AID_REF_DO.TAG); 372 hash_ref_do = new Hash_REF_DO(); 373 ref_do = new REF_DO(aid_ref_do, hash_ref_do); 374 375 if (mRuleCache.containsKey(ref_do)) { 376 if (DEBUG) { 377 Log.i(mTag, "findAccessRule() Case D " + ref_do.toString() + ", " 378 + mRuleCache.get(ref_do).toString()); 379 } 380 return mRuleCache.get(ref_do); 381 } 382 383 if (DEBUG) Log.i(mTag, "findAccessRule() not found"); 384 return null; 385 } 386 387 /* 388 * The GP_SE_AC spec says: 389 * According to the rule conflict resolution process defined in section 3.2.1, if a specific 390 * rule exists 391 * that associates another device application with the SE application identified by AID (e.g. 392 * there is 393 * a rule associating AID with the hash of another device application), then the ARA-M (when 394 * using GET DATA [Specific]) or the Access Control Enforcer (when using GET DATA [All]) shall 395 * set the result of SearchRuleFor(DeviceApplicationCertificate, AID) to NEVER (i.e. precedence 396 * of specific rules over generic rules) 397 * 398 * In own words: 399 * Search the rules cache for a rule that contains the wanted AID but with another specific 400 * Hash value. 401 */ searchForRulesWithSpecificAidButOtherHash(AID_REF_DO aidRefDo)402 private REF_DO searchForRulesWithSpecificAidButOtherHash(AID_REF_DO aidRefDo) { 403 404 // AID has to be specific 405 if (aidRefDo == null) { 406 return null; 407 } 408 409 // The specified AID_REF_DO does not have any AID and it is not for the default AID. 410 if (aidRefDo.getTag() == AID_REF_DO.TAG && aidRefDo.getAid().length == 0) { 411 return null; 412 } 413 414 Set<REF_DO> keySet = mRuleCache.keySet(); 415 Iterator<REF_DO> iter = keySet.iterator(); 416 while (iter.hasNext()) { 417 REF_DO ref_do = iter.next(); 418 if (aidRefDo.equals(ref_do.getAidDo())) { 419 if (ref_do.getHashDo() != null 420 && ref_do.getHashDo().getHash().length > 0) { 421 // this ref_do contains the search AID and a specific hash value 422 return ref_do; 423 } 424 } 425 } 426 return null; 427 } 428 429 /* 430 * The GP_SE_AC spec says: 431 * According to the rule conflict resolution process defined in section 3.2.1, if a specific 432 * rule exists 433 * that associates another device application with the SE application identified by AID (e.g. 434 * there is 435 * a rule associating AID with the hash of another device application), then the ARA-M (when 436 * using GET DATA [Specific]) or the Access Control Enforcer (when using GET DATA [All]) shall 437 * set the result of SearchRuleFor(DeviceApplicationCertificate, AID) to NEVER (i.e. precedence 438 * of specific rules over generic rules) 439 * 440 * In own words: 441 * Search the rules cache for a rule that contains a Hash with an all SE AID (4F 00). 442 */ searchForRulesWithAllAidButOtherHash()443 private Object searchForRulesWithAllAidButOtherHash() { 444 445 AID_REF_DO aid_ref_do = new AID_REF_DO(AID_REF_DO.TAG); 446 447 Set<REF_DO> keySet = mRuleCache.keySet(); 448 Iterator<REF_DO> iter = keySet.iterator(); 449 while (iter.hasNext()) { 450 REF_DO ref_do = iter.next(); 451 if (aid_ref_do.equals(ref_do.getAidDo())) { 452 // aid tlv is equal 453 if (ref_do.getHashDo() != null 454 && ref_do.getHashDo().getHash().length > 0) { 455 // return ref_do if 456 // a HASH value is available and has a length > 0 (SHA1_LEN) 457 return ref_do; 458 } 459 } 460 } 461 return null; 462 } 463 464 /** Check if the given Refresh Tag is equal to the last known */ isRefreshTagEqual(byte[] refreshTag)465 public boolean isRefreshTagEqual(byte[] refreshTag) { 466 if (refreshTag == null || mRefreshTag == null) return false; 467 468 return Arrays.equals(refreshTag, mRefreshTag); 469 } 470 getRefreshTag()471 public byte[] getRefreshTag() { 472 return mRefreshTag; 473 } 474 475 /** Sets the Refresh Tag */ setRefreshTag(byte[] refreshTag)476 public void setRefreshTag(byte[] refreshTag) { 477 mRefreshTag = refreshTag; 478 } 479 480 /** Debug information to be used by dumpsys */ dump(PrintWriter writer)481 public void dump(PrintWriter writer) { 482 writer.println(mTag + ":"); 483 484 /* Dump the refresh tag */ 485 writer.print("Current refresh tag is: "); 486 if (mRefreshTag == null) { 487 writer.print("<null>"); 488 } else { 489 for (byte oneByte : mRefreshTag) writer.printf("%02X:", oneByte); 490 } 491 writer.println(); 492 493 /* Dump the rules cache */ 494 writer.println("Rules:"); 495 int i = 0; 496 for (Map.Entry<REF_DO, ChannelAccess> entry : mRuleCache.entrySet()) { 497 i++; 498 writer.print("rule " + i + ": "); 499 writer.println(entry.getKey().toString() + " -> " + entry.getValue().toString()); 500 } 501 writer.println(); 502 } 503 } 504