Skip to content
Snippets Groups Projects
Commit 4c20e224 authored by Eran Messeri's avatar Eran Messeri
Browse files

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
parent dfada27e
No related branches found
No related tags found
No related merge requests found
......@@ -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);
......
/*
* 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;
}
}
/*
* 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;
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment