Three independent spec-misreads found via Path X investigation against
the X-CUBE-ALIRO vendor library, all causing
ACWG_Error_Crypto_EncryptDecrypt on the vendor's processAUTH1ResponsePayload.
Each was symmetric between this applet and our PC/SC reader, so
aliro-bench-test passed against our own host-side reader but failed
against any spec-compliant third-party reader. Path X also surfaced
an X-CUBE-ALIRO-specific compat shim (bitmap + EXCHANGE stub) which is
documented to be retired by the Step-Up Milestone 1 work.
1. salt_volatile dropped x(credential_long_term_pub) at the end.
§8.3.1.13 salt_volatile ends at the 0xA5 proprietary information TLV;
the credential key belongs in `info` (and even there it's the
EPHEMERAL one, which buildInfo already does correctly).
2. Kdh now uses X9.63 KDF per §8.3.1.4 instead of HKDF.
The §8.3.1.4 closing note ("actual key derivation is performed using
§8.3.1.5") refers to subsequent session-key derivation from Kdh
(§8.3.1.13 -> §8.3.1.5 HKDF), NOT a substitution for Kdh itself.
For 32-byte output X9.63 KDF reduces to:
Kdh = SHA-256(ZAB || 0x00000001 || transaction_identifier)
Added a native SHA-256 instance to AliroCrypto for this one-shot.
3. salt_volatile flag uses AUTH1's command_parameters, not AUTH0's.
§8.3.1.13 says "command_parameters || authentication_policy from the
command data field". When §8.3.1.13 runs (after AUTH1), the active
request is AUTH1; authentication_policy only exists in AUTH0 so it's
still pulled from saved AUTH0 state, but command_parameters is the
AUTH1 value (typically 0x01 = "request credential_PubK in response").
4. signaling_bitmap kept at 0x0005 when AD provisioned + INS_EXCHANGE
stub on AliroApplet returns 9000 with empty payload. Empirically the
X-CUBE-ALIRO vendor library errors on bitmap=0x0000 even though the
spec allows it (separate vendor quirk worth filing); EXCHANGE stub
exists because the firmware unconditionally sends 0xC9 post-AUTH1
for the Reader Status sub-event report. Both shims are documented to
be retired in Step-Up Milestone 1 -- StepUpApplet will handle 0xC9
on its own AID (ACCE5502) per §10.2.1 after the spec-mandated
step-up AID SELECT.
PC/SC bench-test still passes: AUTH1=9000, ~3.2 s, bitmap=0x0005.
Nucleo X-CUBE-ALIRO firmware now reports retval=ACWG_OK on
processAUTH1ResponsePayload (confirmed against j3r452 UID
04565E4A0B2190 in /tmp/nucleo-three-fixes.log). The remaining
"DOOR OPERATION FAILED" on Nucleo is downstream Step-Up not being
implemented yet -- StepUpApplet is still the scaffold and returns
6D00/6E00 to ENVELOPE / EXCHANGE. That's Milestone 1 work.
80/80 Java tests + 126/126 Python tests pass.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds INS_DIAG_HMAC (0xD0), INS_DIAG_ECDH (0xD1), INS_DIAG_ECDSA_SIGN
(0xD2), and INS_DIAG_GCM (0xD3) to AliroApplet, gated by a
DIAGNOSTICS_ENABLED compile-time flag so they're trivially strippable
for production builds. Each INS runs its primitive N times (N in P3 byte)
against hardcoded card-side test vectors and returns the result. Output
lands in the APDU buffer at offset 16, never in scratch -- the AUTH
session state stays intact even if a diag INS runs mid-session.
The HMAC INS routes through a new AliroCrypto.diagHmac() wrapper that
exposes the internal AliroHmac instance without leaking it through the
class boundary. ECDH/ECDSA use a dedicated diagKeyPair (separate from
the protocol's credentialEphemeralKeyPair) so a diagnostic call can
never disturb a real AUTH flow.
These INSes are what aliro-bench-profile drives over PC/SC -- they let
us measure each AUTH1 primitive's cost in isolation and reconstruct the
AUTH1 budget against the bench-test wall-clock.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three components, all bench-validated to varying depths:
- applet/: CSA Aliro v1.0 Java Card applet for J3R180. AUTH0 + AUTH1
expedited-standard flow end-to-end green via PC/SC bench reader
(aliro-bench-test). Userland AES-256-GCM and HMAC-SHA-256 layered
on top of J3R180's primitives because the card lacks both natively.
P-256 curve params seeded explicitly per J3R180's quirk.
- harness/: Python orchestrator (aliro-trustgen, aliro-personalize,
aliro-bench-test) for trust-bundle generation, card personalization
via PersonalizationApplet, and PC/SC AUTH0+AUTH1 transactions. 126
pytest cases passing.
- reader/STM32CubeExpansion_ALIRO_V1_0_0/: ST X-CUBE-ALIRO V1.0.0
with our NFC10A1 port (NUCLEO-U545RE-Q + X-NUCLEO-NFC10A1, ST25R200
shared with NFC09A1). nfc10-only/ project, NFC10A1 BSP shim,
ALIRO_TRUST_OVERRIDE include into vendor's provisioning.c, and an
ALIRO_APDU_TRACE wrapper around demoTransceiveBlocking. Boots,
detects the J3R180, completes SELECT + AUTH0; AUTH1 currently fails
with RFAL ERR_PROTO (0xB) — under investigation, see
docs/plans/2026-04-20-nucleo-nfc10a1-port.md and bench-notes/.
Excluded: x-cube-aliro.zip vendor archive, harness/.venv, build dirs,
generated aliro_trust.h (contains private reader scalar), all PEMs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>