feat(applet): StepUpApplet derives session keys on SELECT when armed
After AUTH1 parks StepUpSK in SessionContext, the reader issues SELECT to the Step-Up AID per spec §10.2. StepUpApplet.select() now picks up the parked SK, derives StepUpSKDevice / StepUpSKReader via the §8.4.3 HKDF (already implemented in AliroCrypto.deriveStepUpSessionKeys), and stages both in transient CLEAR_ON_DESELECT fields for the ENVELOPE (M1C.1) and EXCHANGE (M1B.1) handlers. Introduces CryptoSingletons -- a lazy package-private holder for the single AliroCrypto instance shared between AliroApplet and StepUpApplet. Saves ~352 B of transient (kdfWorkbuf + hkdfPrevT + expandScratch) versus a per-applet duplicate. Java Card forbids new in <clinit> so the singleton uses lazy null-check init. Opt 1 prelude per docs/plans/2026-06-11-step-up-implementation-v2.md. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -142,8 +142,10 @@ public class AliroApplet extends Applet {
|
||||
* needs a separate {@code expeditedSKDeviceKey} field. */
|
||||
private AliroGcm gcm;
|
||||
|
||||
/** Low-level crypto primitives (ECDH, HKDF, Kdh, expedited key derivation). */
|
||||
private AliroCrypto crypto;
|
||||
// Low-level crypto primitives (ECDH, HKDF, Kdh, expedited key derivation)
|
||||
// are pulled from CryptoSingletons.getAliroCrypto() so both AliroApplet
|
||||
// and StepUpApplet share one instance — saves ~352 B transient versus a
|
||||
// per-applet duplicate. Local refs in deriveSessionKeys / processDiag.
|
||||
|
||||
/** Persistent (1B EEPROM) — set after {@link #ensureCryptoInitialized()} runs. */
|
||||
private byte cryptoInitialized;
|
||||
@@ -293,7 +295,9 @@ public class AliroApplet extends Applet {
|
||||
} catch (ISOException e) { throw e; // preserve inner diagnostic SW
|
||||
} catch (Throwable t) { ISOException.throwIt((short) 0x6FA8); }
|
||||
try {
|
||||
crypto = new AliroCrypto();
|
||||
// Force lazy alloc of the shared AliroCrypto so the same install-
|
||||
// time failure ladder applies if the constructor throws.
|
||||
CryptoSingletons.getAliroCrypto();
|
||||
} catch (ISOException e) { throw e; // preserve inner diagnostic SW (0x6FC1-C5)
|
||||
} catch (Throwable t) { ISOException.throwIt((short) 0x6FA6); }
|
||||
// Seed P-256 curve params on every keypair before any genKeyPair / setS /
|
||||
@@ -416,8 +420,9 @@ public class AliroApplet extends Applet {
|
||||
|
||||
switch (ins) {
|
||||
case INS_DIAG_HMAC: {
|
||||
AliroCrypto cryptoHmac = CryptoSingletons.getAliroCrypto();
|
||||
for (short i = 0; i < n; i++) {
|
||||
crypto.diagHmac(
|
||||
cryptoHmac.diagHmac(
|
||||
DIAG_KEY_32, (short) 0, (short) DIAG_KEY_32.length,
|
||||
DIAG_MSG_64, (short) 0, (short) DIAG_MSG_64.length,
|
||||
buf, DIAG_OUT_OFF);
|
||||
@@ -427,8 +432,9 @@ public class AliroApplet extends Applet {
|
||||
case INS_DIAG_ECDH: {
|
||||
javacard.security.ECPrivateKey priv =
|
||||
(javacard.security.ECPrivateKey) diagKeyPair.getPrivate();
|
||||
AliroCrypto cryptoEcdh = CryptoSingletons.getAliroCrypto();
|
||||
for (short i = 0; i < n; i++) {
|
||||
crypto.computeEcdhSharedX(priv,
|
||||
cryptoEcdh.computeEcdhSharedX(priv,
|
||||
SECP256R1_G, (short) 0,
|
||||
buf, DIAG_OUT_OFF);
|
||||
}
|
||||
@@ -652,6 +658,7 @@ public class AliroApplet extends Applet {
|
||||
short saltLen = buildSaltVolatile(store, saltVolatile, (short) 0);
|
||||
short infoLen = buildInfo(scratch, SCRATCH_READER_PUB_UNCOMP_OFF);
|
||||
|
||||
AliroCrypto crypto = CryptoSingletons.getAliroCrypto();
|
||||
crypto.deriveKdh(
|
||||
(ECPrivateKey) credentialEphemeralKeyPair.getPrivate(),
|
||||
sessionState, OFF_READER_EPUBK,
|
||||
|
||||
Reference in New Issue
Block a user