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