Skip to content

chore: release - #988

Merged
stormer78 merged 1 commit into
mainfrom
release-plz-2026-08-16T10-54-51Z
Aug 16, 2026
Merged

chore: release#988
stormer78 merged 1 commit into
mainfrom
release-plz-2026-08-16T10-54-51Z

Conversation

@stormer78

@stormer78 stormer78 commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

🤖 New release

  • vti-common: 0.11.41 -> 0.12.0 (⚠ API breaking changes)
  • vta-vault: 0.1.4 -> 0.2.0
  • vta-service: 0.15.4 -> 0.16.0 (⚠ API breaking changes)
  • vta-cli-common: 0.10.32 -> 0.10.33
  • vta-audit: 0.1.4 -> 0.1.5
  • vti-secrets: 0.1.12 -> 0.1.13
  • vta-config: 0.3.6 -> 0.3.7
  • vta-keyspaces: 0.1.2 -> 0.1.3
  • vta-keys: 0.2.3 -> 0.2.4
  • vta-support: 0.2.5 -> 0.2.6
  • vta-backup: 0.1.8 -> 0.1.9
  • vta-policy: 0.2.5 -> 0.2.6
  • vta-sweepers: 0.1.1 -> 0.1.2
  • vta-tee: 0.1.7 -> 0.1.8
  • vta-webvh: 0.1.7 -> 0.1.8

vti-common breaking changes

--- failure constructible_struct_adds_field: externally-constructible struct adds field ---

Description:
A pub struct constructible with a struct literal has a new pub field. Existing struct literals must be updated to include the new field.
        ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.48.0/src/lints/constructible_struct_adds_field.ron

Failed in:
  field VaultConfig.mdoc_iaca_trust_anchors in /tmp/.tmp20KozW/verifiable-trust-infrastructure/vti-common/src/config.rs:178

vta-service breaking changes

--- failure constructible_struct_adds_field: externally-constructible struct adds field ---

Description:
A pub struct constructible with a struct literal has a new pub field. Existing struct literals must be updated to include the new field.
        ref: https://doc.rust-lang.org/reference/expressions/struct-expr.html
       impl: https://github.com/obi1kenobi/cargo-semver-checks/tree/v0.48.0/src/lints/constructible_struct_adds_field.ron

Failed in:
  field AppState.mdoc_trust in /tmp/.tmp20KozW/verifiable-trust-infrastructure/vta-service/src/server.rs:118
Changelog

vti-common

0.12.0 — 2026-08-16

