Home | History | Annotate | Download | only in security
      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