1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.security.keystore2;
18 
19 import android.annotation.NonNull;
20 import android.security.KeyStoreSecurityLevel;
21 import android.system.keystore2.KeyDescriptor;
22 import android.system.keystore2.KeyMetadata;
23 
24 import java.math.BigInteger;
25 import java.security.interfaces.EdECPublicKey;
26 import java.security.spec.EdECPoint;
27 import java.security.spec.NamedParameterSpec;
28 import java.util.Arrays;
29 import java.util.Objects;
30 
31 /**
32  * {@link EdECPublicKey} backed by keystore.
33  *
34  * @hide
35  */
36 public class AndroidKeyStoreEdECPublicKey extends AndroidKeyStorePublicKey
37         implements EdECPublicKey {
38     /**
39      * DER sequence, as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-4 and
40      * https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.
41      * SEQUENCE (2 elem)
42      *  SEQUENCE (1 elem)
43      *    OBJECT IDENTIFIER 1.3.101.112 curveEd25519 (EdDSA 25519 signature algorithm)
44      *    as defined in https://datatracker.ietf.org/doc/html/rfc8410#section-3
45      *  BIT STRING (256 bit) as defined in
46      *  https://datatracker.ietf.org/doc/html/rfc8032#section-5.1.2
47      */
48     private static final byte[] DER_KEY_PREFIX = new byte[] {
49             0x30,
50             0x2a,
51             0x30,
52             0x05,
53             0x06,
54             0x03,
55             0x2b,
56             0x65,
57             0x70,
58             0x03,
59             0x21,
60             0x00,
61     };
62     private static final int ED25519_KEY_SIZE_BYTES = 32;
63 
64     private byte[] mEncodedKey;
65     private EdECPoint mPoint;
66 
AndroidKeyStoreEdECPublicKey( @onNull KeyDescriptor descriptor, @NonNull KeyMetadata metadata, @NonNull String algorithm, @NonNull KeyStoreSecurityLevel iSecurityLevel, @NonNull byte[] encodedKey)67     public AndroidKeyStoreEdECPublicKey(
68             @NonNull KeyDescriptor descriptor,
69             @NonNull KeyMetadata metadata,
70             @NonNull String algorithm,
71             @NonNull KeyStoreSecurityLevel iSecurityLevel,
72             @NonNull byte[] encodedKey) {
73         super(descriptor, metadata, encodedKey, algorithm, iSecurityLevel);
74         mEncodedKey = encodedKey;
75 
76         int preambleLength = matchesPreamble(DER_KEY_PREFIX, encodedKey);
77         if (preambleLength == 0) {
78             throw new IllegalArgumentException("Key size is not correct size");
79         }
80 
81         mPoint = pointFromKeyByteArray(
82                 Arrays.copyOfRange(encodedKey, preambleLength, encodedKey.length));
83     }
84 
85     @Override
getPrivateKey()86     AndroidKeyStorePrivateKey getPrivateKey() {
87         return new AndroidKeyStoreEdECPrivateKey(
88                 getUserKeyDescriptor(),
89                 getKeyIdDescriptor().nspace,
90                 getAuthorizations(),
91                 "EdDSA",
92                 getSecurityLevel());
93     }
94 
95     @Override
getParams()96     public NamedParameterSpec getParams() {
97         return NamedParameterSpec.ED25519;
98     }
99 
100     @Override
getPoint()101     public EdECPoint getPoint() {
102         return mPoint;
103     }
104 
matchesPreamble(byte[] preamble, byte[] encoded)105     private static int matchesPreamble(byte[] preamble, byte[] encoded) {
106         if (encoded.length != (preamble.length + ED25519_KEY_SIZE_BYTES)) {
107             return 0;
108         }
109         if (Arrays.compare(preamble, Arrays.copyOf(encoded, preamble.length)) != 0) {
110             return 0;
111         }
112         return preamble.length;
113     }
114 
pointFromKeyByteArray(byte[] coordinates)115     private static EdECPoint pointFromKeyByteArray(byte[] coordinates) {
116         Objects.requireNonNull(coordinates);
117 
118         // Oddity of the key is the most-significant bit of the last byte.
119         boolean isOdd = (0x80 & coordinates[coordinates.length - 1]) != 0;
120         // Zero out the oddity bit.
121         coordinates[coordinates.length - 1] &= (byte) 0x7f;
122         // Representation of Y is in little-endian, according to rfc8032 section-3.1.
123         reverse(coordinates);
124         // The integer representing Y starts from the first bit in the coordinates array.
125         BigInteger y = new BigInteger(1, coordinates);
126         return new EdECPoint(isOdd, y);
127     }
128 
reverse(byte[] coordinateArray)129     private static void reverse(byte[] coordinateArray) {
130         int start = 0;
131         int end = coordinateArray.length - 1;
132         while (start < end) {
133             byte tmp = coordinateArray[start];
134             coordinateArray[start] = coordinateArray[end];
135             coordinateArray[end] = tmp;
136             start++;
137             end--;
138         }
139     }
140 
141     @Override
getEncoded()142     public byte[] getEncoded() {
143         return mEncodedKey.clone();
144     }
145 }
146