Files
aliro-project/docs/plans/2026-04-17-aliro-credential-design.md
Dangerous Things 782074f6ae Initial snapshot: Aliro applet, harness, Nucleo NFC10A1 reader port
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>
2026-05-02 10:17:46 -07:00

14 KiB

Aliro Credential — Design

Date: 2026-04-17 Status: Design agreed; ready for implementation planning.

1. Goal

Build a Java Card applet that implements the CSA Aliro v1.0 User Device role over NFC, plus a hardware test fixture capable of exercising it end-to-end. Target deployment is dangerousthings.com's implantable Java Card products (VivoKey Apex family); generic J3R180 cards and jcardsim serve as intermediate validation stages.

2. Scope

In scope (v1)

  • Transport: NFC only (ISO-DEP / T4AT over NFC-A, per Aliro spec §10).
  • Protocol phases: Expedited-standard, Expedited-fast, Step-up — the full NFC User Device role.
  • Roles provided by this project:
    • User Device (Java Card applet).
    • Reader (instrumented test fixture on ST hardware).
    • Credential Issuer (PC-side test CA).
  • Test fixture: NUCLEO-U545RE-Q + X-NUCLEO-NFC09A1 (ST25R200) running a modified build of ST's X-CUBE-ALIRO reference. (Earlier draft of this doc said NFC10A1 — that was a part-number mistake. NFC10A1 carries ST25R3916, which is not supported by X-CUBE-ALIRO V1.0.0; only NFC09A1/ST25R200 and NFC12A1/ST25R500 ship with BSP drivers.)
  • Development progression for the applet: jcardsim (fast loop) → J3R180 dev card (real hardware) → VivoKey Apex (final target).

Explicitly out of scope (v1)

  • BLE and BLE+UWB transports (spec §11, §12).
  • Production-grade reader infrastructure. The reader is a test rig, not a product.
  • Runtime reader provisioning. Trust anchors are compiled into reader firmware.
  • Access Manager, back-end systems.
  • Revocation Document generation as a CI-integrated workflow (generated manually from the test CA when a revocation test case is needed).
  • X.509 certificates of any kind. v1 uses:
    • reader_PubK authentication (spec §6.3) — the applet is provisioned with the raw Reader public key. No Reader certificate, no Reader System Issuer CA.
    • kid header in the Access Document's IssuerAuth (spec §7.2.1) — the Reader is provisioned with the raw Credential Issuer public key. No x5chain, no Credential Issuer certificate.
    • The create_self_signed_ca helper in harness/issuer/ca.py is retained for future v2 use but is not exercised in the v1 flow.

3. Architecture

Three cleanly separated components:

┌─────────────────────┐        NFC-A / T4AT         ┌────────────────────┐
│  PC Harness (Py)    │                             │   Reader           │
│  - Test CA / Issuer │◀── USB-CDC (logs) ──────────│   Nucleo-U545RE-Q  │
│  - Personalizer     │                             │   + NFC09A1        │
│  - Trust-anchor gen │                             │   mod X-CUBE-ALIRO │
│  - Reader log diff  │                             │                    │
│  - E2E pytest       │                             └──────────┬─────────┘
└──────────┬──────────┘                                        │
           │ PC/SC (pyscard)                                   │ 13.56 MHz
           │                                                   │
           ▼                                                   ▼
      ┌─────────────────────────────────────────────────────────────┐
      │  User Device (Java Card applet)                             │
      │  - AliroApplet (AIDs …5501 / …5502)                         │
      │  - PersonalizationApplet (separate AID, locks after COMMIT) │
      │  - jcardsim / J3R180 / Apex                                 │
      └─────────────────────────────────────────────────────────────┘

4. The Reader (test device)

Hardware

  • MCU board: NUCLEO-U545RE-Q (STM32U545RE).
  • NFC board: X-NUCLEO-NFC09A1 (ST25R200), stacked via Arduino UNO R3 headers.
  • Both officially supported by X-CUBE-ALIRO V1.0.0 (Projects/nucleo-u5/Applications/Aliro/nfc-only/); no wiring.

Hardware caveat: earlier drafts of this doc paired NUCLEO-U545RE-Q with X-NUCLEO-NFC10A1 (ST25R3916). That pairing was wrong — X-CUBE-ALIRO V1.0.0 does not include an ST25R3916 driver. The supported shields for the nucleo-u5 project are NFC09A1 (ST25R200, nfc-only/) and NFC12A1 (ST25R500, nfc12-only/).

Firmware baseline

Start from STM32CubeExpansion_ALIRO_V1_0_0/Projects/nucleo-u5/Applications/Aliro/nfc-only. Vendor the whole tree under reader/vendor/ untouched. Modifications live beside it; a separate unmodified build (reader/vendor-stock/) stays flashable so the applet can be smoke-tested against reference firmware when instrumentation is suspect.

Modifications

  1. Build-time trust anchors. The PC harness generates reader/aliro_trust.h containing:

    • Credential Issuer CA public key (issuer_ca_pubkey_x/y, 32 bytes each).
    • Reader key pair (raw ECC P-256 private / public).
    • Optional Reader System Issuer CA public key for reader_Cert validation.

    Upstream hardcoded values in Projects/Common/Aliro/Src/provisioning.c are replaced via a #include hook. CMake depends on the generated file so regenerating forces a rebuild. aliro_trust.h is gitignored; only the generating inputs are version-controlled.

  2. Structured logging. One tagged line per protocol event, emitted over the ST-LINK virtual COM port at 115200 8N1:

    [ISO8601] [tag] [hex-payload] [short-note]
    

    Hook points (all at layer boundaries, none inside crypto):

    • Raw APDU in/out at T4AT.
    • Phase transitions in app_aliro.c: AUTH0_START, AUTH0_SENT, AUTH1_SENT, PRIVATE_CHANNEL_ESTABLISHED, USER_DEVICE_VERIFIED, EXCHANGE_IN/OUT, STEP_UP_START, DEVICE_REQUEST, DEVICE_RESPONSE, CONTROL_FLOW_SENT.
    • Plaintext of private_channel payloads, captured just after the stock code's AES-GCM decrypt and just before dispatch. Accepted because this is a test rig; never ship this path to production.

    Implementation: a single log_event(tag, buf, len, fmt, …) wrapper funneling to CDC_Transmit_FS.

  3. LEDs preserved. LD1 = grant (green blink), LD2 = deny (red blink), LD3 = transaction active. Eyes-only smoke testing without the PC.

5. The Applet (system under test)

Targets

Target Role JC version Notes
jcardsim Development / fast loop 3.0.5 API No hardware needed.
J3R180 On-metal validation 3.0.5 Contactless dev card.
VivoKey Apex Production 3.0.5 Same .cap where possible.

Structure

One applet class (AliroApplet), registered twice per spec §10.2.1:

  • A000000909ACCE5501 — Expedited Phase AID.
  • A000000909ACCE5502 — Step-up Phase AID.

A second, separate applet (PersonalizationApplet) under a non-Aliro AID handles provisioning. It accepts SET_CRED_KEY, SET_ISSUER_PUBKEY, SET_ACCESS_DOC, COMMIT, and RESET commands, rejects all state-modifying commands after COMMIT, and re-unlocks only via RESET authenticated with a personalization secret.

State

  • Transient (RAM): Active phase, per-transaction ephemeral keypair, derived session keys, GCM nonce counters.
  • Persistent (EEPROM/Flash): Access Credential keypair (ECC P-256), Credential Issuer trust anchors, pre-signed Access Document bytes, up to 16 Kpersistent slots (each: reader_group_identifier 16 B + Kpersistent 32 B + iteration + expiry). Spec §6.2 mandates a minimum of 16 Kpersistent bindings per Access Credential.

Crypto primitives

All of the following are required by Aliro v1.0 and present on Java Card 3.0.5 platforms of interest:

Primitive Spec use JC API
ECC P-256 keygen Ephemeral & long-term keys KeyBuilder.TYPE_EC_FP_*
ECDH (raw point out) §8.3.1.4 shared-key derivation KeyAgreement.ALG_EC_SVDP_DH_PLAIN_XY
ECDSA-SHA-256 §8.3.1.2 signing in AUTH1 Signature.ALG_ECDSA_SHA_256
SHA-256 Digests, HKDF compressor MessageDigest.ALG_SHA_256
HMAC-SHA-256 HKDF primitive Signature.ALG_HMAC_SHA_256
HKDF §8.3.1.5 key derivation Built from HMAC-SHA-256 (~30 LOC).
AES-128-GCM private_channel confidentiality/integrity Cipher.ALG_AES_GCM (Apex support to confirm; CTR+GMAC fallback planned)
TRNG §8.3.1.1 RNG RandomData.ALG_TRNG

CBOR strategy

The applet does not sign CBOR. The Access Document arrives at personalization time already signed by the test CA; the applet stores the bytes and reuses the IssuerAuth verbatim.

During step-up, the applet parses just enough of the reader's DeviceRequest (CBOR map lookup by NameSpace + ElementIdentifier) to emit the matching IssuerSignedItems and reuses the stored IssuerAuth unchanged. A hand-rolled decoder (~200 LOC, deterministic-encoding subset only, per spec §7.2 / RFC 8949 §4.2.1) avoids pulling a library into the applet.

Flash budget (estimate)

  • Applet code: ~12 KB.
  • Persistent state: ~3 KB (keys + 16 Kpersistent slots + ~1.5 KB Access Document).
  • Well within J3R180's 180 KB.

