Merge "Keystore: Support Curve 25519 in the SPI layer" am: 76cee808e2

Original change: https://android-review.googlesource.com/c/platform/frameworks/base/+/1984970

Change-Id: Ia2cf9182ba121878d7ea2bb9e30961637ac18518
This commit is contained in:
Treehugger Robot
2022-03-01 19:41:58 +00:00
committed by Automerger Merge Worker
2 changed files with 100 additions and 29 deletions

View File

@@ -66,6 +66,7 @@ import java.security.SecureRandom;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.spec.AlgorithmParameterSpec; import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec; import java.security.spec.ECGenParameterSpec;
import java.security.spec.NamedParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec; import java.security.spec.RSAKeyGenParameterSpec;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@@ -119,36 +120,42 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private static final int RSA_MIN_KEY_SIZE = 512; private static final int RSA_MIN_KEY_SIZE = 512;
private static final int RSA_MAX_KEY_SIZE = 8192; private static final int RSA_MAX_KEY_SIZE = 8192;
private static final Map<String, Integer> SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE = private static final Map<String, Integer> SUPPORTED_EC_CURVE_NAME_TO_SIZE =
new HashMap<String, Integer>(); new HashMap<String, Integer>();
private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>(); private static final List<String> SUPPORTED_EC_CURVE_NAMES = new ArrayList<String>();
private static final List<Integer> SUPPORTED_EC_NIST_CURVE_SIZES = new ArrayList<Integer>(); private static final List<Integer> SUPPORTED_EC_CURVE_SIZES = new ArrayList<Integer>();
private static final String CURVE_X_25519 = NamedParameterSpec.X25519.getName();
private static final String CURVE_ED_25519 = NamedParameterSpec.ED25519.getName();
static { static {
// Aliases for NIST P-224 // Aliases for NIST P-224
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-224", 224); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-224", 224);
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp224r1", 224); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
// Aliases for NIST P-256 // Aliases for NIST P-256
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-256", 256);
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
// Aliases for Curve 25519
SUPPORTED_EC_CURVE_NAME_TO_SIZE.put(CURVE_X_25519.toLowerCase(Locale.US), 256);
SUPPORTED_EC_CURVE_NAME_TO_SIZE.put(CURVE_ED_25519.toLowerCase(Locale.US), 256);
// Aliases for NIST P-384 // Aliases for NIST P-384
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-384", 384); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-384", 384);
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp384r1", 384); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
// Aliases for NIST P-521 // Aliases for NIST P-521
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-521", 521); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-521", 521);
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp521r1", 521); SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet()); SUPPORTED_EC_CURVE_NAMES.addAll(SUPPORTED_EC_CURVE_NAME_TO_SIZE.keySet());
Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES); Collections.sort(SUPPORTED_EC_CURVE_NAMES);
SUPPORTED_EC_NIST_CURVE_SIZES.addAll( SUPPORTED_EC_CURVE_SIZES.addAll(
new HashSet<Integer>(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.values())); new HashSet<Integer>(SUPPORTED_EC_CURVE_NAME_TO_SIZE.values()));
Collections.sort(SUPPORTED_EC_NIST_CURVE_SIZES); Collections.sort(SUPPORTED_EC_CURVE_SIZES);
} }
private final int mOriginalKeymasterAlgorithm; private final int mOriginalKeymasterAlgorithm;
@@ -164,6 +171,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private int mKeySizeBits; private int mKeySizeBits;
private SecureRandom mRng; private SecureRandom mRng;
private KeyDescriptor mAttestKeyDescriptor; private KeyDescriptor mAttestKeyDescriptor;
private String mEcCurveName;
private int[] mKeymasterPurposes; private int[] mKeymasterPurposes;
private int[] mKeymasterBlockModes; private int[] mKeymasterBlockModes;
@@ -177,12 +185,15 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mOriginalKeymasterAlgorithm = keymasterAlgorithm; mOriginalKeymasterAlgorithm = keymasterAlgorithm;
} }
private @EcCurve int keySize2EcCurve(int keySizeBits) private static @EcCurve int keySizeAndNameToEcCurve(int keySizeBits, String ecCurveName)
throws InvalidAlgorithmParameterException { throws InvalidAlgorithmParameterException {
switch (keySizeBits) { switch (keySizeBits) {
case 224: case 224:
return EcCurve.P_224; return EcCurve.P_224;
case 256: case 256:
if (isCurve25519(ecCurveName)) {
return EcCurve.CURVE_25519;
}
return EcCurve.P_256; return EcCurve.P_256;
case 384: case 384:
return EcCurve.P_384; return EcCurve.P_384;
@@ -247,7 +258,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
if (mKeySizeBits == -1) { if (mKeySizeBits == -1) {
mKeySizeBits = getDefaultKeySize(keymasterAlgorithm); mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
} }
checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked()); checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked(),
mEcCurveName);
if (spec.getKeystoreAlias() == null) { if (spec.getKeystoreAlias() == null) {
throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided"); throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
@@ -299,6 +311,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mAttestKeyDescriptor = buildAndCheckAttestKeyDescriptor(spec); mAttestKeyDescriptor = buildAndCheckAttestKeyDescriptor(spec);
checkAttestKeyPurpose(spec); checkAttestKeyPurpose(spec);
checkCorrectKeyPurposeForCurve(spec);
success = true; success = true;
} finally { } finally {
@@ -317,6 +330,42 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
} }
} }
private void checkCorrectKeyPurposeForCurve(KeyGenParameterSpec spec)
throws InvalidAlgorithmParameterException {
// Validate the key usage purposes against the curve. x25519 should be
// key exchange only, ed25519 signing and attesting.
if (!isCurve25519(mEcCurveName)) {
return;
}
if (mEcCurveName.equalsIgnoreCase(CURVE_X_25519)
&& spec.getPurposes() != KeyProperties.PURPOSE_AGREE_KEY) {
throw new InvalidAlgorithmParameterException(
"x25519 may only be used for key agreement.");
} else if (mEcCurveName.equalsIgnoreCase(CURVE_ED_25519)
&& !hasOnlyAllowedPurposeForEd25519(spec.getPurposes())) {
throw new InvalidAlgorithmParameterException(
"ed25519 may not be used for key agreement.");
}
}
private static boolean isCurve25519(String ecCurveName) {
if (ecCurveName == null) {
return false;
}
return ecCurveName.equalsIgnoreCase(CURVE_X_25519)
|| ecCurveName.equalsIgnoreCase(CURVE_ED_25519);
}
private static boolean hasOnlyAllowedPurposeForEd25519(@KeyProperties.PurposeEnum int purpose) {
final int allowedPurposes = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_VERIFY
| KeyProperties.PURPOSE_ATTEST_KEY;
boolean hasAllowedPurpose = (purpose & allowedPurposes) != 0;
boolean hasDisallowedPurpose = (purpose & ~allowedPurposes) != 0;
return hasAllowedPurpose && !hasDisallowedPurpose;
}
private KeyDescriptor buildAndCheckAttestKeyDescriptor(KeyGenParameterSpec spec) private KeyDescriptor buildAndCheckAttestKeyDescriptor(KeyGenParameterSpec spec)
throws InvalidAlgorithmParameterException { throws InvalidAlgorithmParameterException {
if (spec.getAttestKeyAlias() != null) { if (spec.getAttestKeyAlias() != null) {
@@ -473,6 +522,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mRSAPublicExponent = null; mRSAPublicExponent = null;
mRng = null; mRng = null;
mKeyStore = null; mKeyStore = null;
mEcCurveName = null;
} }
private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException { private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
@@ -514,13 +564,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
case KeymasterDefs.KM_ALGORITHM_EC: case KeymasterDefs.KM_ALGORITHM_EC:
if (algSpecificSpec instanceof ECGenParameterSpec) { if (algSpecificSpec instanceof ECGenParameterSpec) {
ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec; ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
String curveName = ecSpec.getName(); mEcCurveName = ecSpec.getName();
Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get( final Integer ecSpecKeySizeBits = SUPPORTED_EC_CURVE_NAME_TO_SIZE.get(
curveName.toLowerCase(Locale.US)); mEcCurveName.toLowerCase(Locale.US));
if (ecSpecKeySizeBits == null) { if (ecSpecKeySizeBits == null) {
throw new InvalidAlgorithmParameterException( throw new InvalidAlgorithmParameterException(
"Unsupported EC curve name: " + curveName "Unsupported EC curve name: " + mEcCurveName
+ ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES); + ". Supported: " + SUPPORTED_EC_CURVE_NAMES);
} }
if (mKeySizeBits == -1) { if (mKeySizeBits == -1) {
mKeySizeBits = ecSpecKeySizeBits; mKeySizeBits = ecSpecKeySizeBits;
@@ -744,7 +794,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) { if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
params.add(KeyStore2ParameterUtils.makeEnum( params.add(KeyStore2ParameterUtils.makeEnum(
Tag.EC_CURVE, keySize2EcCurve(mKeySizeBits) Tag.EC_CURVE, keySizeAndNameToEcCurve(mKeySizeBits, mEcCurveName)
)); ));
} }
@@ -864,7 +914,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private static void checkValidKeySize( private static void checkValidKeySize(
int keymasterAlgorithm, int keymasterAlgorithm,
int keySize, int keySize,
boolean isStrongBoxBacked) boolean isStrongBoxBacked,
String mEcCurveName)
throws InvalidAlgorithmParameterException { throws InvalidAlgorithmParameterException {
switch (keymasterAlgorithm) { switch (keymasterAlgorithm) {
case KeymasterDefs.KM_ALGORITHM_EC: case KeymasterDefs.KM_ALGORITHM_EC:
@@ -873,9 +924,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
"Unsupported StrongBox EC key size: " "Unsupported StrongBox EC key size: "
+ keySize + " bits. Supported: 256"); + keySize + " bits. Supported: 256");
} }
if (!SUPPORTED_EC_NIST_CURVE_SIZES.contains(keySize)) { if (isStrongBoxBacked && isCurve25519(mEcCurveName)) {
throw new InvalidAlgorithmParameterException(
"Unsupported StrongBox EC: " + mEcCurveName);
}
if (!SUPPORTED_EC_CURVE_SIZES.contains(keySize)) {
throw new InvalidAlgorithmParameterException("Unsupported EC key size: " throw new InvalidAlgorithmParameterException("Unsupported EC key size: "
+ keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES); + keySize + " bits. Supported: " + SUPPORTED_EC_CURVE_SIZES);
} }
break; break;
case KeymasterDefs.KM_ALGORITHM_RSA: case KeymasterDefs.KM_ALGORITHM_RSA:

View File

@@ -66,6 +66,11 @@ public class AndroidKeyStoreProvider extends Provider {
private static final String DESEDE_SYSTEM_PROPERTY = private static final String DESEDE_SYSTEM_PROPERTY =
"ro.hardware.keystore_desede"; "ro.hardware.keystore_desede";
// Conscrypt returns the Ed25519 OID as the JCA key algorithm.
private static final String ED25519_OID = "1.3.101.112";
// Conscrypt returns "XDH" as the X25519 JCA key algorithm.
private static final String X25519_ALIAS = "XDH";
/** @hide **/ /** @hide **/
public AndroidKeyStoreProvider() { public AndroidKeyStoreProvider() {
super(PROVIDER_NAME, 1.0, "Android KeyStore security provider"); super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
@@ -78,10 +83,16 @@ public class AndroidKeyStoreProvider extends Provider {
// java.security.KeyPairGenerator // java.security.KeyPairGenerator
put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC"); put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA"); put("KeyPairGenerator.RSA", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
put("KeyPairGenerator." + X25519_ALIAS,
PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
put("KeyPairGenerator." + ED25519_OID,
PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$RSA");
// java.security.KeyFactory // java.security.KeyFactory
putKeyFactoryImpl("EC"); putKeyFactoryImpl("EC");
putKeyFactoryImpl("RSA"); putKeyFactoryImpl("RSA");
putKeyFactoryImpl(X25519_ALIAS);
putKeyFactoryImpl(ED25519_OID);
// javax.crypto.KeyGenerator // javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES"); put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
@@ -219,12 +230,17 @@ public class AndroidKeyStoreProvider extends Provider {
KeyStoreSecurityLevel securityLevel = iSecurityLevel; KeyStoreSecurityLevel securityLevel = iSecurityLevel;
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) { if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) {
return new AndroidKeyStoreECPublicKey(descriptor, metadata, return new AndroidKeyStoreECPublicKey(descriptor, metadata,
iSecurityLevel, (ECPublicKey) publicKey); iSecurityLevel, (ECPublicKey) publicKey);
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) { } else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) {
return new AndroidKeyStoreRSAPublicKey(descriptor, metadata, return new AndroidKeyStoreRSAPublicKey(descriptor, metadata,
iSecurityLevel, (RSAPublicKey) publicKey); iSecurityLevel, (RSAPublicKey) publicKey);
} else if (ED25519_OID.equalsIgnoreCase(jcaKeyAlgorithm)) {
//TODO(b/214203951) missing classes in conscrypt
throw new ProviderException("Curve " + ED25519_OID + " not supported yet");
} else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) {
//TODO(b/214203951) missing classes in conscrypt
throw new ProviderException("Curve " + X25519_ALIAS + " not supported yet");
} else { } else {
throw new ProviderException("Unsupported Android Keystore public key algorithm: " throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+ jcaKeyAlgorithm); + jcaKeyAlgorithm);