diff --git a/applet/src/main/java/com/dangerousthings/aliro/AliroApplet.java b/applet/src/main/java/com/dangerousthings/aliro/AliroApplet.java index 1041a95..b241830 100644 --- a/applet/src/main/java/com/dangerousthings/aliro/AliroApplet.java +++ b/applet/src/main/java/com/dangerousthings/aliro/AliroApplet.java @@ -837,15 +837,14 @@ public class AliroApplet extends Applet { // // Bit 0 reflects whether we can actually serve an AD: it requires // both hasAccessDocument() (finalized) AND isAccessDocumentVerified() - // (IssuerAuth check passed). Post-M2A.3 finalize requires verify so - // the "finalized but not verified" branch is defensive — see - // CredentialStore.markAccessDocumentVerified() callers. + // (IssuerAuth check passed). finalizeAccessDocument() commits both + // flags atomically, so the "finalized but not verified" branch is + // defensive. // - // Historical note: M1 hardcoded 0x0005 because the closed-source - // ACWG_processAUTH1ResponsePayload() in X-CUBE-ALIRO's Aliro.a errors - // out when bits 0 + 2 are clear and AD is provisioned. With bit 2 - // now always set we still keep that library happy, and bit 0 only - // makes a promise we can actually keep. + // Note: the closed-source ACWG_processAUTH1ResponsePayload() in + // X-CUBE-ALIRO's Aliro.a errors out when bits 0 + 2 are clear and AD + // is provisioned. With bit 2 now always set we still keep that + // library happy, and bit 0 only makes a promise we can actually keep. byte bitmapLo = 0x04; // bit 2 — split-AID step-up architecture if (store.hasAccessDocument() && store.isAccessDocumentVerified()) { bitmapLo |= 0x01; // bit 0 — AD retrievable diff --git a/applet/src/main/java/com/dangerousthings/aliro/CredentialStore.java b/applet/src/main/java/com/dangerousthings/aliro/CredentialStore.java index 1d7a89a..7eec6db 100644 --- a/applet/src/main/java/com/dangerousthings/aliro/CredentialStore.java +++ b/applet/src/main/java/com/dangerousthings/aliro/CredentialStore.java @@ -268,7 +268,14 @@ final class CredentialStore { return accessDocumentFinalized; } - void markAccessDocumentVerified() { + /** + * Test-only: forces the verified flag without running the IssuerAuth + * COSE_Sign1 verify. Used by tests that exercise downstream behavior + * (signaling bitmap, serialization round-trip) with opaque AD bytes that + * aren't signed by a real issuer key. Production code reaches the verified + * state via {@link #finalizeAccessDocument(short, CoseVerifier, byte[])}. + */ + void markAccessDocumentVerifiedForTesting() { accessDocumentVerified = true; } diff --git a/applet/src/main/java/com/dangerousthings/aliro/StepUpApplet.java b/applet/src/main/java/com/dangerousthings/aliro/StepUpApplet.java index afab6a6..8ab20b3 100644 --- a/applet/src/main/java/com/dangerousthings/aliro/StepUpApplet.java +++ b/applet/src/main/java/com/dangerousthings/aliro/StepUpApplet.java @@ -44,19 +44,17 @@ import javacard.framework.Util; * the entire encrypted DeviceResponse then chunk it; pipe plaintext * through GCM as we emit ENVELOPE response chunks. Reduces transient * footprint AND minimizes plaintext residence in RAM. - *
  • Opt 4 — IssuerAuth verify cached at personalization. See - * {@link CredentialStore#finalizeAccessDocument(short)} TODO — the - * COSE_Sign1 verify happens once at write time, the persistent + *
  • Opt 4 — IssuerAuth verify cached at personalization. + * {@link CredentialStore#finalizeAccessDocument} runs the COSE_Sign1 + * verify once at write time; the persistent * {@code accessDocumentVerified} flag is checked here at read time. * Saves ~100 ms per transaction. Trusts that the Credential Issuer * trust anchor is fixed for the card's lifetime — true for DT's * implantable target but document the limitation.
  • * * - *

    The {@code AliroApplet.INS_EXCHANGE} stub was retired once StepUpApplet - * landed handling for 0xC9 directly — spec-conformant readers (X-CUBE-ALIRO - * included, once the upstream crypto interop is right) route EXCHANGE here - * after the step-up AID SELECT per §10.2. + *

    StepUpApplet handles {@code INS_EXCHANGE} (0xC9) directly: spec-conformant + * readers route EXCHANGE here after the step-up AID SELECT per §10.2. */ public class StepUpApplet extends Applet { @@ -107,11 +105,10 @@ public class StepUpApplet extends Applet { * or {@link StepUpSession#deviceIv} before each en/decrypt. CLEAR_ON_DESELECT. */ private final byte[] ivScratch; - /** Persistent EXCHANGE plaintext sink for the decrypt-and-discard path. - * Sized to the largest reasonable Reader Status sub-event we'd see - * during M1 (X-CUBE-ALIRO observed values are well under 64 B); we'll - * resize when the real Reader Status payload size lands. CLEAR_ON_DESELECT - * so post-deselect there's no plaintext residue. */ + /** Transient plaintext sink for ENVELOPE DeviceRequest and EXCHANGE + * Reader Status request decrypts. Sized for the largest reasonable + * reader-supplied payload; CLEAR_ON_DESELECT so post-deselect there's + * no plaintext residue. */ private final byte[] scratchPlaintext; private static final short SCRATCH_PLAINTEXT_LEN = 256; @@ -173,11 +170,10 @@ public class StepUpApplet extends Applet { * SELECT entry point. If {@link SessionContext} is armed (i.e. AUTH1 on * {@link AliroApplet} just succeeded and parked {@code StepUpSK}), copy * it out and derive {@code StepUpSKDevice}/{@code StepUpSKReader} via - * the §8.4.3 HKDF so the ENVELOPE / EXCHANGE handlers in M1B / M1C can - * AES-256-GCM with them. Always returns true — an un-armed SELECT (e.g. - * a reader that touches the Step-Up AID before AUTH1) still gets a - * successful FCI; downstream handlers will reject commands that need a - * live session. + * the §8.4.3 HKDF so the ENVELOPE / EXCHANGE handlers can AES-256-GCM + * with them. Always returns true — an un-armed SELECT (e.g. a reader + * that touches the Step-Up AID before AUTH1) still gets a successful + * FCI; downstream handlers will reject commands that need a live session. */ @Override public boolean select() { @@ -237,8 +233,8 @@ public class StepUpApplet extends Applet { } /** - * EXCHANGE (CLA=0x80, INS=0xC9) handler — M2E.1 (request validation) + - * M2E.2 (encrypted Reader Status response). + * EXCHANGE (CLA=0x80, INS=0xC9) handler: request validation + encrypted + * Reader Status response. * *

    Spec §8.3.3.5 / Tables 8-19 + 8-20. The reader sends * {@code encrypted_payload || authentication_tag} under @@ -309,7 +305,7 @@ public class StepUpApplet extends Applet { // semantically validates). session.advanceReaderCounter(); - // M2E.1: parse the request shape: [sub_event_id, payload_len, payload]. + // Parse the request shape: [sub_event_id, payload_len, payload]. // Need at least sub_event_id + payload_len = 2 bytes. if (ptLen < 2) { Util.arrayFillNonAtomic(scratchPlaintext, (short) 0, ptLen, (byte) 0); @@ -331,9 +327,9 @@ public class StepUpApplet extends Applet { // Wipe the request plaintext — content not needed past validation. Util.arrayFillNonAtomic(scratchPlaintext, (short) 0, ptLen, (byte) 0); - // M2E.2: build the Reader Status response plaintext directly into - // the APDU buffer at offset 0, then encrypt in place. AliroGcm.encrypt - // supports out == pt at the same offset (CTR mode + appended tag). + // Build the Reader Status response plaintext directly into the APDU + // buffer at offset 0, then encrypt in place. AliroGcm.encrypt supports + // out == pt at the same offset (CTR mode + appended tag). buf[0] = (byte) 0x01; // sub_event_id (echoes request) buf[1] = (byte) 0x00; // status = OK buf[2] = (byte) 0x00; // payload_len = 0 @@ -354,8 +350,8 @@ public class StepUpApplet extends Applet { } /** - * ENVELOPE (CLA=0x00, INS=0xC3) handler — M2D.3 (real DeviceResponse) + - * M2D.4 (ISO 7816 response chaining). + * ENVELOPE (CLA=0x00, INS=0xC3) handler: emits the canonical-CBOR + * DeviceResponse via ISO 7816 response chaining. * *

    Pipeline: *

      diff --git a/applet/src/main/java/com/dangerousthings/aliro/StepUpSession.java b/applet/src/main/java/com/dangerousthings/aliro/StepUpSession.java index c807ad6..0e3b06b 100644 --- a/applet/src/main/java/com/dangerousthings/aliro/StepUpSession.java +++ b/applet/src/main/java/com/dangerousthings/aliro/StepUpSession.java @@ -18,7 +18,7 @@ import javacard.framework.Util; *