Keystore 2.0 SPI: Evolve Factory SPI
We no longer need to get the key characteristics from the Keystore daemon to construct the KeyInfo for a key. Also we have to extract the key info from the KeyParameter AIDL type rather than from the hand written KeymasterArguments. This patch also exposes the correct security level for a key through KeyInfo. Bug: 159476414 Test: None Change-Id: I86a85e481e19fdadfed38a42aeac4ffe5f8b83fa
This commit is contained in:
@@ -16,7 +16,6 @@
|
||||
|
||||
package android.security.keystore2;
|
||||
|
||||
import android.security.Credentials;
|
||||
import android.security.KeyStore;
|
||||
import android.security.keystore.KeyGenParameterSpec;
|
||||
import android.security.keystore.KeyInfo;
|
||||
@@ -64,18 +63,9 @@ public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
|
||||
"Unsupported key type: " + key.getClass().getName()
|
||||
+ ". KeyInfo can be obtained only for Android Keystore private keys");
|
||||
}
|
||||
AndroidKeyStorePrivateKey
|
||||
keystorePrivateKey = (AndroidKeyStorePrivateKey) key;
|
||||
String keyAliasInKeystore = keystorePrivateKey.getAlias();
|
||||
String entryAlias;
|
||||
if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
|
||||
entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
|
||||
}
|
||||
AndroidKeyStorePrivateKey keystorePrivateKey = (AndroidKeyStorePrivateKey) key;
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(
|
||||
mKeyStore, entryAlias, keyAliasInKeystore, keystorePrivateKey.getUid());
|
||||
T result = (T) AndroidKeyStoreSecretKeyFactorySpi.getKeyInfo(keystorePrivateKey);
|
||||
return result;
|
||||
} else if (X509EncodedKeySpec.class.equals(keySpecClass)) {
|
||||
if (!(key instanceof AndroidKeyStorePublicKey)) {
|
||||
@@ -98,8 +88,7 @@ public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
|
||||
}
|
||||
} else if (RSAPublicKeySpec.class.equals(keySpecClass)) {
|
||||
if (key instanceof AndroidKeyStoreRSAPublicKey) {
|
||||
AndroidKeyStoreRSAPublicKey
|
||||
rsaKey = (AndroidKeyStoreRSAPublicKey) key;
|
||||
AndroidKeyStoreRSAPublicKey rsaKey = (AndroidKeyStoreRSAPublicKey) key;
|
||||
@SuppressWarnings("unchecked")
|
||||
T result =
|
||||
(T) new RSAPublicKeySpec(rsaKey.getModulus(), rsaKey.getPublicExponent());
|
||||
@@ -112,8 +101,7 @@ public class AndroidKeyStoreKeyFactorySpi extends KeyFactorySpi {
|
||||
}
|
||||
} else if (ECPublicKeySpec.class.equals(keySpecClass)) {
|
||||
if (key instanceof AndroidKeyStoreECPublicKey) {
|
||||
AndroidKeyStoreECPublicKey
|
||||
ecKey = (AndroidKeyStoreECPublicKey) key;
|
||||
AndroidKeyStoreECPublicKey ecKey = (AndroidKeyStoreECPublicKey) key;
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) new ECPublicKeySpec(ecKey.getW(), ecKey.getParams());
|
||||
return result;
|
||||
|
||||
@@ -16,13 +16,15 @@
|
||||
|
||||
package android.security.keystore2;
|
||||
|
||||
import android.security.Credentials;
|
||||
import android.annotation.NonNull;
|
||||
import android.security.GateKeeper;
|
||||
import android.security.KeyStore;
|
||||
import android.security.keymaster.KeyCharacteristics;
|
||||
import android.security.keymaster.KeymasterArguments;
|
||||
import android.security.keymaster.KeymasterDefs;
|
||||
import android.security.keystore.KeyGenParameterSpec;
|
||||
import android.security.keystore.KeyInfo;
|
||||
import android.security.keystore.KeyProperties;
|
||||
import android.system.keystore2.Authorization;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.InvalidKeyException;
|
||||
@@ -64,137 +66,161 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
|
||||
throw new InvalidKeySpecException("Unsupported key spec: " + keySpecClass.getName());
|
||||
}
|
||||
AndroidKeyStoreKey keystoreKey = (AndroidKeyStoreKey) key;
|
||||
String keyAliasInKeystore = keystoreKey.getAlias();
|
||||
String entryAlias;
|
||||
if (keyAliasInKeystore.startsWith(Credentials.USER_PRIVATE_KEY)) {
|
||||
entryAlias = keyAliasInKeystore.substring(Credentials.USER_PRIVATE_KEY.length());
|
||||
} else if (keyAliasInKeystore.startsWith(Credentials.USER_SECRET_KEY)){
|
||||
// key has legacy prefix
|
||||
entryAlias = keyAliasInKeystore.substring(Credentials.USER_SECRET_KEY.length());
|
||||
} else {
|
||||
throw new InvalidKeySpecException("Invalid key alias: " + keyAliasInKeystore);
|
||||
}
|
||||
|
||||
return getKeyInfo(mKeyStore, entryAlias, keyAliasInKeystore, keystoreKey.getUid());
|
||||
return getKeyInfo(keystoreKey);
|
||||
}
|
||||
|
||||
static KeyInfo getKeyInfo(KeyStore keyStore, String entryAlias, String keyAliasInKeystore,
|
||||
int keyUid) {
|
||||
KeyCharacteristics keyCharacteristics = new KeyCharacteristics();
|
||||
int errorCode = keyStore.getKeyCharacteristics(
|
||||
keyAliasInKeystore, null, null, keyUid, keyCharacteristics);
|
||||
if (errorCode != KeyStore.NO_ERROR) {
|
||||
throw new ProviderException("Failed to obtain information about key."
|
||||
+ " Keystore error: " + errorCode);
|
||||
}
|
||||
static @NonNull KeyInfo getKeyInfo(@NonNull AndroidKeyStoreKey key) {
|
||||
|
||||
boolean insideSecureHardware;
|
||||
@KeyProperties.OriginEnum int origin;
|
||||
int keySize;
|
||||
@KeyProperties.PurposeEnum int purposes;
|
||||
@KeyProperties.SecurityLevelEnum int securityLevel =
|
||||
KeyProperties.SECURITY_LEVEL_SOFTWARE;
|
||||
boolean insideSecureHardware = false;
|
||||
@KeyProperties.OriginEnum int origin = -1;
|
||||
int keySize = -1;
|
||||
@KeyProperties.PurposeEnum int purposes = 0;
|
||||
String[] encryptionPaddings;
|
||||
String[] signaturePaddings;
|
||||
@KeyProperties.DigestEnum String[] digests;
|
||||
@KeyProperties.BlockModeEnum String[] blockModes;
|
||||
int keymasterSwEnforcedUserAuthenticators;
|
||||
int keymasterHwEnforcedUserAuthenticators;
|
||||
List<BigInteger> keymasterSecureUserIds;
|
||||
List<String> digestsList = new ArrayList<>();
|
||||
List<String> blockModesList = new ArrayList<>();
|
||||
int keymasterSwEnforcedUserAuthenticators = 0;
|
||||
int keymasterHwEnforcedUserAuthenticators = 0;
|
||||
List<BigInteger> keymasterSecureUserIds = new ArrayList<BigInteger>();
|
||||
List<String> encryptionPaddingsList = new ArrayList<String>();
|
||||
List<String> signaturePaddingsList = new ArrayList<String>();
|
||||
Date keyValidityStart = null;
|
||||
Date keyValidityForOriginationEnd = null;
|
||||
Date keyValidityForConsumptionEnd = null;
|
||||
long userAuthenticationValidityDurationSeconds = 0;
|
||||
boolean userAuthenticationRequired = true;
|
||||
boolean userAuthenticationValidWhileOnBody = false;
|
||||
boolean trustedUserPresenceRequired = false;
|
||||
boolean trustedUserConfirmationRequired = false;
|
||||
try {
|
||||
if (keyCharacteristics.hwEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
|
||||
insideSecureHardware = true;
|
||||
origin = KeyProperties.Origin.fromKeymaster(
|
||||
keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
|
||||
} else if (keyCharacteristics.swEnforced.containsTag(KeymasterDefs.KM_TAG_ORIGIN)) {
|
||||
insideSecureHardware = false;
|
||||
origin = KeyProperties.Origin.fromKeymaster(
|
||||
keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_ORIGIN, -1));
|
||||
} else {
|
||||
throw new ProviderException("Key origin not available");
|
||||
}
|
||||
long keySizeUnsigned =
|
||||
keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_KEY_SIZE, -1);
|
||||
if (keySizeUnsigned == -1) {
|
||||
throw new ProviderException("Key size not available");
|
||||
} else if (keySizeUnsigned > Integer.MAX_VALUE) {
|
||||
throw new ProviderException("Key too large: " + keySizeUnsigned + " bits");
|
||||
}
|
||||
keySize = (int) keySizeUnsigned;
|
||||
purposes = KeyProperties.Purpose.allFromKeymaster(
|
||||
keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PURPOSE));
|
||||
|
||||
List<String> encryptionPaddingsList = new ArrayList<String>();
|
||||
List<String> signaturePaddingsList = new ArrayList<String>();
|
||||
// Keymaster stores both types of paddings in the same array -- we split it into two.
|
||||
for (int keymasterPadding : keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_PADDING)) {
|
||||
try {
|
||||
@KeyProperties.EncryptionPaddingEnum String jcaPadding =
|
||||
KeyProperties.EncryptionPadding.fromKeymaster(keymasterPadding);
|
||||
encryptionPaddingsList.add(jcaPadding);
|
||||
} catch (IllegalArgumentException e) {
|
||||
try {
|
||||
@KeyProperties.SignaturePaddingEnum String padding =
|
||||
KeyProperties.SignaturePadding.fromKeymaster(keymasterPadding);
|
||||
signaturePaddingsList.add(padding);
|
||||
} catch (IllegalArgumentException e2) {
|
||||
throw new ProviderException(
|
||||
"Unsupported encryption padding: " + keymasterPadding);
|
||||
}
|
||||
for (Authorization a : key.getAuthorizations()) {
|
||||
switch (a.keyParameter.tag) {
|
||||
case KeymasterDefs.KM_TAG_ORIGIN:
|
||||
insideSecureHardware =
|
||||
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
|
||||
securityLevel = a.securityLevel;
|
||||
origin = KeyProperties.Origin.fromKeymaster(a.keyParameter.integer);
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_KEY_SIZE:
|
||||
long keySizeUnsigned = KeyStore2ParameterUtils.getUnsignedInt(a);
|
||||
if (keySizeUnsigned > Integer.MAX_VALUE) {
|
||||
throw new ProviderException(
|
||||
"Key too large: " + keySizeUnsigned + " bits");
|
||||
}
|
||||
keySize = (int) keySizeUnsigned;
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_PURPOSE:
|
||||
purposes |= KeyProperties.Purpose.fromKeymaster(a.keyParameter.integer);
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_PADDING:
|
||||
try {
|
||||
if (a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PKCS1_1_5_SIGN
|
||||
|| a.keyParameter.integer == KeymasterDefs.KM_PAD_RSA_PSS) {
|
||||
@KeyProperties.SignaturePaddingEnum String padding =
|
||||
KeyProperties.SignaturePadding.fromKeymaster(
|
||||
a.keyParameter.integer);
|
||||
signaturePaddingsList.add(padding);
|
||||
} else {
|
||||
@KeyProperties.EncryptionPaddingEnum String jcaPadding =
|
||||
KeyProperties.EncryptionPadding.fromKeymaster(
|
||||
a.keyParameter.integer);
|
||||
encryptionPaddingsList.add(jcaPadding);
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ProviderException("Unsupported padding: "
|
||||
+ a.keyParameter.integer);
|
||||
}
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_DIGEST:
|
||||
digestsList.add(KeyProperties.Digest.fromKeymaster(a.keyParameter.integer));
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_BLOCK_MODE:
|
||||
blockModesList.add(
|
||||
KeyProperties.BlockMode.fromKeymaster(a.keyParameter.integer)
|
||||
);
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_USER_AUTH_TYPE:
|
||||
if (KeyStore2ParameterUtils.isSecureHardware(a.securityLevel)) {
|
||||
keymasterHwEnforcedUserAuthenticators = a.keyParameter.integer;
|
||||
} else {
|
||||
keymasterSwEnforcedUserAuthenticators = a.keyParameter.integer;
|
||||
}
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_USER_SECURE_ID:
|
||||
keymasterSecureUserIds.add(
|
||||
KeymasterArguments.toUint64(a.keyParameter.longInteger));
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_ACTIVE_DATETIME:
|
||||
keyValidityStart = KeyStore2ParameterUtils.getDate(a);
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME:
|
||||
keyValidityForOriginationEnd =
|
||||
KeyStore2ParameterUtils.getDate(a);
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME:
|
||||
keyValidityForConsumptionEnd =
|
||||
KeyStore2ParameterUtils.getDate(a);
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED:
|
||||
userAuthenticationRequired = false;
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_AUTH_TIMEOUT:
|
||||
userAuthenticationValidityDurationSeconds =
|
||||
KeyStore2ParameterUtils.getUnsignedInt(a);
|
||||
if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
|
||||
throw new ProviderException(
|
||||
"User authentication timeout validity too long: "
|
||||
+ userAuthenticationValidityDurationSeconds + " seconds");
|
||||
}
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY:
|
||||
userAuthenticationValidWhileOnBody =
|
||||
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED:
|
||||
trustedUserPresenceRequired =
|
||||
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
|
||||
break;
|
||||
case KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED:
|
||||
trustedUserConfirmationRequired =
|
||||
KeyStore2ParameterUtils.isSecureHardware(a.securityLevel);
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
encryptionPaddings =
|
||||
encryptionPaddingsList.toArray(new String[encryptionPaddingsList.size()]);
|
||||
signaturePaddings =
|
||||
signaturePaddingsList.toArray(new String[signaturePaddingsList.size()]);
|
||||
|
||||
digests = KeyProperties.Digest.allFromKeymaster(
|
||||
keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_DIGEST));
|
||||
blockModes = KeyProperties.BlockMode.allFromKeymaster(
|
||||
keyCharacteristics.getEnums(KeymasterDefs.KM_TAG_BLOCK_MODE));
|
||||
keymasterSwEnforcedUserAuthenticators =
|
||||
keyCharacteristics.swEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
|
||||
keymasterHwEnforcedUserAuthenticators =
|
||||
keyCharacteristics.hwEnforced.getEnum(KeymasterDefs.KM_TAG_USER_AUTH_TYPE, 0);
|
||||
keymasterSecureUserIds =
|
||||
keyCharacteristics.getUnsignedLongs(KeymasterDefs.KM_TAG_USER_SECURE_ID);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new ProviderException("Unsupported key characteristic", e);
|
||||
}
|
||||
|
||||
Date keyValidityStart = keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ACTIVE_DATETIME);
|
||||
Date keyValidityForOriginationEnd =
|
||||
keyCharacteristics.getDate(KeymasterDefs.KM_TAG_ORIGINATION_EXPIRE_DATETIME);
|
||||
Date keyValidityForConsumptionEnd =
|
||||
keyCharacteristics.getDate(KeymasterDefs.KM_TAG_USAGE_EXPIRE_DATETIME);
|
||||
boolean userAuthenticationRequired =
|
||||
!keyCharacteristics.getBoolean(KeymasterDefs.KM_TAG_NO_AUTH_REQUIRED);
|
||||
long userAuthenticationValidityDurationSeconds =
|
||||
keyCharacteristics.getUnsignedInt(KeymasterDefs.KM_TAG_AUTH_TIMEOUT, 0);
|
||||
if (userAuthenticationValidityDurationSeconds > Integer.MAX_VALUE) {
|
||||
throw new ProviderException("User authentication timeout validity too long: "
|
||||
+ userAuthenticationValidityDurationSeconds + " seconds");
|
||||
if (keySize == -1) {
|
||||
throw new ProviderException("Key size not available");
|
||||
}
|
||||
if (origin == -1) {
|
||||
throw new ProviderException("Key origin not available");
|
||||
}
|
||||
|
||||
encryptionPaddings =
|
||||
encryptionPaddingsList.toArray(new String[0]);
|
||||
signaturePaddings =
|
||||
signaturePaddingsList.toArray(new String[0]);
|
||||
|
||||
boolean userAuthenticationRequirementEnforcedBySecureHardware = (userAuthenticationRequired)
|
||||
&& (keymasterHwEnforcedUserAuthenticators != 0)
|
||||
&& (keymasterSwEnforcedUserAuthenticators == 0);
|
||||
boolean userAuthenticationValidWhileOnBody =
|
||||
keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_ALLOW_WHILE_ON_BODY);
|
||||
boolean trustedUserPresenceRequired =
|
||||
keyCharacteristics.hwEnforced.getBoolean(
|
||||
KeymasterDefs.KM_TAG_TRUSTED_USER_PRESENCE_REQUIRED);
|
||||
|
||||
String[] digests = digestsList.toArray(new String[0]);
|
||||
String[] blockModes = blockModesList.toArray(new String[0]);
|
||||
|
||||
boolean invalidatedByBiometricEnrollment = false;
|
||||
if (keymasterSwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC
|
||||
|| keymasterHwEnforcedUserAuthenticators == KeymasterDefs.HW_AUTH_BIOMETRIC) {
|
||||
// Fingerprint-only key; will be invalidated if the root SID isn't in the SID list.
|
||||
invalidatedByBiometricEnrollment = keymasterSecureUserIds != null
|
||||
&& !keymasterSecureUserIds.isEmpty()
|
||||
invalidatedByBiometricEnrollment = !keymasterSecureUserIds.isEmpty()
|
||||
&& !keymasterSecureUserIds.contains(getGateKeeperSecureUserId());
|
||||
}
|
||||
|
||||
boolean userConfirmationRequired = keyCharacteristics.hwEnforced.getBoolean(KeymasterDefs.KM_TAG_TRUSTED_CONFIRMATION_REQUIRED);
|
||||
|
||||
return new KeyInfo(entryAlias,
|
||||
return new KeyInfo(key.getUserKeyDescriptor().alias,
|
||||
insideSecureHardware,
|
||||
origin,
|
||||
keySize,
|
||||
@@ -213,11 +239,8 @@ public class AndroidKeyStoreSecretKeyFactorySpi extends SecretKeyFactorySpi {
|
||||
userAuthenticationValidWhileOnBody,
|
||||
trustedUserPresenceRequired,
|
||||
invalidatedByBiometricEnrollment,
|
||||
userConfirmationRequired,
|
||||
// Keystore 1.0 does not tell us the exact security level of the key
|
||||
// so we assume TEE if the key is in secure hardware.
|
||||
insideSecureHardware ? KeyProperties.SecurityLevelEnum.TRUSTED_ENVIRONMENT
|
||||
: KeyProperties.SecurityLevelEnum.SOFTWARE);
|
||||
trustedUserConfirmationRequired,
|
||||
securityLevel);
|
||||
}
|
||||
|
||||
private static BigInteger getGateKeeperSecureUserId() throws ProviderException {
|
||||
|
||||
Reference in New Issue
Block a user