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