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