identity: Fix buildSessionTranscript() from a zero leading P-256 EC Public Key.

Properly encode an sessiontranscript from P-256 EC Public Key, in
particular ensure that the resulting sessiontranscript which includes an valid P-256 EC public key is expected.

Was able to reproduce this with about 1% failures running a test.
After the fix didn't get a failure in 1,000 runs.

This bug is similar with AOSP patch "identity: Fix uncompressed form encoding of P-256 EC Public Key."

Bug: 239857653
Test: atest --rerun-until-failure 1000  android.security.identity.cts.ReaderAuthTest#readerAuth
Change-Id: Id5ce46977cf3b6ce6c43beda657cd26b24969fe5
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 6da90e5..2561fcc 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
@@ -145,14 +145,38 @@
         // encoded DeviceEngagement
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try {
-            ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
-            // X and Y are always positive so for interop we remove any leading zeroes
-            // inserted by the BigInteger encoder.
-            byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
-            byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
             baos.write(new byte[]{41});
-            baos.write(x);
-            baos.write(y);
+            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);
+
             baos.write(new byte[]{42, 44});
         } catch (IOException e) {
             e.printStackTrace();
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 4ec54a7..94d7d15 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
@@ -1141,14 +1141,38 @@
         // encoded DeviceEngagement
         ByteArrayOutputStream baos = new ByteArrayOutputStream();
         try {
-            ECPoint w = ((ECPublicKey) ephemeralKeyPair.getPublic()).getW();
-            // X and Y are always positive so for interop we remove any leading zeroes
-            // inserted by the BigInteger encoder.
-            byte[] x = stripLeadingZeroes(w.getAffineX().toByteArray());
-            byte[] y = stripLeadingZeroes(w.getAffineY().toByteArray());
             baos.write(new byte[]{42});
-            baos.write(x);
-            baos.write(y);
+            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);
+
             baos.write(new byte[]{43, 44});
         } catch (IOException e) {
             e.printStackTrace();