feat(applet): accessDocumentVerified flag in CredentialStore (M2A.1)

Persistent boolean flag for the cached IssuerAuth verify result. Field
sits beside accessDocumentFinalized in writeTo/readFrom (FIELD_VERSION
bumped 1->2 -- schema change requires re-personalize). reset() clears.
Test pins the serialize/deserialize round-trip via the existing
RecordingSink/ReplaySource helpers.

Foundation for M2A.2 (CoseVerifier) and M2A.3 (verify-at-finalize wiring).
This commit is contained in:
michael
2026-06-17 13:57:51 -07:00
parent 4741ce7ba3
commit 531b1d3186
2 changed files with 28 additions and 2 deletions

View File

@@ -36,7 +36,7 @@ final class CredentialStore {
/** Stable field order for AMD-H Element serialization. Append-only — /** Stable field order for AMD-H Element serialization. Append-only —
* never reorder or remove without bumping the package version and * never reorder or remove without bumping the package version and
* writing an explicit migration step in the new ELF's onRestore. */ * writing an explicit migration step in the new ELF's onRestore. */
static final byte FIELD_VERSION = 1; static final byte FIELD_VERSION = 2;
/** Publish-point read by AliroApplet/StepUpApplet via {@link #get()}. /** Publish-point read by AliroApplet/StepUpApplet via {@link #get()}.
* PersonalizationApplet owns the actual instance; this is just an alias * PersonalizationApplet owns the actual instance; this is just an alias
@@ -57,6 +57,7 @@ final class CredentialStore {
private final byte[] accessDocument; private final byte[] accessDocument;
private short accessDocumentLen; private short accessDocumentLen;
private boolean accessDocumentFinalized; private boolean accessDocumentFinalized;
private boolean accessDocumentVerified;
private boolean committed; private boolean committed;
@@ -202,6 +203,14 @@ final class CredentialStore {
return accessDocumentFinalized; return accessDocumentFinalized;
} }
void markAccessDocumentVerified() {
accessDocumentVerified = true;
}
boolean isAccessDocumentVerified() {
return accessDocumentVerified;
}
short getAccessDocumentLen() { short getAccessDocumentLen() {
return accessDocumentLen; return accessDocumentLen;
} }
@@ -223,6 +232,7 @@ final class CredentialStore {
sink.write(credentialPubKeySet); sink.write(credentialPubKeySet);
sink.write(readerPubKeySet); sink.write(readerPubKeySet);
sink.write(accessDocumentFinalized); sink.write(accessDocumentFinalized);
sink.write(accessDocumentVerified);
sink.write(accessDocumentLen); sink.write(accessDocumentLen);
sink.write(credentialPrivKey); sink.write(credentialPrivKey);
sink.write(credentialPubKey); sink.write(credentialPubKey);
@@ -246,6 +256,7 @@ final class CredentialStore {
s.credentialPubKeySet = src.readBoolean(); s.credentialPubKeySet = src.readBoolean();
s.readerPubKeySet = src.readBoolean(); s.readerPubKeySet = src.readBoolean();
s.accessDocumentFinalized = src.readBoolean(); s.accessDocumentFinalized = src.readBoolean();
s.accessDocumentVerified = src.readBoolean();
s.accessDocumentLen = src.readShort(); s.accessDocumentLen = src.readShort();
byte[] a; byte[] a;
a = src.readByteArray(); a = src.readByteArray();
@@ -307,6 +318,7 @@ final class CredentialStore {
readerPubKeySet = false; readerPubKeySet = false;
accessDocumentLen = 0; accessDocumentLen = 0;
accessDocumentFinalized = false; accessDocumentFinalized = false;
accessDocumentVerified = false;
committed = false; committed = false;
} }
} }

View File

@@ -81,6 +81,20 @@ class CredentialStoreSerializationTest {
assertFalse(restored.hasAccessDocument()); assertFalse(restored.hasAccessDocument());
} }
@Test
void accessDocumentVerifiedRoundTripsThroughSerialize() {
CredentialStore s1 = CredentialStore.bootstrap();
s1.resetForTesting();
s1.markAccessDocumentVerified();
RecordingSink buf = new RecordingSink();
s1.writeTo(buf);
CredentialStore s2 = CredentialStore.readFrom(buf.toSource());
assertTrue(s2.isAccessDocumentVerified(),
"verified flag must survive serialize/deserialize round-trip");
}
/** /**
* Forges a payload with a bogus FIELD_VERSION byte and asserts that * Forges a payload with a bogus FIELD_VERSION byte and asserts that
* {@link CredentialStore#readFrom} rejects it with * {@link CredentialStore#readFrom} rejects it with
@@ -91,7 +105,7 @@ class CredentialStoreSerializationTest {
@Test @Test
void readFromRejectsWrongFieldVersion() { void readFromRejectsWrongFieldVersion() {
RecordingSink buf = new RecordingSink(); RecordingSink buf = new RecordingSink();
buf.write((byte) 0x7F); // != FIELD_VERSION (== 1) buf.write((byte) 0x7F); // != FIELD_VERSION
ISOException ex = assertThrows(ISOException.class, ISOException ex = assertThrows(ISOException.class,
() -> CredentialStore.readFrom(buf.toSource())); () -> CredentialStore.readFrom(buf.toSource()));