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