• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.rkpdapp.unittest;
18 
19 
20 import static com.google.crypto.tink.subtle.EllipticCurves.EcdsaEncoding.IEEE_P1363;
21 import static com.google.crypto.tink.subtle.Enums.HashType.SHA256;
22 
23 import com.google.crypto.tink.subtle.EcdsaSignJce;
24 import com.google.crypto.tink.subtle.Ed25519Sign;
25 import com.google.crypto.tink.subtle.EllipticCurves;
26 
27 import org.bouncycastle.asn1.x509.BasicConstraints;
28 import org.bouncycastle.asn1.x509.Extension;
29 import org.bouncycastle.asn1.x509.KeyUsage;
30 import org.bouncycastle.x509.X509V3CertificateGenerator;
31 
32 import java.io.ByteArrayOutputStream;
33 import java.math.BigInteger;
34 import java.security.AlgorithmParameters;
35 import java.security.KeyFactory;
36 import java.security.KeyPair;
37 import java.security.KeyPairGenerator;
38 import java.security.MessageDigest;
39 import java.security.PublicKey;
40 import java.security.cert.X509Certificate;
41 import java.security.interfaces.ECPrivateKey;
42 import java.security.interfaces.ECPublicKey;
43 import java.security.spec.ECGenParameterSpec;
44 import java.security.spec.ECParameterSpec;
45 import java.security.spec.ECPoint;
46 import java.security.spec.ECPublicKeySpec;
47 import java.time.Duration;
48 import java.time.Instant;
49 import java.util.Date;
50 import java.util.List;
51 
52 import javax.security.auth.x500.X500Principal;
53 
54 import co.nstant.in.cbor.CborBuilder;
55 import co.nstant.in.cbor.CborEncoder;
56 import co.nstant.in.cbor.builder.MapBuilder;
57 import co.nstant.in.cbor.model.Array;
58 import co.nstant.in.cbor.model.DataItem;
59 
60 /**
61  * Utility class for unit testing.
62  */
63 public class Utils {
64     private static final int KEY_TYPE = 1;
65     private static final int KEY_TYPE_OKP = 1;
66     private static final int KEY_TYPE_EC2 = 2;
67     private static final int KID = 2;
68     private static final int ALGORITHM = 3;
69     private static final int ALGORITHM_EDDSA = -8;
70     private static final int ALGORITHM_ES256 = -7;
71     private static final int ALGORITHM_ECDH_ES_HKDF_256 = -25;
72     private static final int CURVE = -1;
73     public  static final int CURVE_X25519 = 4;
74     public static final int CURVE_ED25519 = 6;
75     public static final int CURVE_P256 = 1;
76     private static final int X_COORDINATE = -2;
77     private static final int Y_COORDINATE = -3;
78 
getP256PubKeyFromBytes(byte[] xPub, byte[] yPub)79     public static PublicKey getP256PubKeyFromBytes(byte[] xPub, byte[] yPub) throws Exception {
80         BigInteger x = new BigInteger(1, xPub);
81         BigInteger y = new BigInteger(1, yPub);
82         AlgorithmParameters parameters = AlgorithmParameters.getInstance("EC");
83         parameters.init(new ECGenParameterSpec("secp256r1"));
84         ECParameterSpec ecParameters = parameters.getParameterSpec(ECParameterSpec.class);
85         ECPoint point = new ECPoint(x, y);
86         ECPublicKeySpec keySpec = new ECPublicKeySpec(point, ecParameters);
87         KeyFactory keyFactory = KeyFactory.getInstance("EC");
88         return keyFactory.generatePublic(keySpec);
89     }
90 
getBytesFromP256PrivateKey(ECPrivateKey privateKey)91     public static byte[] getBytesFromP256PrivateKey(ECPrivateKey privateKey) throws Exception {
92         int keySizeBytes = (privateKey.getParams().getOrder().bitLength() + Byte.SIZE - 1)
93                 / Byte.SIZE;
94         final byte[] rawPublicKey = new byte[keySizeBytes];
95 
96         final byte[] priv = privateKey.getS().toByteArray();
97         if (priv.length <= keySizeBytes) {
98             System.arraycopy(priv, 0, rawPublicKey,  keySizeBytes
99                     - priv.length, priv.length);
100         } else if (priv.length == keySizeBytes + 1 && priv[0] == 0) {
101             System.arraycopy(priv, 1, rawPublicKey, 0, keySizeBytes);
102         } else {
103             throw new IllegalStateException("private value is too large");
104         }
105         return rawPublicKey;
106     }
107 
getBytesFromP256PublicKey(ECPublicKey publicKey)108     public static byte[] getBytesFromP256PublicKey(ECPublicKey publicKey) throws Exception {
109         int keySizeBytes =
110                 (publicKey.getParams().getOrder().bitLength() + Byte.SIZE - 1) / Byte.SIZE;
111 
112         final byte[] rawPublicKey = new byte[2 * keySizeBytes];
113         int offset = 0;
114 
115         final byte[] x = publicKey.getW().getAffineX().toByteArray();
116         if (x.length <= keySizeBytes) {
117             System.arraycopy(x, 0, rawPublicKey, offset + keySizeBytes
118                     - x.length, x.length);
119         } else if (x.length == keySizeBytes + 1 && x[0] == 0) {
120             System.arraycopy(x, 1, rawPublicKey, offset, keySizeBytes);
121         } else {
122             throw new IllegalStateException("x value is too large");
123         }
124         offset += keySizeBytes;
125 
126         final byte[] y = publicKey.getW().getAffineY().toByteArray();
127         if (y.length <= keySizeBytes) {
128             System.arraycopy(y, 0, rawPublicKey, offset + keySizeBytes
129                     - y.length, y.length);
130         } else if (y.length == keySizeBytes + 1 && y[0] == 0) {
131             System.arraycopy(y, 1, rawPublicKey, offset, keySizeBytes);
132         } else {
133             throw new IllegalStateException("y value is too large");
134         }
135         return rawPublicKey;
136     }
137 
generateEcdsaKeyPair()138     public static KeyPair generateEcdsaKeyPair() throws Exception {
139         KeyPairGenerator generator = KeyPairGenerator.getInstance("EC");
140         ECGenParameterSpec params = new ECGenParameterSpec("secp256r1");
141         generator.initialize(params);
142         return generator.generateKeyPair();
143     }
144 
signPublicKey(KeyPair issuerKeyPair, PublicKey publicKeyToSign)145     public static X509Certificate signPublicKey(KeyPair issuerKeyPair, PublicKey publicKeyToSign)
146             throws Exception {
147         X500Principal issuer = new X500Principal("CN=TEE");
148         BigInteger serial = BigInteger.ONE;
149         X500Principal subject = new X500Principal("CN=TEE");
150 
151         Instant now = Instant.now();
152         X509V3CertificateGenerator certificateBuilder = new X509V3CertificateGenerator();
153         certificateBuilder.setIssuerDN(issuer);
154         certificateBuilder.setSerialNumber(serial);
155         certificateBuilder.setNotBefore(Date.from(now));
156         certificateBuilder.setNotAfter(Date.from(now.plus(Duration.ofDays(1))));
157         certificateBuilder.setSignatureAlgorithm("SHA256WITHECDSA");
158         certificateBuilder.setSubjectDN(subject);
159         certificateBuilder.setPublicKey(publicKeyToSign);
160         certificateBuilder.addExtension(
161                 Extension.basicConstraints, /*isCritical=*/ true, new BasicConstraints(true));
162         certificateBuilder.addExtension(
163                 Extension.keyUsage, /*isCritical=*/ true, new KeyUsage(KeyUsage.keyCertSign));
164         return certificateBuilder.generate(issuerKeyPair.getPrivate());
165     }
166 
encodeAndSignSign1Ed25519(byte[] encodedPublicKey, byte[] privateKey)167     public static Array encodeAndSignSign1Ed25519(byte[] encodedPublicKey, byte[] privateKey)
168             throws Exception {
169         byte[] encodedProtectedHeaders = encodeSimpleMap(1, -8);
170         return (Array) (new CborBuilder()
171             .addArray()
172                 .add(encodedProtectedHeaders)      // Protected headers
173                 .addMap()                          // Empty unprotected Headers
174                     .end()
175                 .add(encodedPublicKey)
176                 .add(encodeAndSignSigStructure(
177                         encodedProtectedHeaders, encodedPublicKey, privateKey, CURVE_ED25519))
178             .end()
179             .build().get(0));
180     }
181 
encodeAndSignSign1Ecdsa256(byte[] encodedPublicKey, byte[] privateKey)182     public static Array encodeAndSignSign1Ecdsa256(byte[] encodedPublicKey, byte[] privateKey)
183             throws Exception {
184         byte[] encodedProtectedHeaders = encodeSimpleMap(1, -7);
185         return (Array) (new CborBuilder()
186             .addArray()
187                 .add(encodedProtectedHeaders)      // Protected headers
188                 .addMap()                          // Empty unprotected Headers
189                     .end()
190                 .add(encodedPublicKey)
191                 .add(encodeAndSignSigStructure(
192                         encodedProtectedHeaders, encodedPublicKey, privateKey, CURVE_P256))
193             .end()
194             .build().get(0));
195     }
196 
encodeAndSignSigStructure( byte[] protectedHeaders, byte[] payload, byte[] privateKey, int curve)197     private static byte[] encodeAndSignSigStructure(
198             byte[] protectedHeaders, byte[] payload, byte[] privateKey,
199             int curve) throws Exception {
200         return encodeAndSignSigStructure(protectedHeaders, null, payload,
201                 privateKey, curve);
202     }
203 
encodeAndSignSigStructure(byte[] protectedHeaders, byte[] externalAad, byte[] payload, byte[] privateKey, int curve)204     private static byte[] encodeAndSignSigStructure(byte[] protectedHeaders, byte[] externalAad,
205             byte[] payload, byte[] privateKey, int curve)
206             throws Exception {
207         ByteArrayOutputStream baos = new ByteArrayOutputStream();
208         new CborEncoder(baos).encode(new CborBuilder()
209                 .addArray()
210                 .add("Signature1")                                      // context string
211                 .add(protectedHeaders)                                  // protected headers
212                 .add(null == externalAad ? new byte[0] : externalAad)   // external aad
213                 .add(payload)                                           // payload
214                 .end()
215                 .build());
216         if (curve == CURVE_ED25519) {
217             Ed25519Sign signer = new Ed25519Sign(privateKey);
218             return signer.sign(baos.toByteArray());
219         } else {
220             ECPrivateKey privKey = EllipticCurves.getEcPrivateKey(
221                     EllipticCurves.CurveType.NIST_P256, privateKey);
222             EcdsaSignJce ecdsaSigner = new EcdsaSignJce(privKey, SHA256, IEEE_P1363);
223             return ecdsaSigner.sign(baos.toByteArray());
224         }
225     }
226 
encodeEd25519PubKey(byte[] publicKey)227     public static byte[] encodeEd25519PubKey(byte[] publicKey) throws Exception {
228         ByteArrayOutputStream baos = new ByteArrayOutputStream();
229         new CborEncoder(baos).encode(new CborBuilder()
230                 .addMap()
231                     .put(KEY_TYPE, KEY_TYPE_OKP)
232                     .put(ALGORITHM, ALGORITHM_EDDSA)
233                     .put(CURVE, CURVE_ED25519)
234                     .put(X_COORDINATE, publicKey)
235                     .end()
236                 .build());
237         return baos.toByteArray();
238     }
239 
encodeP256PubKey(byte[] pubX, byte[] pubY, boolean isEek)240     public static byte[] encodeP256PubKey(byte[] pubX, byte[] pubY, boolean isEek)
241             throws Exception {
242         ByteArrayOutputStream baos = new ByteArrayOutputStream();
243         MapBuilder<CborBuilder> cborBuilder = new CborBuilder()
244                 .addMap()
245                     .put(KEY_TYPE, KEY_TYPE_EC2)
246                     .put(ALGORITHM, isEek ? ALGORITHM_ECDH_ES_HKDF_256 : ALGORITHM_ES256)
247                     .put(CURVE, CURVE_P256)
248                     .put(X_COORDINATE, pubX)
249                     .put(Y_COORDINATE, pubY);
250         List<DataItem> coseKey;
251         if (isEek) {
252             MessageDigest digest = MessageDigest.getInstance("SHA-256");
253             digest.update(pubX);
254             byte[] kid = digest.digest(pubY);
255             coseKey = cborBuilder.put(KID, kid).end().build();
256         } else {
257             coseKey = cborBuilder.end().build();
258         }
259         new CborEncoder(baos).encode(coseKey);
260         return baos.toByteArray();
261     }
262 
263 
encodeX25519PubKey(byte[] publicKey)264     public static byte[] encodeX25519PubKey(byte[] publicKey) throws Exception {
265         ByteArrayOutputStream baos = new ByteArrayOutputStream();
266         MessageDigest digest = MessageDigest.getInstance("SHA-256");
267         byte[] kid = digest.digest(publicKey);
268         new CborEncoder(baos).encode(new CborBuilder()
269                 .addMap()
270                     .put(KEY_TYPE, KEY_TYPE_OKP)
271                     .put(KID, kid)
272                     .put(ALGORITHM, ALGORITHM_ECDH_ES_HKDF_256)
273                     .put(CURVE, CURVE_X25519)
274                     .put(X_COORDINATE, publicKey)
275                     .end()
276                 .build());
277         return baos.toByteArray();
278     }
279 
encodeSimpleMap(int key, int value)280     private static byte[] encodeSimpleMap(int key, int value) throws Exception {
281         ByteArrayOutputStream baos = new ByteArrayOutputStream();
282         new CborEncoder(baos).encode(new CborBuilder()
283                 .addMap()
284                     .put(key, value)
285                     .end()
286                 .build());
287         return baos.toByteArray();
288     }
289 }
290