Add support for SHA-256 Hash_REF_DO

Bug: 127536923
Test: run cts CtsSecureElementAccessControlTestCases*
Change-Id: Ia76ca7fde4b9bf3fe2f1c0b0cf770910362b6446
diff --git a/src/com/android/se/security/AccessControlEnforcer.java b/src/com/android/se/security/AccessControlEnforcer.java
index 99ec165..2366ce7 100644
--- a/src/com/android/se/security/AccessControlEnforcer.java
+++ b/src/com/android/se/security/AccessControlEnforcer.java
@@ -50,18 +50,13 @@
 import com.android.se.security.ara.AraController;
 import com.android.se.security.arf.ArfController;
 
-import java.io.ByteArrayInputStream;
 import java.io.IOException;
 import java.io.PrintWriter;
 import java.security.AccessControlException;
 import java.security.MessageDigest;
 import java.security.NoSuchAlgorithmException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
-import java.security.cert.CertificateException;
-import java.security.cert.CertificateFactory;
-import java.security.cert.X509Certificate;
 import java.util.ArrayList;
+import java.util.List;
 import java.util.MissingResourceException;
 import java.util.NoSuchElementException;
 
@@ -91,28 +86,6 @@
         return AraController.getAraMAid();
     }
 
-    private static Certificate decodeCertificate(byte[] certData) throws CertificateException {
-        CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
-        X509Certificate cert =
-                (X509Certificate) certFactory.generateCertificate(
-                        new ByteArrayInputStream(certData));
-        return cert;
-    }
-
-    /** Returns the Hash of the Application */
-    public static byte[] getAppCertHash(Certificate appCert) throws CertificateEncodingException {
-        MessageDigest md = null;
-        try {
-            md = MessageDigest.getInstance("SHA");
-        } catch (NoSuchAlgorithmException e) {
-            throw new AccessControlException("Exception getting SHA for the signature");
-        }
-        if (md == null) {
-            throw new AccessControlException("Hash can not be computed");
-        }
-        return md.digest(appCert.getEncoded());
-    }
-
     public PackageManager getPackageManager() {
         return mPackageManager;
     }
