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:
@@ -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:
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user