docs: Step-Up implementation plans v1+v2 + inline TODOs at four opt sites

Two plans land:

- 2026-06-07-step-up-implementation.md (v1): original 6-phase plan
  written before the Path X session. Captured the four optimizations
  (opt 1: reuse AliroGcm; opt 2: structural CBOR only; opt 3:
  stream-encrypt during ENVELOPE emit; opt 4: cache IssuerAuth verify
  at personalization) and the spec citations for each. Superseded by v2
  but retained for the planning-history record.

- 2026-06-11-step-up-implementation-v2.md: revised after Path X
  resolved the three crypto bugs. Splits into Milestone 1 (~400 LOC,
  4-6 hours, gets DOOR OPERATION SUCCEEDED on stock X-CUBE-ALIRO with
  decrypt-and-discard stubs) and Milestone 2 (~1500 LOC, 1-2 weeks,
  real Access Document retrieval via mdoc DeviceResponse + CBOR + COSE).
  The four optimizations are preserved into M2 where they matter; M1
  ships without them since the demo doesn't need real document
  payload yet.

Inline TODO comments mark the four optimization sites in source:

- CredentialStore.finalizeAccessDocument -- opt 4a (verify-flag in a
  JCSystem.beginTransaction block) + opt 4b (Credential Issuer key
  rotation assumption documented).

- StepUpApplet class javadoc -- all four optimizations laid out so the
  implementer (subagent or human) lands them as they build the body.

(The AliroApplet INS_EXCHANGE comment that references the plans is
in the previous fix commit; it'll move/retire when M1 lands.)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
michael
2026-06-11 10:19:05 -07:00
parent f94e416c99
commit 9189b41e7f
4 changed files with 918 additions and 0 deletions

View File

@@ -176,6 +176,18 @@ final class CredentialStore {
* Marks the Access Document as provisioned with {@code totalLen} bytes
* of valid content starting at offset 0. Returns false (and does not
* mutate state) if {@code totalLen} is outside {@code [0, ACCESS_DOC_MAX_LEN]}.
*
* <p>TODO (Step-Up impl, opt 4a): wrap this in
* {@code JCSystem.beginTransaction()} along with a one-shot IssuerAuth
* COSE_Sign1 verify against the stored Credential Issuer public key, and
* set a persistent {@code accessDocumentVerified} flag. Caching the
* verify result saves ~100 ms per Step-Up transaction at the cost of one
* extra persistent byte + the assumption that the Credential Issuer
* trust anchor is fixed for the card's lifetime (opt 4b — true for DT's
* implantable use case but document the limitation). The verify-flag
* write and the {@code accessDocumentFinalized} flip MUST land in the
* same atomic transaction so partial state can't ship an unverified
* document marked verified.
*/
boolean finalizeAccessDocument(short totalLen) {
if (totalLen < 0 || totalLen > ACCESS_DOC_MAX_LEN) {

View File

@@ -23,6 +23,40 @@ import javacard.framework.Util;
* {@link AliroApplet}'s derivation in §8.3.1.13). Since Java Card
* installs distinct instances per AID, that state will be exchanged via
* a shared package-private holder (not yet implemented).
*
* <p><b>Implementation notes for the Step-Up milestone (see
* {@code docs/plans/2026-06-07-step-up-implementation.md}):</b>
* <ol>
* <li><b>Opt 1 — Reuse AliroGcm.</b> Don't allocate a second
* {@link AliroGcm} for Step-Up; reuse {@link AliroApplet}'s instance
* via {@code aesKey.setKey(stepUpSKDevice)} between phases. Saves
* ~368 B transient. ExpeditedSK and StepUpSK are distinct so the
* key/IV uniqueness invariant holds across rekey.</li>
* <li><b>Opt 2 — Structural CBOR only.</b> The Access Document is opaque
* transport bytes to us; the reader does field-level parsing. We only
* need a structural CBOR codec (major type + length boundaries) for
* the SessionData wrapper and to extract {@code IssuerAuth} for
* verification. Do not implement field-level mdoc parsing — wastes
* bytecode and RAM. CBOR encoding MUST be deterministic per RFC 8949
* §4.2.1 anywhere we emit.</li>
* <li><b>Opt 3 — Stream-encrypt during ENVELOPE emit.</b> Don't buffer
* 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.</li>
* <li><b>Opt 4 — IssuerAuth verify cached at personalization.</b> See
* {@link CredentialStore#finalizeAccessDocument(short)} TODO — the
* COSE_Sign1 verify happens 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.</li>
* </ol>
*
* <p>Also when this lands, revisit {@link AliroApplet}'s {@code INS_EXCHANGE}
* stub: with real Step-Up at this AID, the stub becomes either (a) unused
* if {@code signaling_bitmap} bit 2 stays set and spec-conformant readers
* route EXCHANGE here, or (b) replaceable with a proper "Reader Status
* sub-event" reply if X-CUBE-ALIRO's vendor bug persists.
*/
public class StepUpApplet extends Applet {