Keystore: Support Curve 25519 in the SPI layer

Add support for Curve 25519 in the public API.
This requires upgrading the keymint dependency to V2.

Note that this CL only passes tha tags to Keystore,
but does not yet let the caller use the generated keys
because of missing Conscrypt classes.

Bug: 194359292
Test: atest android.keystore.cts.Curve25519Test
Change-Id: I15223abec34b72c857e26fcc47d8ecf08c1f8c8d
This commit is contained in:
Eran Messeri
2022-02-14 16:58:56 +00:00
parent 478baf2287
commit 5deebdd570
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.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.NamedParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
import java.util.ArrayList;
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_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>();
private static final List<String> SUPPORTED_EC_NIST_CURVE_NAMES = new ArrayList<String>();
private static final List<Integer> SUPPORTED_EC_NIST_CURVE_SIZES = new ArrayList<Integer>();
private static final List<String> SUPPORTED_EC_CURVE_NAMES = new ArrayList<String>();
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 {
// Aliases for NIST P-224
SUPPORTED_EC_NIST_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("p-224", 224);
SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp224r1", 224);
// Aliases for NIST P-256
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("p-256", 256);
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("secp256r1", 256);
SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.put("prime256v1", 256);
SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("p-256", 256);
SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp256r1", 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
SUPPORTED_EC_NIST_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("p-384", 384);
SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp384r1", 384);
// Aliases for NIST P-521
SUPPORTED_EC_NIST_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("p-521", 521);
SUPPORTED_EC_CURVE_NAME_TO_SIZE.put("secp521r1", 521);
SUPPORTED_EC_NIST_CURVE_NAMES.addAll(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.keySet());
Collections.sort(SUPPORTED_EC_NIST_CURVE_NAMES);
SUPPORTED_EC_CURVE_NAMES.addAll(SUPPORTED_EC_CURVE_NAME_TO_SIZE.keySet());
Collections.sort(SUPPORTED_EC_CURVE_NAMES);
SUPPORTED_EC_NIST_CURVE_SIZES.addAll(
new HashSet<Integer>(SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.values()));
Collections.sort(SUPPORTED_EC_NIST_CURVE_SIZES);
SUPPORTED_EC_CURVE_SIZES.addAll(
new HashSet<Integer>(SUPPORTED_EC_CURVE_NAME_TO_SIZE.values()));
Collections.sort(SUPPORTED_EC_CURVE_SIZES);
}
private final int mOriginalKeymasterAlgorithm;
@@ -164,6 +171,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
private int mKeySizeBits;
private SecureRandom mRng;
private KeyDescriptor mAttestKeyDescriptor;
private String mEcCurveName;
private int[] mKeymasterPurposes;
private int[] mKeymasterBlockModes;
@@ -177,12 +185,15 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mOriginalKeymasterAlgorithm = keymasterAlgorithm;
}
private @EcCurve int keySize2EcCurve(int keySizeBits)
private static @EcCurve int keySizeAndNameToEcCurve(int keySizeBits, String ecCurveName)
throws InvalidAlgorithmParameterException {
switch (keySizeBits) {
case 224:
return EcCurve.P_224;
case 256:
if (isCurve25519(ecCurveName)) {
return EcCurve.CURVE_25519;
}
return EcCurve.P_256;
case 384:
return EcCurve.P_384;
@@ -247,7 +258,8 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
if (mKeySizeBits == -1) {
mKeySizeBits = getDefaultKeySize(keymasterAlgorithm);
}
checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked());
checkValidKeySize(keymasterAlgorithm, mKeySizeBits, mSpec.isStrongBoxBacked(),
mEcCurveName);
if (spec.getKeystoreAlias() == null) {
throw new InvalidAlgorithmParameterException("KeyStore entry alias not provided");
@@ -299,6 +311,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mAttestKeyDescriptor = buildAndCheckAttestKeyDescriptor(spec);
checkAttestKeyPurpose(spec);
checkCorrectKeyPurposeForCurve(spec);
success = true;
} 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)
throws InvalidAlgorithmParameterException {
if (spec.getAttestKeyAlias() != null) {
@@ -473,6 +522,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
mRSAPublicExponent = null;
mRng = null;
mKeyStore = null;
mEcCurveName = null;
}
private void initAlgorithmSpecificParameters() throws InvalidAlgorithmParameterException {
@@ -514,13 +564,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
case KeymasterDefs.KM_ALGORITHM_EC:
if (algSpecificSpec instanceof ECGenParameterSpec) {
ECGenParameterSpec ecSpec = (ECGenParameterSpec) algSpecificSpec;
String curveName = ecSpec.getName();
Integer ecSpecKeySizeBits = SUPPORTED_EC_NIST_CURVE_NAME_TO_SIZE.get(
curveName.toLowerCase(Locale.US));
mEcCurveName = ecSpec.getName();
final Integer ecSpecKeySizeBits = SUPPORTED_EC_CURVE_NAME_TO_SIZE.get(
mEcCurveName.toLowerCase(Locale.US));
if (ecSpecKeySizeBits == null) {
throw new InvalidAlgorithmParameterException(
"Unsupported EC curve name: " + curveName
+ ". Supported: " + SUPPORTED_EC_NIST_CURVE_NAMES);
"Unsupported EC curve name: " + mEcCurveName
+ ". Supported: " + SUPPORTED_EC_CURVE_NAMES);
}
if (mKeySizeBits == -1) {
mKeySizeBits = ecSpecKeySizeBits;
@@ -744,7 +794,7 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
if (mKeymasterAlgorithm == KeymasterDefs.KM_ALGORITHM_EC) {
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(
int keymasterAlgorithm,
int keySize,
boolean isStrongBoxBacked)
boolean isStrongBoxBacked,
String mEcCurveName)
throws InvalidAlgorithmParameterException {
switch (keymasterAlgorithm) {
case KeymasterDefs.KM_ALGORITHM_EC:
@@ -873,9 +924,13 @@ public abstract class AndroidKeyStoreKeyPairGeneratorSpi extends KeyPairGenerato
"Unsupported StrongBox EC key size: "
+ 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: "
+ keySize + " bits. Supported: " + SUPPORTED_EC_NIST_CURVE_SIZES);
+ keySize + " bits. Supported: " + SUPPORTED_EC_CURVE_SIZES);
}
break;
case KeymasterDefs.KM_ALGORITHM_RSA:

View File

@@ -66,6 +66,11 @@ public class AndroidKeyStoreProvider extends Provider {
private static final String DESEDE_SYSTEM_PROPERTY =
"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 **/
public AndroidKeyStoreProvider() {
super(PROVIDER_NAME, 1.0, "Android KeyStore security provider");
@@ -78,10 +83,16 @@ public class AndroidKeyStoreProvider extends Provider {
// java.security.KeyPairGenerator
put("KeyPairGenerator.EC", PACKAGE_NAME + ".AndroidKeyStoreKeyPairGeneratorSpi$EC");
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
putKeyFactoryImpl("EC");
putKeyFactoryImpl("RSA");
putKeyFactoryImpl(X25519_ALIAS);
putKeyFactoryImpl(ED25519_OID);
// javax.crypto.KeyGenerator
put("KeyGenerator.AES", PACKAGE_NAME + ".AndroidKeyStoreKeyGeneratorSpi$AES");
@@ -219,12 +230,17 @@ public class AndroidKeyStoreProvider extends Provider {
KeyStoreSecurityLevel securityLevel = iSecurityLevel;
if (KeyProperties.KEY_ALGORITHM_EC.equalsIgnoreCase(jcaKeyAlgorithm)) {
return new AndroidKeyStoreECPublicKey(descriptor, metadata,
iSecurityLevel, (ECPublicKey) publicKey);
} else if (KeyProperties.KEY_ALGORITHM_RSA.equalsIgnoreCase(jcaKeyAlgorithm)) {
return new AndroidKeyStoreRSAPublicKey(descriptor, metadata,
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 {
throw new ProviderException("Unsupported Android Keystore public key algorithm: "
+ jcaKeyAlgorithm);