identity: Add a utility function to convert P-256 EC public key to DER format
Bug: 240359297
Test: atest --rerun-until-failure 1000 android.security.identity.cts.ReaderAuthTest#readerAuth
Change-Id: I38e1085db04478c91306e0d51f66de061a034e18
diff --git a/identity/util/src/java/com/android/security/identity/internal/Iso18013.java b/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
index 2561fcc..b47009b 100644
--- a/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
+++ b/identity/util/src/java/com/android/security/identity/internal/Iso18013.java
@@ -146,36 +146,9 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
baos.write(new byte[]{41});
- ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
- // Each coordinate may be encoded in 33*, 32, or fewer bytes.
- //
- // * : it can be 33 bytes because toByteArray() guarantees "The array will contain the
- // minimum number of bytes required to represent this BigInteger, including at
- // least one sign bit, which is (ceil((this.bitLength() + 1)/8))" which means that
- // the MSB is always 0x00. This is taken care of by calling calling
- // stripLeadingZeroes().
- //
- // We need the encoding to be exactly 32 bytes since according to RFC 5480 section 2.2
- // and SEC 1: Elliptic Curve Cryptography section 2.3.3 the encoding is 0x04 | X | Y
- // where X and Y are encoded in exactly 32 byte, big endian integer values each.
- //
- byte[] xBytes = stripLeadingZeroes(w.getAffineX().toByteArray());
- if (xBytes.length > 32) {
- throw new RuntimeException("xBytes is " + xBytes.length + " which is unexpected");
- }
- for (int n = 0; n < 32 - xBytes.length; n++) {
- baos.write(0x00);
- }
- baos.write(xBytes);
- byte[] yBytes = stripLeadingZeroes(w.getAffineY().toByteArray());
- if (yBytes.length > 32) {
- throw new RuntimeException("yBytes is " + yBytes.length + " which is unexpected");
- }
- for (int n = 0; n < 32 - yBytes.length; n++) {
- baos.write(0x00);
- }
- baos.write(yBytes);
+ ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
+ baos.write(Util.convertP256PublicKeyToDERFormat(w));
baos.write(new byte[]{42, 44});
} catch (IOException e) {
@@ -303,18 +276,4 @@
throw new IllegalStateException("Error performing key agreement", e);
}
}
-
- private static byte[] stripLeadingZeroes(byte[] value) {
- int n = 0;
- while (n < value.length && value[n] == 0) {
- n++;
- }
- int newLen = value.length - n;
- byte[] ret = new byte[newLen];
- int m = 0;
- while (n < value.length) {
- ret[m++] = value[n++];
- }
- return ret;
- }
}
diff --git a/identity/util/src/java/com/android/security/identity/internal/Util.java b/identity/util/src/java/com/android/security/identity/internal/Util.java
index 94d7d15..de7369a 100644
--- a/identity/util/src/java/com/android/security/identity/internal/Util.java
+++ b/identity/util/src/java/com/android/security/identity/internal/Util.java
@@ -1130,6 +1130,46 @@
Log.e(TAG, name + ": dumping " + data.length + " bytes\n" + fmt.toString());
}
+ // Convert EC P256 public key to DER format binary format
+ public static byte[] convertP256PublicKeyToDERFormat(ECPoint w) {
+ byte[] ret = new byte[64];
+
+ // Each coordinate may be encoded in 33*, 32, or fewer bytes.
+ //
+ // * : it can be 33 bytes because toByteArray() guarantees "The array will contain the
+ // minimum number of bytes required to represent this BigInteger, including at
+ // least one sign bit, which is (ceil((this.bitLength() + 1)/8))" which means that
+ // the MSB is always 0x00. This is taken care of by calling calling
+ // stripLeadingZeroes().
+ //
+ // We need the encoding to be exactly 32 bytes since according to RFC 5480 section 2.2
+ // and SEC 1: Elliptic Curve Cryptography section 2.3.3 the encoding is 0x04 | X | Y
+ // where X and Y are encoded in exactly 32 byte, big endian integer values each.
+ //
+ byte[] xBytes = stripLeadingZeroes(w.getAffineX().toByteArray());
+ if (xBytes.length > 32) {
+ throw new RuntimeException("xBytes is " + xBytes.length + " which is unexpected");
+ }
+ for (int n = 0; n < 32 - xBytes.length; n++) {
+ ret[n] = 0x00;
+ }
+ for (int n = 32 - xBytes.length; n < xBytes.length; n++) {
+ ret[n] = xBytes[n];
+ }
+
+ byte[] yBytes = stripLeadingZeroes(w.getAffineY().toByteArray());
+ if (yBytes.length > 32) {
+ throw new RuntimeException("yBytes is " + yBytes.length + " which is unexpected");
+ }
+ for (int n = 0; n < 32 - yBytes.length; n++) {
+ ret[32 + n] = 0x00;
+ }
+ for (int n = 32 - yBytes.length; n < yBytes.length; n++) {
+ ret[32 + n] = yBytes[n];
+ }
+
+ return ret;
+ }
// This returns a SessionTranscript which satisfy the requirement
// that the uncompressed X and Y coordinates of the public key for the
@@ -1142,36 +1182,9 @@
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try {
baos.write(new byte[]{42});
- ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
- // Each coordinate may be encoded in 33*, 32, or fewer bytes.
- //
- // * : it can be 33 bytes because toByteArray() guarantees "The array will contain the
- // minimum number of bytes required to represent this BigInteger, including at
- // least one sign bit, which is (ceil((this.bitLength() + 1)/8))" which means that
- // the MSB is always 0x00. This is taken care of by calling calling
- // stripLeadingZeroes().
- //
- // We need the encoding to be exactly 32 bytes since according to RFC 5480 section 2.2
- // and SEC 1: Elliptic Curve Cryptography section 2.3.3 the encoding is 0x04 | X | Y
- // where X and Y are encoded in exactly 32 byte, big endian integer values each.
- //
- byte[] xBytes = stripLeadingZeroes(w.getAffineX().toByteArray());
- if (xBytes.length > 32) {
- throw new RuntimeException("xBytes is " + xBytes.length + " which is unexpected");
- }
- for (int n = 0; n < 32 - xBytes.length; n++) {
- baos.write(0x00);
- }
- baos.write(xBytes);
- byte[] yBytes = stripLeadingZeroes(w.getAffineY().toByteArray());
- if (yBytes.length > 32) {
- throw new RuntimeException("yBytes is " + yBytes.length + " which is unexpected");
- }
- for (int n = 0; n < 32 - yBytes.length; n++) {
- baos.write(0x00);
- }
- baos.write(yBytes);
+ ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
+ baos.write(convertP256PublicKeyToDERFormat(w));
baos.write(new byte[]{43, 44});
} catch (IOException e) {