From cedefef70e578b32706ef73cc9b5d64c103fb548 Mon Sep 17 00:00:00 2001 From: michael Date: Thu, 11 Jun 2026 10:23:44 -0700 Subject: [PATCH] test(applet): pin deriveStepUpSessionKeys to spec ASCII info strings Adds a second regression test for AliroCrypto.deriveStepUpSessionKeys that hard-codes the exact RFC 5869 reference computation (empty salt, "SKDevice" / "SKReader" ASCII info) with a different IKM byte pattern and independent expand-once helper. Complements the existing deriveStepUpSessionKeysMatchesManualHkdf so a future spec misread in shared helper code can't slip through. Implementation itself was landed earlier in f94e416 ("fix: AUTH1 crypto interop with stock X-CUBE-ALIRO + EXCHANGE compat stub"); this is the M1A.1 task's tightened pin from the Step-Up v2 plan. Co-Authored-By: Claude Opus 4.7 --- .../aliro/AliroCryptoTest.java | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/applet/src/test/java/com/dangerousthings/aliro/AliroCryptoTest.java b/applet/src/test/java/com/dangerousthings/aliro/AliroCryptoTest.java index 5bedbfc..05cc9ec 100644 --- a/applet/src/test/java/com/dangerousthings/aliro/AliroCryptoTest.java +++ b/applet/src/test/java/com/dangerousthings/aliro/AliroCryptoTest.java @@ -167,6 +167,50 @@ class AliroCryptoTest { } } + /** + * Independent pin against an inline RFC 5869 reference (one HKDF-Expand + * block: T(1) = HMAC(PRK, info || 0x01)). Complements + * {@link #deriveStepUpSessionKeysMatchesManualHkdf} by anchoring on the + * exact ASCII info strings the spec specifies ("SKDevice" / "SKReader") + * with a different IKM byte pattern, so a future spec misread can't be + * masked by sharing helper code with the equivalent test above. + */ + @Test + void deriveStepUpSessionKeysMatchesRfc5869WithSpecInfoStrings() throws Exception { + AliroCrypto crypto = new AliroCrypto(); + byte[] stepUpSK = new byte[32]; + for (byte i = 0; i < 32; i++) stepUpSK[i] = (byte) (0x10 + i); + + byte[] skDevice = new byte[32]; + byte[] skReader = new byte[32]; + crypto.deriveStepUpSessionKeys( + stepUpSK, (short) 0, + skDevice, (short) 0, + skReader, (short) 0); + + // Reference HKDF-SHA-256 with empty salt + javax.crypto.Mac extractMac = javax.crypto.Mac.getInstance("HmacSHA256"); + extractMac.init(new javax.crypto.spec.SecretKeySpec(new byte[32], "HmacSHA256")); + byte[] prk = extractMac.doFinal(stepUpSK); + + byte[] expectedDevice = expandOnce(prk, "SKDevice".getBytes("US-ASCII")); + byte[] expectedReader = expandOnce(prk, "SKReader".getBytes("US-ASCII")); + + assertArrayEquals(expectedDevice, skDevice, + "StepUpSKDevice = HKDF-Expand(PRK, info=ASCII(\"SKDevice\"), L=32)"); + assertArrayEquals(expectedReader, skReader, + "StepUpSKReader = HKDF-Expand(PRK, info=ASCII(\"SKReader\"), L=32)"); + } + + private static byte[] expandOnce(byte[] prk, byte[] info) throws Exception { + // T(1) = HMAC-SHA-256(PRK, info || 0x01); first 32 bytes is the output + javax.crypto.Mac mac = javax.crypto.Mac.getInstance("HmacSHA256"); + mac.init(new javax.crypto.spec.SecretKeySpec(prk, "HmacSHA256")); + mac.update(info); + mac.update((byte) 0x01); + return mac.doFinal(); + } + @Test void hkdfExpandRfc5869TestCase1() { AliroCrypto crypto = new AliroCrypto();