Keystore: Wire X25519 key agreement
Implement support for the X25519 key agreement functionality. Similar to Ed25519, two new classes are added: * AndroidKeyStoreXDHPrivateKey * AndroidKeyStoreXDHPublicKey The private key class is simply a handle to the KeyMint key. The public key class implements XECPublicKey, the interface needed for using this key in a platform-backed key agreement. Because of Conscrypt API boundaries, the functionality of Conscrypt's OpenSSLX25519PublicKey is duplicated here - namely, matching the prefix of the encoded key. Bug: 194359292 Test: atest android.keystore.cts.Curve25519Test Change-Id: Ifc12be528ab544fd6909bb0dd6224a0a4dd400c6 Merged-In: Ifc12be528ab544fd6909bb0dd6224a0a4dd400c6
This commit is contained in:
@@ -61,6 +61,17 @@ public class AndroidKeyStoreKeyAgreementSpi extends KeyAgreementSpi
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* X25519 key agreement support.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public static class XDH extends AndroidKeyStoreKeyAgreementSpi {
|
||||
public XDH() {
|
||||
super(Algorithm.EC);
|
||||
}
|
||||
}
|
||||
|
||||
private final int mKeymintAlgorithm;
|
||||
|
||||
// Fields below are populated by engineInit and should be preserved after engineDoFinal.
|
||||
|
||||
@@ -104,6 +104,7 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
|
||||
// javax.crypto.KeyAgreement
|
||||
put("KeyAgreement.ECDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$ECDH");
|
||||
put("KeyAgreement.XDH", PACKAGE_NAME + ".AndroidKeyStoreKeyAgreementSpi$XDH");
|
||||
|
||||
// java.security.SecretKeyFactory
|
||||
putSecretKeyFactoryImpl("AES");
|
||||
@@ -235,8 +236,8 @@ public class AndroidKeyStoreProvider extends Provider {
|
||||
return new AndroidKeyStoreEdECPublicKey(descriptor, metadata, ED25519_OID,
|
||||
iSecurityLevel, publicKeyEncoded);
|
||||
} else if (X25519_ALIAS.equalsIgnoreCase(jcaKeyAlgorithm)) {
|
||||
//TODO(b/214203951) missing classes in conscrypt
|
||||
throw new ProviderException("Curve " + X25519_ALIAS + " not supported yet");
|
||||
return new AndroidKeyStoreXDHPublicKey(descriptor, metadata, X25519_ALIAS,
|
||||
iSecurityLevel, publicKey.getEncoded());
|
||||
} else {
|
||||
throw new ProviderException("Unsupported Android Keystore public key algorithm: "
|
||||
+ jcaKeyAlgorithm);
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.security.keystore2;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.security.KeyStoreSecurityLevel;
|
||||
import android.system.keystore2.Authorization;
|
||||
import android.system.keystore2.KeyDescriptor;
|
||||
|
||||
import java.security.PrivateKey;
|
||||
import java.security.interfaces.EdECKey;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
|
||||
/**
|
||||
* X25519 Private Key backed by Keystore.
|
||||
* instance of {@link PrivateKey} and {@link EdECKey}
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class AndroidKeyStoreXDHPrivateKey extends AndroidKeyStorePrivateKey implements EdECKey {
|
||||
public AndroidKeyStoreXDHPrivateKey(
|
||||
@NonNull KeyDescriptor descriptor, long keyId,
|
||||
@NonNull Authorization[] authorizations,
|
||||
@NonNull String algorithm,
|
||||
@NonNull KeyStoreSecurityLevel securityLevel) {
|
||||
super(descriptor, keyId, authorizations, algorithm, securityLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NamedParameterSpec getParams() {
|
||||
return NamedParameterSpec.X25519;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package android.security.keystore2;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.security.KeyStoreSecurityLevel;
|
||||
import android.system.keystore2.KeyDescriptor;
|
||||
import android.system.keystore2.KeyMetadata;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.interfaces.XECPublicKey;
|
||||
import java.security.spec.AlgorithmParameterSpec;
|
||||
import java.security.spec.NamedParameterSpec;
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* {@link XECPublicKey} backed by keystore.
|
||||
* This class re-implements Conscrypt's OpenSSLX25519PublicKey. The reason is that
|
||||
* OpenSSLX25519PublicKey does not implement XECPublicKey and is not a part of Conscrypt's public
|
||||
* interface so it cannot be referred to.
|
||||
*
|
||||
* So the functionality is duplicated here until (likely Android U) one of the things mentioned
|
||||
* above is fixed.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class AndroidKeyStoreXDHPublicKey extends AndroidKeyStorePublicKey implements XECPublicKey {
|
||||
private static final byte[] X509_PREAMBLE = new byte[] {
|
||||
0x30, 0x2a, 0x30, 0x05, 0x06, 0x03, 0x2b, 0x65, 0x6e, 0x03, 0x21, 0x00,
|
||||
};
|
||||
|
||||
private static final byte[] X509_PREAMBLE_WITH_NULL = new byte[] {
|
||||
0x30, 0x2C, 0x30, 0x07, 0x06, 0x03, 0x2B, 0x65, 0x6E, 0x05, 0x00, 0x03, 0x21, 0x00,
|
||||
};
|
||||
|
||||
private static final int X25519_KEY_SIZE_BYTES = 32;
|
||||
|
||||
private final byte[] mEncodedKey;
|
||||
private final int mPreambleLength;
|
||||
|
||||
public AndroidKeyStoreXDHPublicKey(
|
||||
@NonNull KeyDescriptor descriptor,
|
||||
@NonNull KeyMetadata metadata,
|
||||
@NonNull String algorithm,
|
||||
@NonNull KeyStoreSecurityLevel iSecurityLevel,
|
||||
@NonNull byte[] encodedKey) {
|
||||
super(descriptor, metadata, encodedKey, algorithm, iSecurityLevel);
|
||||
mEncodedKey = encodedKey;
|
||||
if (mEncodedKey == null) {
|
||||
throw new IllegalArgumentException("empty encoded key.");
|
||||
}
|
||||
|
||||
mPreambleLength = matchesPreamble(X509_PREAMBLE, mEncodedKey) | matchesPreamble(
|
||||
X509_PREAMBLE_WITH_NULL, mEncodedKey);
|
||||
if (mPreambleLength == 0) {
|
||||
throw new IllegalArgumentException("Key size is not correct size");
|
||||
}
|
||||
}
|
||||
|
||||
private static int matchesPreamble(byte[] preamble, byte[] encoded) {
|
||||
if (encoded.length != (preamble.length + X25519_KEY_SIZE_BYTES)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (Arrays.compare(preamble, 0, preamble.length, encoded, 0, preamble.length) != 0) {
|
||||
return 0;
|
||||
}
|
||||
return preamble.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
AndroidKeyStorePrivateKey getPrivateKey() {
|
||||
return new AndroidKeyStoreXDHPrivateKey(
|
||||
getUserKeyDescriptor(),
|
||||
getKeyIdDescriptor().nspace,
|
||||
getAuthorizations(),
|
||||
"x25519",
|
||||
getSecurityLevel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public BigInteger getU() {
|
||||
return new BigInteger(Arrays.copyOfRange(mEncodedKey, mPreambleLength, mEncodedKey.length));
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getEncoded() {
|
||||
return mEncodedKey.clone();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getAlgorithm() {
|
||||
return "XDH";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getFormat() {
|
||||
return "x.509";
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlgorithmParameterSpec getParams() {
|
||||
return NamedParameterSpec.X25519;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user