• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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