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