Added

  • vta-vault: Resolve mdoc issuers against configured IACA trust anchors (#987)

Answers the one question mdoc asks differently from every other credential
format here: which key do I trust to have issued this?

Every other format names its issuer as a DID, so receiving resolves that DID
and verifies against whatever comes back — the VTA holds no list and there is
no operator step. An mdoc has no issuer DID. It carries an X.509 chain in the
issuerAuth COSE unprotected header (x5chain, label 33): a Document Signer
certificate issued by an IACA. Verifying it means the VTA must already hold the
roots it accepts, which is a trust store, not a lookup.

The decision taken is a configured set of IACA root certificates — how
production EUDI verifiers work, and what Member State trusted lists (ETSI TS
119 612) distribute. It keeps X.509 at the boundary: nothing below this module
learns certificates exist, and receive_mdoc still takes a plain resolved key.

Validation is scoped to what ISO 18013-5 Annex B actually specifies — a
two-level IACA to Document Signer hierarchy, so no general RFC 5280 path
building. Checks the leaf issuer DN against a configured anchor subject, the
leaf signature against that anchor key, the leaf validity window, that the
anchor is a CA (a DS certificate configured by mistake cannot become a root),
and keyUsage.digitalSignature where present.

Deliberately not checked, both documented in the module: revocation (CRL/OCSP
needs egress and an unavailability policy — its own decision), and the ISO mDL
EKU 1.0.18013.5.1.2, which the EUDI PID profile does not share, so enforcing it
would reject valid PID credentials as what looks exactly like a trust failure.

Fails closed. An empty anchor set is an error, not permissive — mdoc is the one
format whose issuer is not a resolvable DID, so there is no safe default. The
config field defaults to empty, so an existing config still loads and an upgrade
neither breaks a deployment nor silently starts trusting mdocs.

Anchors are inline PEM in [vault] rather than file paths: an enclave has no
convenient filesystem, and inline values are covered by the effective-config
digest boot attestation commits to, so a verifier can see which issuers a TEE
VTA was trusting when it was attested.

x509-parser takes the verify-aws feature rather than the default verify, which
pulls ring — ring currently only reaches this workspace through a
dev-dependency, while aws-lc-rs is already a real dependency. Same crypto, no
new production tree.

vta-vault

0.2.0 — 2026-08-16

Added

  • vta-vault: Bind an mdoc to the VTA key that can present it (#990)

An mdoc's holder binding is a key, not a DID: the MSO carries a deviceKey, and
only its private half can sign DeviceAuth. Nothing in the stored envelope said
which VTA key that was, so a received mdoc could be stored and then turn out to
be unpresentable — with the failure surfacing much later, at presentation, and
nothing pointing at the cause.

Receive now resolves that binding and refuses the credential if this VTA does
not hold the key. Storing a credential you can never present is a trap, and the
right moment to find out is the moment it arrives.

mdoc_device_key_sec1 extracts the MSO deviceKey as a compressed SEC1 point —
the same encoding the VTA stores its own P-256 public keys in — so the caller
can compare without re-deriving either side. Extraction lives in vta-vault
because it reads mdoc internals; the matching lives in vta-service because that
is the layer that can see the keyspace. vta-vault does not depend on vta-keys,
and this keeps it that way.

find_key_by_public_multibase is a linear scan: the keyspace is indexed by key
id, not by public key, and a reverse index for one receive-path caller is not
worth the write amplification on every mint. It takes no AuthClaims because it
answers a factual question, not an authorization one — the caller gates on the
returned record's context_id, because binding a credential to a key in a
context the caller cannot act in would be a cross-tenant escape.

  • vta-vault: Resolve mdoc issuers against configured IACA trust anchors (#987)

Answers the one question mdoc asks differently from every other credential
format here: which key do I trust to have issued this?

Every other format names its issuer as a DID, so receiving resolves that DID
and verifies against whatever comes back — the VTA holds no list and there is
no operator step. An mdoc has no issuer DID. It carries an X.509 chain in the
issuerAuth COSE unprotected header (x5chain, label 33): a Document Signer
certificate issued by an IACA. Verifying it means the VTA must already hold the
roots it accepts, which is a trust store, not a lookup.

The decision taken is a configured set of IACA root certificates — how
production EUDI verifiers work, and what Member State trusted lists (ETSI TS
119 612) distribute. It keeps X.509 at the boundary: nothing below this module
learns certificates exist, and receive_mdoc still takes a plain resolved key.

Validation is scoped to what ISO 18013-5 Annex B actually specifies — a
two-level IACA to Document Signer hierarchy, so no general RFC 5280 path
building. Checks the leaf issuer DN against a configured anchor subject, the
leaf signature against that anchor key, the leaf validity window, that the
anchor is a CA (a DS certificate configured by mistake cannot become a root),
and keyUsage.digitalSignature where present.

Deliberately not checked, both documented in the module: revocation (CRL/OCSP
needs egress and an unavailability policy — its own decision), and the ISO mDL
EKU 1.0.18013.5.1.2, which the EUDI PID profile does not share, so enforcing it
would reject valid PID credentials as what looks exactly like a trust failure.

Fails closed. An empty anchor set is an error, not permissive — mdoc is the one
format whose issuer is not a resolvable DID, so there is no safe default. The
config field defaults to empty, so an existing config still loads and an upgrade
neither breaks a deployment nor silently starts trusting mdocs.

Anchors are inline PEM in [vault] rather than file paths: an enclave has no
convenient filesystem, and inline values are covered by the effective-config
digest boot attestation commits to, so a verifier can see which issuers a TEE
VTA was trusting when it was attested.

x509-parser takes the verify-aws feature rather than the default verify, which
pulls ring — ring currently only reaches this workspace through a
dev-dependency, while aws-lc-rs is already a real dependency. Same crypto, no
new production tree.

vta-service

0.16.0 — 2026-08-16

Added

  • vta-vault: Bind an mdoc to the VTA key that can present it (#990)

An mdoc's holder binding is a key, not a DID: the MSO carries a deviceKey, and
only its private half can sign DeviceAuth. Nothing in the stored envelope said
which VTA key that was, so a received mdoc could be stored and then turn out to
be unpresentable — with the failure surfacing much later, at presentation, and
nothing pointing at the cause.

Receive now resolves that binding and refuses the credential if this VTA does
not hold the key. Storing a credential you can never present is a trap, and the
right moment to find out is the moment it arrives.

mdoc_device_key_sec1 extracts the MSO deviceKey as a compressed SEC1 point —
the same encoding the VTA stores its own P-256 public keys in — so the caller
can compare without re-deriving either side. Extraction lives in vta-vault
because it reads mdoc internals; the matching lives in vta-service because that
is the layer that can see the keyspace. vta-vault does not depend on vta-keys,
and this keeps it that way.

find_key_by_public_multibase is a linear scan: the keyspace is indexed by key
id, not by public key, and a reverse index for one receive-path caller is not
worth the write amplification on every mint. It takes no AuthClaims because it
answers a factual question, not an authorization one — the caller gates on the
returned record's context_id, because binding a credential to a key in a
context the caller cannot act in would be a cross-tenant escape.

  • vta-service: Accept ISO mdoc over the credential-receive Trust Task (#989)

Everything shipped for mdoc so far — the format identity (#984), receive-side
verification (#986) and the IACA trust anchors (#987) — was reachable only
through the library. handle_receive was hardcoded to the Data-Integrity path:
a JSON credential, an issuer DID resolved through the DID cache, no format
parameter. An mdoc could not arrive at the VTA at all. This connects it.

ReceiveBody gains an optional format tag and a credentialBase64 carrier, since
an mdoc is CBOR and cannot travel as JSON. Absent format still means
Data-Integrity, which is the shape every existing client sends, so a deployed
wallet is unaffected — pinned by a test that parses a pre-existing body and
asserts it still routes to the DI path. Exactly one of credential or
credentialBase64 must be present; both, or neither, is a malformedRequest.

The mdoc arm is where the two credential families genuinely diverge. A DI
credential names its issuer as a DID and the key is resolved through the cache;
an mdoc names its issuer as an X.509 Document Signer, so the credential is
decoded first to read its x5chain and the key comes from the configured IACA
anchors instead. That asymmetry is the whole reason the anchors exist.

AppState carries the parsed anchors, built once in build_app_state from
[vault] mdoc_iaca_trust_anchors. A malformed certificate fails the boot rather
than surfacing as a puzzling rejection on the first mdoc that arrives. Empty is
legal and means this VTA accepts no mdoc issuers; the resolver fails closed on
it, so wiring the wire surface does not by itself make any VTA start trusting
mdocs — an operator still has to configure anchors deliberately.

No schema change: vault/credentials/receive/0.1 is in UNSPECCED_DISPATCHED_URIS,
so there is no published payload schema to update and dispatch validation is a
no-op for it either way.

Note for a follow-up, not changed here: test_support.rs constructs an AppState
literal directly, so build_app_state is not in practice the single constructor
its doc comment claims. Adding a field has to be done in both places.


This PR was generated with release-plz.

@stormer78
stormer78 requested a review from a team as a code owner August 16, 2026 10:54
@stormer78
stormer78 force-pushed the release-plz-2026-08-16T10-54-51Z branch from af29967 to c475c4a Compare August 16, 2026 11:49
@stormer78
stormer78 force-pushed the release-plz-2026-08-16T10-54-51Z branch from c475c4a to e6d4785 Compare August 16, 2026 12:37
@stormer78
stormer78 merged commit c880213 into main Aug 16, 2026
15 checks passed
@stormer78
stormer78 deleted the release-plz-2026-08-16T10-54-51Z branch August 16, 2026 12:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant