feat(applet): StepUpApplet INS_EXCHANGE decrypt-and-discard with 9000 ack
X-CUBE-ALIRO firmware sends a Reader Status sub-event via EXCHANGE (CLA=0x80, INS=0xC9) after the step-up AID SELECT, per Aliro §8.3.3.5 Table 8-14. The payload is encrypted with StepUpSKReader using AES-256-GCM with IV = 0x0000000000000000 || stepup_reader_counter (4B BE) per §8.3.1.8. For Milestone 1 we decrypt-and-discard: tag verification proves we have matching session keys, then we return SW=9000 with empty payload. M2 will add a proper encrypted Reader Status response sub-event. Extends CryptoSingletons to hold the shared AliroGcm instance too -- opt 1 sibling of the AliroCrypto sharing from M1A.2. Adds AliroGcm.decrypt since the prior pipeline only encrypted (AUTH1 response path). Counter starts at 1 per session-bound init (spec §8.4.3 -> mdoc [6] §9.1.1.5), incremented after each successful decrypt. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -134,13 +134,12 @@ public class AliroApplet extends Applet {
|
||||
/** SHA-1 for key_slot = first 8 bytes of SHA-1(uncompressed credential_PubK). */
|
||||
private MessageDigest sha1;
|
||||
|
||||
/** Userland AES-256-GCM (built on AES-ECB-NOPAD + AES-256 key). The
|
||||
* target card (NXP J3R180) does not expose {@code AEADCipher.ALG_AES_GCM}
|
||||
* despite advertising JC 3.0.5, so we implement GCM in userland.
|
||||
* {@link AliroGcm} owns its own persistent {@link AESKey} and
|
||||
* {@link javacardx.crypto.Cipher} internally — this class no longer
|
||||
* needs a separate {@code expeditedSKDeviceKey} field. */
|
||||
private AliroGcm gcm;
|
||||
// Userland AES-256-GCM (built on AES-ECB-NOPAD + AES-256 key) is pulled
|
||||
// from CryptoSingletons.getAliroGcm(). The target card (NXP J3R180) does
|
||||
// not expose AEADCipher.ALG_AES_GCM despite advertising JC 3.0.5, so we
|
||||
// implement GCM in userland. Sharing with StepUpApplet via the singleton
|
||||
// saves ~370 B of transient/EEPROM footprint that a per-applet duplicate
|
||||
// would otherwise pay. See encryptResponseGcm().
|
||||
|
||||
// Low-level crypto primitives (ECDH, HKDF, Kdh, expedited key derivation)
|
||||
// are pulled from CryptoSingletons.getAliroCrypto() so both AliroApplet
|
||||
@@ -291,7 +290,10 @@ public class AliroApplet extends Applet {
|
||||
ecdsaSigner = Signature.getInstance(Signature.ALG_ECDSA_SHA_256, false);
|
||||
sha1 = MessageDigest.getInstance(MessageDigest.ALG_SHA, false);
|
||||
try {
|
||||
gcm = new AliroGcm();
|
||||
// Force lazy alloc of the shared AliroGcm so any install-time
|
||||
// failure (Cipher.getInstance / KeyBuilder.buildKey rejection)
|
||||
// surfaces with the same greppable diagnostic SW as before.
|
||||
CryptoSingletons.getAliroGcm();
|
||||
} catch (ISOException e) { throw e; // preserve inner diagnostic SW
|
||||
} catch (Throwable t) { ISOException.throwIt((short) 0x6FA8); }
|
||||
try {
|
||||
@@ -457,6 +459,7 @@ public class AliroApplet extends Applet {
|
||||
case INS_DIAG_GCM: {
|
||||
// Plaintext: 137 bytes anywhere in buf past the output region.
|
||||
// Contents don't affect timing.
|
||||
AliroGcm gcm = CryptoSingletons.getAliroGcm();
|
||||
for (short i = 0; i < n; i++) {
|
||||
gcm.encrypt(
|
||||
DIAG_KEY_32, (short) 0,
|
||||
@@ -964,7 +967,7 @@ public class AliroApplet extends Applet {
|
||||
// device_counter big-endian in the last 4 bytes; first AUTH1 = 1
|
||||
scratch[(short) (ivOff + 11)] = (byte) 0x01;
|
||||
|
||||
return gcm.encrypt(
|
||||
return CryptoSingletons.getAliroGcm().encrypt(
|
||||
derivedKeys, OFF_EXPEDITED_SK_DEVICE,
|
||||
scratch, ivOff,
|
||||
plaintext, ptOff, ptLen,
|
||||
|
||||
Reference in New Issue
Block a user