6. The PC Harness

Language choices

  • Python 3.11 for everything PC-side (issuer / personalizer / log capture / E2E tests).
  • Java + JUnit for the jcardsim fast loop (bolting Python onto jcardsim adds cost without value).

Python libraries

Purpose Library
ECC P-256, ECDSA-SHA256, ECDH, HKDF, AES-GCM cryptography
CBOR with deterministic encoding cbor2
COSE_Sign1 for IssuerAuth pycose
Direct PC/SC applet I/O pyscard
Reader VCP capture pyserial
Test CA X.509 cryptography.x509

Test CA (harness/issuer/)

Deterministic and on-disk. Inputs version-controlled; outputs clearly marked TEST-ONLY. A single trustgen command regenerates:

  • Credential Issuer CA key pair + root cert.
  • Reader System Issuer CA key pair + root cert.
  • A test Reader cert.
  • A test Access Credential key pair.
  • A default Access Document: permissive access rule, no schedule restriction, TimeVerificationRequired=false.

Outputs drop into trustgen/out/, where both (a) the applet personalizer and (b) the reader trust-anchor header generator consume them.

7. Repo layout

aliro_project/
├── applet/                  # Java Card source (Gradle + ant-javacard)
│   ├── src/main/java/...    # AliroApplet, PersonalizationApplet, CBOR, HKDF
│   └── src/test/java/...    # JUnit + jcardsim tests
├── reader/
│   ├── vendor/              # STM32CubeExpansion_ALIRO_V1_0_0/ (modified)
│   ├── vendor-stock/        # Unmodified reference copy for diagnosis
│   └── aliro_trust.h        # Generated, gitignored
├── harness/                 # Python 3.11 + uv
│   ├── issuer/              # Test CA & Access Document builder
│   ├── personalize/         # pyscard-based loader for PersonalizationApplet
│   ├── reader_log/          # pyserial capture + tag parser + trace differ
│   ├── trustgen/            # Emits reader/aliro_trust.h from CA + reader keys
│   └── tests/               # End-to-end pytest (hardware required)
├── spec/                    # Aliro 1.0 PDF + extracted AID/OID cheatsheet
└── docs/plans/              # This file.

8. Test strategy

Two complementary loops.

Fast loop — jcardsim + JUnit

  • Runs on every applet change. Seconds. No hardware.
  • Covers protocol correctness, crypto math, state machine transitions, CBOR edge cases, Kpersistent slot eviction, step-up DeviceRequest variants.
  • Drives the applet through the same APDUs the real reader would send.

Hardware loop — pytest

  • Personalize a card via PC/SC (pyscard) → place at reader antenna → capture reader VCP log → assert expected trace.
  • Slow. Catches jcardsim-vs-real-card divergence (GCM availability, transient-object lifetimes, T=CL framing quirks).
  • Gatekeeper before every card port (J3R180 → Apex).

Top-level build targets

  • make applet — JUnit + jcardsim tests, build .cap.
  • make reader — regenerate trust header, build firmware, flash.
  • make e2e — expects card on desk and reader enumerated on USB.

9. Security & safety notes

  • The reader firmware logs private_channel plaintext. Test-only. The production branch (if any is ever cut from this code) must #ifdef those logs out. Flagged here so a future reader isn't tempted to ship this image.
  • Test CA keys live in-repo, clearly marked. They exist to make flashes and resets reproducible; they have no production use.
  • Personalization secret for the PersonalizationApplet RESET command lives in the harness config alongside the test CA — also test-only.

10. Open / deferred

  • AES-GCM availability on VivoKey Apex. Needs verification before J3R180 → Apex port. If absent, the CTR+GMAC fallback path activates; that fallback is listed in §5 crypto primitives but not yet prototyped.
  • Exact PersonalizationApplet wire format. Intentionally deferred; it's a private interface between our harness and our applet, not spec-driven, so the format gets designed during implementation with no external constraints.
  • CI. No CI in v1; everything runs on the developer workstation. Revisit once the JUnit suite is substantial enough to want on every push.
  • Expedited-fast Kpersistent expiration policy. Spec leaves this largely to the User Device. Default: LRU eviction at the 16-slot cap, no time-based expiry. Revisit if deployments need one.

11. Glossary (for quick reference)

  • User Device — the thing being tapped (our Java Card applet).
  • Reader — the thing tapping (our test fixture).
  • Access Credential — the keypair unique to the User Device.
  • Access Document — the CBOR/COSE blob signed by the Credential Issuer, carrying the Access Credential public key and any access rules.
  • IssuerAuth — COSE_Sign1 structure inside the Access Document (spec §7).
  • Kpersistent — symmetric key shared with a specific Reader group, used for the Expedited-fast cryptogram path.
  • reader_group_identifier — 16-byte value identifying a set of Readers that share a trust context.