Signing EEK chain properly in tests. am: 4b059758b5
Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/RemoteProvisioner/+/15044426
Change-Id: I14816e6bebaf2254997b2b4d02224fa929d5357e
diff --git a/tests/unittests/src/com/android/remoteprovisioner/unittest/SystemInterfaceTest.java b/tests/unittests/src/com/android/remoteprovisioner/unittest/SystemInterfaceTest.java
index 5d0f7aa..2a2adc4 100644
--- a/tests/unittests/src/com/android/remoteprovisioner/unittest/SystemInterfaceTest.java
+++ b/tests/unittests/src/com/android/remoteprovisioner/unittest/SystemInterfaceTest.java
@@ -27,7 +27,6 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
import android.hardware.security.keymint.DeviceInfo;
import android.hardware.security.keymint.ProtectedData;
@@ -49,7 +48,6 @@
import co.nstant.in.cbor.CborBuilder;
import co.nstant.in.cbor.CborDecoder;
import co.nstant.in.cbor.CborEncoder;
-import co.nstant.in.cbor.CborException;
import co.nstant.in.cbor.model.Array;
import co.nstant.in.cbor.model.ByteString;
import co.nstant.in.cbor.model.DataItem;
@@ -68,11 +66,10 @@
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.KeyStore;
-import java.security.MessageDigest;
-import java.security.NoSuchAlgorithmException;
import java.security.PublicKey;
import java.security.cert.Certificate;
import java.security.cert.X509Certificate;
+import java.util.Arrays;
import java.util.List;
import java.util.Random;
@@ -92,6 +89,7 @@
mBinder =
IRemoteProvisioning.Stub.asInterface(ServiceManager.getService(SERVICE));
assertNotNull(mBinder);
+ mBinder.deleteAllKeys();
}
@After
@@ -99,82 +97,16 @@
mBinder.deleteAllKeys();
}
- private static byte[] serializeProtectedHeaders() throws CborException {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new CborBuilder()
- .addMap()
- .put(1, -8) // Algorithm, EdDSA
- .end()
- .build());
- return baos.toByteArray();
- }
-
- private static byte[] buildSignatureKey() throws CborException {
- byte[] key = new byte[32];
- new Random().nextBytes(key);
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new CborBuilder()
- .addMap()
- .put(1, 1) // Key type, OKP
- .put(3, -8) // Algorithm, EdDSA
- .put(-1, 6) // Curve, Ed25519
- .put(-2, key) // public key, bytes
- .end()
- .build());
- return baos.toByteArray();
- }
-
- private static Array buildSignedSignatureKey() throws CborException {
- return (Array) (new CborBuilder()
- .addArray()
- .add(serializeProtectedHeaders())
- .addMap()
- .end()
- .add(buildSignatureKey())
- //signature; test mode will instruct the HAL component to skip verification
- .add(new byte[0])
- .end()
- .build().get(0));
- }
-
- private static byte[] buildEek(byte[] eek) throws CborException {
- try {
- MessageDigest digest = MessageDigest.getInstance("SHA-256");
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- new CborEncoder(baos).encode(new CborBuilder()
- .addMap()
- .put(1, 1) // Key type, OKP
- .put(2, digest.digest(eek)) // KID: EEK ID
- .put(3, -25) // Algorithm
- .put(-1, 4) // Curve, X25519
- .put(-2, eek) // public key, bytes
- .end()
- .build());
- return baos.toByteArray();
- } catch (NoSuchAlgorithmException e) {
- fail("SHA-256 somehow not available");
- return null;
- }
- }
-
- private static Array buildSignedEek(byte[] eek) throws CborException {
- return (Array) (new CborBuilder()
- .addArray()
- .add(serializeProtectedHeaders())
- .addMap()
- .end()
- .add(buildEek(eek))
- .add(new byte[0]) //signature; test mode skips this
- .end()
- .build().get(0));
- }
-
- private byte[] generateEekChain(byte[] eek) throws CborException {
+ private byte[] generateEekChain(byte[] eek) throws Exception {
+ com.google.crypto.tink.subtle.Ed25519Sign.KeyPair kp =
+ com.google.crypto.tink.subtle.Ed25519Sign.KeyPair.newKeyPair();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
new CborEncoder(baos).encode(new CborBuilder()
.addArray()
- .add(buildSignedSignatureKey())
- .add(buildSignedEek(eek))
+ .add(Utils.encodeAndSignSign1Ed25519(
+ Utils.encodeEd25519PubKey(kp.getPublicKey()), kp.getPrivateKey()))
+ .add(Utils.encodeAndSignSign1Ed25519(
+ Utils.encodeX25519PubKey(eek), kp.getPrivateKey()))
.end()
.build());
return baos.toByteArray();
@@ -221,11 +153,13 @@
public void testGenerateCSRProvisionAndUseKey() throws Exception {
DeviceInfo deviceInfo = new DeviceInfo();
ProtectedData encryptedBundle = new ProtectedData();
- int numKeys = 1;
+ int numKeys = 10;
byte[] eek = new byte[32];
new Random().nextBytes(eek);
GeekResponse geek = new GeekResponse(generateEekChain(eek), new byte[] {0x02});
- mBinder.generateKeyPair(true /* testMode */, SecurityLevel.TRUSTED_ENVIRONMENT);
+ for (int i = 0; i < numKeys; i++) {
+ mBinder.generateKeyPair(true /* testMode */, SecurityLevel.TRUSTED_ENVIRONMENT);
+ }
byte[] bundle =
SystemInterface.generateCsr(true /* testMode */, numKeys,
SecurityLevel.TRUSTED_ENVIRONMENT,
@@ -246,40 +180,51 @@
assertEquals(MajorType.ARRAY, publicKeysArr.get(0).getMajorType());
Array publicKeys = (Array) publicKeysArr.get(0);
assertEquals(numKeys, publicKeys.getDataItems().size());
- Map publicKey = (Map) publicKeys.getDataItems().get(0);
- byte[] xPub = ((ByteString) publicKey.get(new NegativeInteger(-2))).getBytes();
- byte[] yPub = ((ByteString) publicKey.get(new NegativeInteger(-3))).getBytes();
- assertEquals(xPub.length, 32);
- assertEquals(yPub.length, 32);
KeyPair rootKeyPair = generateEcdsaKeyPair();
KeyPair intermediateKeyPair = generateEcdsaKeyPair();
- PublicKey leafKeyToSign = getP256PubKeyFromBytes(xPub, yPub);
- X509Certificate[] certChain = new X509Certificate[3];
- certChain[0] = signPublicKey(intermediateKeyPair, leafKeyToSign);
- certChain[1] = signPublicKey(rootKeyPair, intermediateKeyPair.getPublic());
- certChain[2] = signPublicKey(rootKeyPair, rootKeyPair.getPublic());
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- for (int i = 0; i < certChain.length; i++) {
- os.write(certChain[i].getEncoded());
+ X509Certificate[][] certChain = new X509Certificate[numKeys][3];
+ for (int i = 0; i < numKeys; i++) {
+ Map publicKey = (Map) publicKeys.getDataItems().get(i);
+ byte[] xPub = ((ByteString) publicKey.get(new NegativeInteger(-2))).getBytes();
+ byte[] yPub = ((ByteString) publicKey.get(new NegativeInteger(-3))).getBytes();
+ assertEquals(xPub.length, 32);
+ assertEquals(yPub.length, 32);
+ PublicKey leafKeyToSign = getP256PubKeyFromBytes(xPub, yPub);
+ certChain[i][0] = signPublicKey(intermediateKeyPair, leafKeyToSign);
+ certChain[i][1] = signPublicKey(rootKeyPair, intermediateKeyPair.getPublic());
+ certChain[i][2] = signPublicKey(rootKeyPair, rootKeyPair.getPublic());
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ for (int j = 0; j < certChain[i].length; j++) {
+ os.write(certChain[i][j].getEncoded());
+ }
+ SystemInterface.provisionCertChain(X509Utils.getAndFormatRawPublicKey(certChain[i][0]),
+ certChain[i][0].getEncoded() /* leafCert */,
+ os.toByteArray() /* certChain */,
+ System.currentTimeMillis() + 2000 /* validity */,
+ SecurityLevel.TRUSTED_ENVIRONMENT,
+ mBinder);
}
- SystemInterface.provisionCertChain(X509Utils.getAndFormatRawPublicKey(certChain[0]),
- certChain[0].getEncoded(),
- os.toByteArray(),
- System.currentTimeMillis() + 2000, // Valid for 2 seconds
- SecurityLevel.TRUSTED_ENVIRONMENT,
- mBinder);
// getPoolStatus will clean the key pool before we go to assign a new provisioned key
mBinder.getPoolStatus(0, SecurityLevel.TRUSTED_ENVIRONMENT);
Certificate[] provisionedCerts1 = generateKeyStoreKey("alias");
Certificate[] provisionedCerts2 = generateKeyStoreKey("alias2");
assertEquals(4, provisionedCerts1.length);
assertEquals(4, provisionedCerts2.length);
+ boolean matched = false;
for (int i = 0; i < certChain.length; i++) {
- assertArrayEquals("i = " + i,
- provisionedCerts1[i + 1].getEncoded(), certChain[i].getEncoded());
- assertArrayEquals("i = " + i,
- provisionedCerts2[i + 1].getEncoded(), certChain[i].getEncoded());
+ if (Arrays.equals(provisionedCerts1[1].getEncoded(), certChain[i][0].getEncoded())) {
+ matched = true;
+ assertArrayEquals("Second key: j = 0",
+ provisionedCerts2[1].getEncoded(), certChain[i][0].getEncoded());
+ for (int j = 1; j < certChain[i].length; j++) {
+ assertArrayEquals("First key: j = " + j,
+ provisionedCerts1[j + 1].getEncoded(), certChain[i][j].getEncoded());
+ assertArrayEquals("Second key: j = " + j,
+ provisionedCerts2[j + 1].getEncoded(), certChain[i][j].getEncoded());
+ }
+ }
}
+ assertTrue(matched);
}
private static byte[] extractRecipientKey(Array recipients) {
diff --git a/tests/unittests/src/com/android/remoteprovisioner/unittest/Utils.java b/tests/unittests/src/com/android/remoteprovisioner/unittest/Utils.java
index ad49870..7ece397 100644
--- a/tests/unittests/src/com/android/remoteprovisioner/unittest/Utils.java
+++ b/tests/unittests/src/com/android/remoteprovisioner/unittest/Utils.java
@@ -16,16 +16,24 @@
package com.android.remoteprovisioner.unittest;
+import com.google.crypto.tink.subtle.Ed25519Sign;
+
+import co.nstant.in.cbor.CborBuilder;
+import co.nstant.in.cbor.CborEncoder;
+import co.nstant.in.cbor.model.Array;
+
import org.bouncycastle.asn1.x509.BasicConstraints;
import org.bouncycastle.asn1.x509.Extension;
import org.bouncycastle.asn1.x509.KeyUsage;
import org.bouncycastle.x509.X509V3CertificateGenerator;
+import java.io.ByteArrayOutputStream;
import java.math.BigInteger;
import java.security.AlgorithmParameters;
import java.security.KeyFactory;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
+import java.security.MessageDigest;
import java.security.PublicKey;
import java.security.cert.X509Certificate;
import java.security.spec.ECGenParameterSpec;
@@ -42,6 +50,16 @@
* Utility class for unit testing.
*/
public class Utils {
+ private static final int KEY_TYPE = 1;
+ private static final int KEY_TYPE_OKP = 1;
+ private static final int KID = 2;
+ private static final int ALGORITHM = 3;
+ private static final int ALGORITHM_EDDSA = -8;
+ private static final int ALGORITHM_ECDH_ES_HKDF_256 = -25;
+ private static final int CURVE = -1;
+ private static final int CURVE_X25519 = 4;
+ private static final int CURVE_ED25519 = 6;
+ private static final int X_COORDINATE = -2;
public static PublicKey getP256PubKeyFromBytes(byte[] xPub, byte[] yPub) throws Exception {
BigInteger x = new BigInteger(1, xPub);
@@ -83,4 +101,79 @@
Extension.keyUsage, /*isCritical=*/ true, new KeyUsage(KeyUsage.keyCertSign));
return certificateBuilder.generate(issuerKeyPair.getPrivate());
}
+
+ public static Array encodeAndSignSign1Ed25519(byte[] encodedPublicKey, byte[] privateKey)
+ throws Exception {
+ byte[] encodedProtectedHeaders = encodeSimpleMap(1, -8);
+ return (Array) (new CborBuilder()
+ .addArray()
+ .add(encodedProtectedHeaders) // Protected headers
+ .addMap() // Empty unprotected Headers
+ .end()
+ .add(encodedPublicKey)
+ .add(encodeAndSignSigStructure(
+ encodedProtectedHeaders, encodedPublicKey, privateKey))
+ .end()
+ .build().get(0));
+ }
+
+ private static byte[] encodeAndSignSigStructure(
+ byte[] protectedHeaders, byte[] payload, byte[] privateKey) throws Exception {
+ return encodeAndSignSigStructure(protectedHeaders, null, payload, privateKey);
+ }
+
+ private static byte[] encodeAndSignSigStructure(byte[] protectedHeaders, byte[] externalAad,
+ byte[] payload, byte[] privateKey)
+ throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new CborEncoder(baos).encode(new CborBuilder()
+ .addArray()
+ .add("Signature1") // context string
+ .add(protectedHeaders) // protected headers
+ .add(null == externalAad ? new byte[0] : externalAad) // external aad
+ .add(payload) // payload
+ .end()
+ .build());
+ Ed25519Sign signer = new Ed25519Sign(privateKey);
+ return signer.sign(baos.toByteArray());
+ }
+
+ public static byte[] encodeEd25519PubKey(byte[] publicKey) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new CborEncoder(baos).encode(new CborBuilder()
+ .addMap()
+ .put(KEY_TYPE, KEY_TYPE_OKP)
+ .put(ALGORITHM, ALGORITHM_EDDSA)
+ .put(CURVE, CURVE_ED25519)
+ .put(X_COORDINATE, publicKey)
+ .end()
+ .build());
+ return baos.toByteArray();
+ }
+
+ public static byte[] encodeX25519PubKey(byte[] publicKey) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ MessageDigest digest = MessageDigest.getInstance("SHA-256");
+ byte[] kid = digest.digest(publicKey);
+ new CborEncoder(baos).encode(new CborBuilder()
+ .addMap()
+ .put(KEY_TYPE, KEY_TYPE_OKP)
+ .put(KID, kid)
+ .put(ALGORITHM, ALGORITHM_ECDH_ES_HKDF_256)
+ .put(CURVE, CURVE_X25519)
+ .put(X_COORDINATE, publicKey)
+ .end()
+ .build());
+ return baos.toByteArray();
+ }
+
+ private static byte[] encodeSimpleMap(int key, int value) throws Exception {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ new CborEncoder(baos).encode(new CborBuilder()
+ .addMap()
+ .put(key, value)
+ .end()
+ .build());
+ return baos.toByteArray();
+ }
}