diff options
4 files changed, 181 insertions, 2 deletions
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java index fc963a88c4d1..b1338d164055 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreKeyAgreementSpi.java @@ -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. diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java index 0355628b8135..9947d34495ab 100644 --- a/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreProvider.java @@ -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); diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPrivateKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPrivateKey.java new file mode 100644 index 000000000000..42589640d2b7 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPrivateKey.java @@ -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; + } +} diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPublicKey.java new file mode 100644 index 000000000000..9f3df3d72d86 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreXDHPublicKey.java @@ -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; + } +} + |