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