@@ -323,17 +296,17 @@
             throw new AccessControlException("package names must be specified");
         }
         try {
-            // estimate SHA-1 hash value of the device application's certificate.
-            Certificate[] appCerts = getAPPCerts(packageName);
+            // estimate SHA-1 and SHA-256 hash values of the device application's certificate.
+            List<byte[]> appCertHashes = getAppCertHashes(packageName);
             // APP certificates must be available => otherwise Exception
-            if (appCerts == null || appCerts.length == 0) {
+            if (appCertHashes == null || appCertHashes.size() == 0) {
                 throw new AccessControlException(
                         "Application certificates are invalid or do not exist.");
             }
             if (checkRefreshTag) {
                 updateAccessRuleIfNeed();
             }
-            return getAccessRule(aid, appCerts);
+            return getAccessRule(aid, appCertHashes);
         } catch (IOException | MissingResourceException e) {
             throw e;
         } catch (Throwable exp) {
@@ -343,13 +316,13 @@
 
     /** Fetches the Access Rules for the given application and AID pair */
     public ChannelAccess getAccessRule(
-            byte[] aid, Certificate[] appCerts)
-            throws AccessControlException, CertificateEncodingException {
+            byte[] aid, List<byte []> appCertHashes)
+            throws AccessControlException {
         ChannelAccess channelAccess = null;
         // if read all is true get rule from cache.
         if (mRulesRead) {
             // get rules from internal storage
-            channelAccess = mAccessRuleCache.findAccessRule(aid, appCerts);
+            channelAccess = mAccessRuleCache.findAccessRule(aid, appCertHashes);
         }
         // if no rule was found return an empty access rule
         // with all access denied.
@@ -363,10 +336,10 @@
     }
 
     /**
-     * Returns Certificate chain for one package.
+     * Returns hashes of certificate chain for one package.
      */
-    private Certificate[] getAPPCerts(String packageName)
-            throws CertificateException, NoSuchAlgorithmException, AccessControlException {
+    private List<byte[]> getAppCertHashes(String packageName)
+            throws NoSuchAlgorithmException, AccessControlException {
         if (packageName == null || packageName.length() == 0) {
             throw new AccessControlException("Package Name not defined");
         }
@@ -380,11 +353,17 @@
         if (foundPkgInfo == null) {
             throw new AccessControlException("Package does not exist");
         }
-        ArrayList<Certificate> appCerts = new ArrayList<Certificate>();
-        for (Signature signature : foundPkgInfo.signatures) {
-            appCerts.add(decodeCertificate(signature.toByteArray()));
+        MessageDigest md = MessageDigest.getInstance("SHA");
+        MessageDigest md256 = MessageDigest.getInstance("SHA-256");
+        if (md == null || md256 == null) {
+            throw new AccessControlException("Hash can not be computed");
         }
-        return appCerts.toArray(new Certificate[appCerts.size()]);
+        List<byte[]> appCertHashes = new ArrayList<byte[]>();
+        for (Signature signature : foundPkgInfo.signatures) {
+            appCertHashes.add(md.digest(signature.toByteArray()));
+            appCertHashes.add(md256.digest(signature.toByteArray()));
+        }
+        return appCertHashes;
     }
 
     /** Returns true if the given application is allowed to recieve NFC Events */
@@ -409,14 +388,14 @@
         int i = 0;
         boolean[] nfcEventFlags = new boolean[packageNames.length];
         for (String packageName : packageNames) {
-            // estimate SHA-1 hash value of the device application's certificate.
+            // estimate hash value of the device application's certificate.
             try {
-                Certificate[] appCerts = getAPPCerts(packageName);
+                List<byte[]> appCertHashes = getAppCertHashes(packageName);
                 // APP certificates must be available => otherwise Exception
-                if (appCerts == null || appCerts.length == 0) {
+                if (appCertHashes == null || appCertHashes.size() == 0) {
                     nfcEventFlags[i] = false;
                 } else {
-                    ChannelAccess channelAccess = getAccessRule(aid, appCerts);
+                    ChannelAccess channelAccess = getAccessRule(aid, appCertHashes);
                     nfcEventFlags[i] =
                             (channelAccess.getNFCEventAccess() == ChannelAccess.ACCESS.ALLOWED);
                 }
diff --git a/src/com/android/se/security/AccessRuleCache.java b/src/com/android/se/security/AccessRuleCache.java
index 8e93e58..8461c49 100644
--- a/src/com/android/se/security/AccessRuleCache.java
+++ b/src/com/android/se/security/AccessRuleCache.java
@@ -45,12 +45,11 @@
 
 import java.io.PrintWriter;
 import java.security.AccessControlException;
-import java.security.cert.Certificate;
-import java.security.cert.CertificateEncodingException;
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.HashMap;
 import java.util.Iterator;
+import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
@@ -248,7 +247,7 @@
     }
 
     /** Find Access Rule for the given AID and Application */
-    public ChannelAccess findAccessRule(byte[] aid, Certificate[] appCerts)
+    public ChannelAccess findAccessRule(byte[] aid, List<byte[]> appCertHashes)
             throws AccessControlException {
 
         // TODO: check difference between DeviceCertHash and Certificate Chain (EndEntityCertHash,
@@ -264,29 +263,25 @@
         AID_REF_DO aid_ref_do = getAidRefDo(aid);
         REF_DO ref_do;
         Hash_REF_DO hash_ref_do;
-        for (Certificate appCert : appCerts) {
-            try {
-                hash_ref_do = new Hash_REF_DO(AccessControlEnforcer.getAppCertHash(appCert));
-                ref_do = new REF_DO(aid_ref_do, hash_ref_do);
+        for (byte[] appCertHash : appCertHashes) {
+            hash_ref_do = new Hash_REF_DO(appCertHash);
+            ref_do = new REF_DO(aid_ref_do, hash_ref_do);
 
-                if (mRuleCache.containsKey(ref_do)) {
-                    // let's take care about the undefined rules, according to the GP specification:
-                    ChannelAccess ca = mRuleCache.get(ref_do);
-                    if (ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED) {
-                        ca.setApduAccess(ChannelAccess.ACCESS.DENIED);
-                    }
-                    if ((ca.getNFCEventAccess() == ChannelAccess.ACCESS.UNDEFINED)
-                            && (ca.getApduAccess() != ChannelAccess.ACCESS.UNDEFINED)) {
-                        ca.setNFCEventAccess(ca.getApduAccess());
-                    }
-                    if (DEBUG) {
-                        Log.i(mTag, "findAccessRule() " + ref_do.toString() + ", "
-                                + mRuleCache.get(ref_do).toString());
-                    }
-                    return mRuleCache.get(ref_do);
+            if (mRuleCache.containsKey(ref_do)) {
+                // let's take care about the undefined rules, according to the GP specification:
+                ChannelAccess ca = mRuleCache.get(ref_do);
+                if (ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED) {
+                    ca.setApduAccess(ChannelAccess.ACCESS.DENIED);
                 }
-            } catch (CertificateEncodingException e) {
-                throw new AccessControlException("Problem with Application Certificate.");
+                if ((ca.getNFCEventAccess() == ChannelAccess.ACCESS.UNDEFINED)
+                        && (ca.getApduAccess() != ChannelAccess.ACCESS.UNDEFINED)) {
+                    ca.setNFCEventAccess(ca.getApduAccess());
+                }
+                if (DEBUG) {
+                    Log.i(mTag, "findAccessRule() " + ref_do.toString() + ", "
+                            + mRuleCache.get(ref_do).toString());
+                }
+                return mRuleCache.get(ref_do);
             }
         }
         // no rule found,
@@ -328,29 +323,25 @@
 
         // Search Rule C ( Certificate(s); <AllSEApplications> )
         aid_ref_do = new AID_REF_DO(AID_REF_DO.TAG);
-        for (Certificate appCert : appCerts) {
-            try {
-                hash_ref_do = new Hash_REF_DO(AccessControlEnforcer.getAppCertHash(appCert));
-                ref_do = new REF_DO(aid_ref_do, hash_ref_do);
+        for (byte[] appCertHash : appCertHashes) {
+            hash_ref_do = new Hash_REF_DO(appCertHash);
+            ref_do = new REF_DO(aid_ref_do, hash_ref_do);
 
-                if (mRuleCache.containsKey(ref_do)) {
-                    // let's take care about the undefined rules, according to the GP specification:
-                    ChannelAccess ca = mRuleCache.get(ref_do);
-                    if (ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED) {
-                        ca.setApduAccess(ChannelAccess.ACCESS.DENIED);
-                    }
-                    if ((ca.getNFCEventAccess() == ChannelAccess.ACCESS.UNDEFINED)
-                            && (ca.getApduAccess() != ChannelAccess.ACCESS.UNDEFINED)) {
-                        ca.setNFCEventAccess(ca.getApduAccess());
-                    }
-                    if (DEBUG) {
-                        Log.i(mTag, "findAccessRule() " + ref_do.toString() + ", "
-                                + mRuleCache.get(ref_do).toString());
-                    }
-                    return mRuleCache.get(ref_do);
+            if (mRuleCache.containsKey(ref_do)) {
+                // let's take care about the undefined rules, according to the GP specification:
+                ChannelAccess ca = mRuleCache.get(ref_do);
+                if (ca.getApduAccess() == ChannelAccess.ACCESS.UNDEFINED) {
+                    ca.setApduAccess(ChannelAccess.ACCESS.DENIED);
                 }
-            } catch (CertificateEncodingException e) {
-                throw new AccessControlException("Problem with Application Certificate.");
+                if ((ca.getNFCEventAccess() == ChannelAccess.ACCESS.UNDEFINED)
+                        && (ca.getApduAccess() != ChannelAccess.ACCESS.UNDEFINED)) {
+                    ca.setNFCEventAccess(ca.getApduAccess());
+                }
+                if (DEBUG) {
+                    Log.i(mTag, "findAccessRule() " + ref_do.toString() + ", "
+                            + mRuleCache.get(ref_do).toString());
+                }
+                return mRuleCache.get(ref_do);
             }
         }
 
diff --git a/src/com/android/se/security/arf/PKCS15/EFACConditions.java b/src/com/android/se/security/arf/PKCS15/EFACConditions.java
index 1068969..26b150a 100644
--- a/src/com/android/se/security/arf/PKCS15/EFACConditions.java
+++ b/src/com/android/se/security/arf/PKCS15/EFACConditions.java
@@ -149,8 +149,9 @@
                     certificateHash = derRule.getTLVData();
 
                     if (certificateHash.length != Hash_REF_DO.SHA1_LEN
+                            && certificateHash.length != Hash_REF_DO.SHA256_LEN
                             && certificateHash.length != 0) {
-                        // other hash than SHA-1 hash values are not supported.
+                        // other hash than SHA-1 and SHA-256 hash values are not supported.
                         throw new PKCS15Exception("Invalid hash found!");
                     } else {
                         hash_ref_do = new Hash_REF_DO(certificateHash);
diff --git a/src/com/android/se/security/gpac/Hash_REF_DO.java b/src/com/android/se/security/gpac/Hash_REF_DO.java
index 7b8a861..edd019a 100644
--- a/src/com/android/se/security/gpac/Hash_REF_DO.java
+++ b/src/com/android/se/security/gpac/Hash_REF_DO.java
@@ -47,6 +47,7 @@
 
     public static final int TAG = 0xC1;
     public static final int SHA1_LEN = 20;
+    public static final int SHA256_LEN = 32;
 
     private byte[] mHash = new byte[0];
 
@@ -92,12 +93,14 @@
     }
 
     /**
-     * Tags: C1 Length: 0 or SHA1_LEN bytes
+     * Tags: C1 Length: 0 or SHA1_LEN or SHA256_LEN bytes
      *
      * <p>Value: Hash: identifies a specific device application Empty: refers to all device
      * applications
      *
-     * <p>Length: SHA1_LEN for 20 bytes SHA-1 hash value 0 for empty value field
+     * <p>Length: SHA1_LEN for 20 bytes SHA-1 hash value
+     *            SHA256_LEN for 32 bytes SHA-256 hash value
+     *            0 for empty value field
      */
     @Override
     public void interpret() throws ParserException {
@@ -106,19 +109,20 @@
 
         byte[] data = getRawData();
         int index = getValueIndex();
+        int length = getValueLength();
 
         // sanity checks
-        if (getValueLength() != 0 && getValueLength() != SHA1_LEN) {
+        if (length != 0 && length != SHA1_LEN && length != SHA256_LEN) {
             throw new ParserException("Invalid value length for Hash-REF-DO!");
         }
 
-        if (getValueLength() == SHA1_LEN) {
-            if (index + getValueLength() > data.length) {
+        if (length != 0) {
+            if (index + length > data.length) {
                 throw new ParserException("Not enough data for Hash-REF-DO!");
             }
 
-            mHash = new byte[getValueLength()];
-            System.arraycopy(data, index, mHash, 0, getValueLength());
+            mHash = new byte[length];
+            System.arraycopy(data, index, mHash, 0, length);
         }
     }
 
@@ -128,14 +132,17 @@
      * <p>Value: Hash: identifies a specific device application Empty: refers to all device
      * applications
      *
-     * <p>Length: SHA1_LEN for 20 bytes SHA-1 hash value 0 for empty value field
+     * <p>Length: 20 for SHA-1 hash or
+     *            32 bytes for SHA-256 hash or
+     *            0 for empty value field
      */
     @Override
     public void build(ByteArrayOutputStream stream) throws DO_Exception {
 
         // sanity checks
-        if (!(mHash.length != SHA1_LEN || mHash.length != 0)) {
-            throw new DO_Exception("Hash value must be " + SHA1_LEN + " bytes in length!");
+        if (mHash.length != SHA1_LEN && mHash.length != SHA256_LEN && mHash.length != 0) {
+            throw new DO_Exception("Hash value must be " + SHA1_LEN + " or " + SHA256_LEN
+                    + " bytes in length!");
         }
 
         stream.write(getTag());