diff options
Diffstat (limited to 'keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java')
-rw-r--r-- | keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java new file mode 100644 index 000000000000..642e08813291 --- /dev/null +++ b/keystore/java/android/security/keystore2/AndroidKeyStoreEdECPublicKey.java @@ -0,0 +1,145 @@ +/* + * 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.EdECPublicKey; +import java.security.spec.EdECPoint; +import java.security.spec.NamedParameterSpec; +import java.util.Arrays; +import java.util.Objects; + +/** + * {@link EdECPublicKey} backed by keystore. + * + * @hide + */ +public class AndroidKeyStoreEdECPublicKey extends AndroidKeyStorePublicKey + implements EdECPublicKey { + /** + * DER sequence, as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-4 and + * https://datatracker.ietf.org/doc/html/rfc5280#section-4.1. + * SEQUENCE (2 elem) + * SEQUENCE (1 elem) + * OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm) + * as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-3 + * BIT STRING (256 bit) as defined in + * https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2 + */ + private static final byte[] DER_KEY_PREFIX = new byte[] { + 0x30, + 0x2a, + 0x30, + 0x05, + 0x06, + 0x03, + 0x2b, + 0x65, + 0x70, + 0x03, + 0x21, + 0x00, + }; + private static final int ED25519_KEY_SIZE_BYTES = 32; + + private byte[] mEncodedKey; + private EdECPoint mPoint; + + public AndroidKeyStoreEdECPublicKey( + @NonNull KeyDescriptor descriptor, + @NonNull KeyMetadata metadata, + @NonNull String algorithm, + @NonNull KeyStoreSecurityLevel iSecurityLevel, + @NonNull byte[] encodedKey) { + super(descriptor, metadata, encodedKey, algorithm, iSecurityLevel); + mEncodedKey = encodedKey; + + int preambleLength = matchesPreamble(DER_KEY_PREFIX, encodedKey); + if (preambleLength == 0) { + throw new IllegalArgumentException("Key size is not correct size"); + } + + mPoint = pointFromKeyByteArray( + Arrays.copyOfRange(encodedKey, preambleLength, encodedKey.length)); + } + + @Override + AndroidKeyStorePrivateKey getPrivateKey() { + return new AndroidKeyStoreEdECPrivateKey( + getUserKeyDescriptor(), + getKeyIdDescriptor().nspace, + getAuthorizations(), + "EdDSA", + getSecurityLevel()); + } + + @Override + public NamedParameterSpec getParams() { + return NamedParameterSpec.ED25519; + } + + @Override + public EdECPoint getPoint() { + return mPoint; + } + + private static int matchesPreamble(byte[] preamble, byte[] encoded) { + if (encoded.length != (preamble.length + ED25519_KEY_SIZE_BYTES)) { + return 0; + } + if (Arrays.compare(preamble, Arrays.copyOf(encoded, preamble.length)) != 0) { + return 0; + } + return preamble.length; + } + + private static EdECPoint pointFromKeyByteArray(byte[] coordinates) { + Objects.requireNonNull(coordinates); + + // Oddity of the key is the most-significant bit of the last byte. + boolean isOdd = (0x80 & coordinates[coordinates.length - 1]) != 0; + // Zero out the oddity bit. + coordinates[coordinates.length - 1] &= (byte) 0x7f; + // Representation of Y is in little-endian, according to rfc8032 section-3.1. + reverse(coordinates); + // The integer representing Y starts from the first bit in the coordinates array. + BigInteger y = new BigInteger(1, coordinates); + return new EdECPoint(isOdd, y); + } + + private static void reverse(byte[] coordinateArray) { + int start = 0; + int end = coordinateArray.length - 1; + while (start < end) { + byte tmp = coordinateArray[start]; + coordinateArray[start] = coordinateArray[end]; + coordinateArray[end] = tmp; + start++; + end--; + } + } + + @Override + public byte[] getEncoded() { + return mEncodedKey.clone(); + } +} |