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());