fix(applet): signaling_bitmap reflects actual AD capability (M2F.1)
Replaces the hardcoded 0x0005 in Table 8-11 with a computed value: - Bit 2 (step-up AID SELECT required) is always set — our architecture always uses split AID (5501 expedited + 5502 step-up) so the channel is always advertised, even when no AD is present. - Bit 0 (AD retrievable) is set only when hasAccessDocument() AND isAccessDocumentVerified() — we don't advertise a capability we can't deliver. The "finalized but not verified" branch is defensive (post-M2A.3 finalize requires verify) but matches the contract. Tests in AliroAppletAuth1Test: - auth1_bitmap_adNotProvisioned_returns0x0004 (replaces the old "all-zero" expectation — bit 2 is now always on) - auth1_bitmap_adProvisionedButNotVerified_returns0x0004 (new; uses markAccessDocumentFinalizedForTesting without markAccessDocumentVerified) - auth1_bitmap_adProvisionedAndVerified_returns0x0005 (the previous M2A.3 case, now correctly gated on the verified flag) Tests run: 147, Failures: 0, Errors: 3 (pre-existing GCM only).
This commit is contained in:
@@ -162,8 +162,11 @@ class AliroAppletAuth1Test {
|
||||
}
|
||||
|
||||
@Test
|
||||
void auth1SignalingBitmapIsAllZeroWhenNoAccessDocProvisioned() throws Exception {
|
||||
// Default setUp provisions keys but no Access Document.
|
||||
void auth1_bitmap_adNotProvisioned_returns0x0004() throws Exception {
|
||||
// Default setUp provisions keys but no Access Document. Bit 2
|
||||
// (step-up AID SELECT required) is still set — the split-AID step-up
|
||||
// architecture is always advertised, even when there's nothing
|
||||
// behind it.
|
||||
sendStandardAuth0();
|
||||
ResponseAPDU r = sendValidAuth1();
|
||||
assertEquals(0x9000, r.getSW());
|
||||
@@ -172,24 +175,47 @@ class AliroAppletAuth1Test {
|
||||
reader.deriveExpeditedSKDevice(credentialEphemPubKey), r.getData());
|
||||
byte[] bitmap = TlvUtil.findTopLevel(pt, 0x5E);
|
||||
org.junit.jupiter.api.Assertions.assertArrayEquals(
|
||||
new byte[] { 0x00, 0x00 }, bitmap,
|
||||
"no Access Document → signaling_bitmap is 0x0000");
|
||||
new byte[] { 0x00, 0x04 }, bitmap,
|
||||
"no Access Document → bit 2 only (0x0004); bit 0 reflects actual retrievability");
|
||||
}
|
||||
|
||||
@Test
|
||||
void auth1SignalingBitmapHasAccessDocBitsWhenAdProvisioned() throws Exception {
|
||||
// Provision a non-empty Access Document via CredentialStore (bypasses
|
||||
// the PersonalizationApplet INS layer — that flow is tested
|
||||
// separately). Content is opaque here; we only care that the
|
||||
// signaling_bitmap reflects presence.
|
||||
void auth1_bitmap_adProvisionedButNotVerified_returns0x0004() throws Exception {
|
||||
// Provision opaque AD bytes and mark them finalized, but NOT verified.
|
||||
// Post-M2A.3 the finalize flow requires IssuerAuth verify so this is a
|
||||
// defensive case — bit 0 (AD retrievable) must NOT be advertised since
|
||||
// we wouldn't actually serve unverified AD on the step-up path.
|
||||
byte[] ad = new byte[120];
|
||||
for (int i = 0; i < ad.length; i++) ad[i] = (byte) (i ^ 0x5A);
|
||||
org.junit.jupiter.api.Assertions.assertTrue(
|
||||
CredentialStore.get().writeAccessDocumentChunk(ad, (short) 0, (short) 0, (short) ad.length));
|
||||
// Bypass IssuerAuth verify — opaque random bytes here, not a real
|
||||
// COSE_Sign1. The signaling bitmap path only cares about the
|
||||
// finalized flag, not the verified flag.
|
||||
CredentialStore.get().markAccessDocumentFinalizedForTesting((short) ad.length);
|
||||
// Deliberately do NOT mark verified.
|
||||
|
||||
sendStandardAuth0();
|
||||
ResponseAPDU r = sendValidAuth1();
|
||||
assertEquals(0x9000, r.getSW());
|
||||
|
||||
byte[] pt = ReaderSide.decryptAuth1Response(
|
||||
reader.deriveExpeditedSKDevice(credentialEphemPubKey), r.getData());
|
||||
byte[] bitmap = TlvUtil.findTopLevel(pt, 0x5E);
|
||||
org.junit.jupiter.api.Assertions.assertArrayEquals(
|
||||
new byte[] { 0x00, 0x04 }, bitmap,
|
||||
"AD finalized but not verified → bit 2 only (0x0004); bit 0 requires verified flag");
|
||||
}
|
||||
|
||||
@Test
|
||||
void auth1_bitmap_adProvisionedAndVerified_returns0x0005() throws Exception {
|
||||
// Provision a non-empty Access Document via CredentialStore (bypasses
|
||||
// the PersonalizationApplet INS layer — that flow is tested
|
||||
// separately). Mark both finalized AND verified to mirror the
|
||||
// post-M2A.3 happy path.
|
||||
byte[] ad = new byte[120];
|
||||
for (int i = 0; i < ad.length; i++) ad[i] = (byte) (i ^ 0x5A);
|
||||
org.junit.jupiter.api.Assertions.assertTrue(
|
||||
CredentialStore.get().writeAccessDocumentChunk(ad, (short) 0, (short) 0, (short) ad.length));
|
||||
CredentialStore.get().markAccessDocumentFinalizedForTesting((short) ad.length);
|
||||
CredentialStore.get().markAccessDocumentVerified();
|
||||
|
||||
sendStandardAuth0();
|
||||
ResponseAPDU r = sendValidAuth1();
|
||||
@@ -202,7 +228,7 @@ class AliroAppletAuth1Test {
|
||||
// = 2^0 + 2^2 = 0x0005 (big-endian)
|
||||
org.junit.jupiter.api.Assertions.assertArrayEquals(
|
||||
new byte[] { 0x00, 0x05 }, bitmap,
|
||||
"Access Document provisioned on NFC → bitmap bits 0 and 2 set (0x0005)");
|
||||
"AD finalized AND verified → bits 0 + 2 set (0x0005)");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user