eIDAS 2.0 & EUDI Wallet Binding
How PasskeyBridge binds carrier-attestation signals to Verifiable Credential subjects for EUDI Wallet interoperability under eIDAS 2.0, without storing PII.
Overview
Regulation (EU) 2024/1183, commonly eIDAS 2.0, requires every Member State to offer a European Digital Identity (EUDI) Wallet, with relying parties in regulated sectors accepting wallet-presented credentials by 2027.
PasskeyBridge does not issue government Person Identification Data and is not a qualified trust service provider. What it ships is the binding layer: a way to record that a wallet-presented Verifiable Credential and a carrier-attestation signal describe the same subject, with no plaintext identifier stored on either side.
This guide documents that mechanism as built: the row it writes, how it is created, and what the hash chain does and does not prove. The credential machinery itself is in Verifiable Credentials, and the API for bindings created from raw identifiers is in Deterministic Cross-Reference Binding.
Binding model
Bindings live in shield_cross_references, one row per carrier-signal and VC-subject pair within a tenant. Row-level security scopes every read to tenant admins.
| Column | Meaning |
|---|---|
tenant_id | Owning tenant |
sim_signal_hash | Digest of the carrier attestation signal |
vc_subject_hash | Digest of the credential subject identifier |
hash_version | The weaker scheme present in the row: 1 when either digest arrived pre-computed, otherwise 2, or 3 when both were keyed as subjects |
match_score | Confidence from 0.0000 to 1.0000. Native binds through shield-vc-present write 1.0000 |
match_method | native_vc, exact, temporal, fuzzy, partial or invalid_timestamps |
vc_credential_type | Credential type recorded from the presentation |
vc_issuer | Issuer DID or URL |
entity_profile_hash | SHA-256 over the stored digests plus two descriptive fields, the composite lookup key |
passkey_credential_id | Optional passkey, null until one is bound |
verified_at and expires_at | Validity window. Default expiry is 90 days |
is_active | Soft-revocation flag. A partial unique index prevents two active rows for one pair |
pqc_signature and pqc_algorithm | Hybrid signature over the row, present when the tenant has PQC enabled |
No column holds a phone number, an email address, a DID document or a raw credential. Digests written by the platform are keyed HMAC-SHA-256 under a server-held pepper, so a leaked table discloses equality between rows and nothing else; a digest supplied by the caller is stored as given and is only as strong as the scheme that produced it, which is what hash_version records.
Establishing a binding
The wallet-facing path creates the binding as a side effect of verifying a presentation:
- The wallet, or its OpenID4VP intermediary, posts its
vp_tokentoshield-vc-presentwithaction: "verify", and the request includesbind_to_signal_hash: the platform's digest for a carrier signal already ingested for that subject. - The function verifies the presentation: signature, expiry, not-before, issuer DID resolution, SD-JWT disclosures, the KB-JWT when the credential carries
cnf.jwk, and any Presentation Exchange descriptors supplied. - On success it looks for an active row for the pair, matching the current pepper, the previous pepper during a rotation, and the legacy unkeyed digest. An existing row is reused and its id returned.
- Otherwise it inserts a row with
match_score1.0,match_methodnative_vc, andentity_profile_hashas the SHA-256 of<signal hash>:<subject hash>:native:VerifiableCredential. - A
vc_presentation_verifiedevent is written toshield_eventscarrying the cross-reference id.
A failed verification writes vc_presentation_failed and no binding, so the table cannot be poisoned by a presentation that did not check out.
One caveat worth carrying into a design review. bind_to_signal_hash is a digest the platform did not compute, so it is stored as supplied and the row is stamped hash_version: 1; use the platform's own digest for that identifier or the row will correlate with nothing. The signature check at step 2 covers the classical ES256 or ES384 JWS and, for a credential your tenant issued, the detached ML-DSA proof stored with it; the response says which of those actually ran.
The alternative path, shield-cross-reference with action: "establish", takes both identifiers raw, keys them server-side, stamps the weaker of the two schemes it used, and scores the match from the two event timestamps. It requires the Enterprise plan and a tenant admin session.
Level of assurance positioning
The eIDAS 2.0 Architecture Reference Framework defines three Levels of Assurance: Low, Substantial and High. PasskeyBridge does not certify an LoA. It produces the artefacts a relying party uses to justify its own determination.
- Carrier signal alone evidences control of a mobile subscription. It supports a Substantial claim and does not by itself reach High.
- Carrier signal bound to a wallet-presented credential adds a second, independent factor and the chain-of-custody artefact: a cross-reference row recording that both were seen for one subject, with a timestamp and a match score.
- Both, plus a passkey attached through
bind_passkeyrecords the device-bound authenticator alongside the other two. The association is a record; acting on it, such as invalidating the passkey when a carrier signal fires, is work your own systems perform.
The LoA assertion remains the relying party's, and so does the obligation to show how it was reached. What this platform contributes is a row that can be re-derived from its own contents and an audit trail of when it was written.
Cryptographic binding
Two different mechanisms are at work, and it is worth keeping them apart.
The identifiers are keyed with HMAC-SHA-256 under a server-held pepper (IDENTIFIER_HASH_PEPPER, with IDENTIFIER_HASH_PEPPER_PREV during a rotation). The pepper is platform-held and lives in the secret store, never in the database: tenants do not supply or control it, and there is no per-tenant salt. Keying is what makes the digests non-enumerable, since a plain SHA-256 of a phone number can be reversed from a precomputed table. A missing pepper fails the write rather than falling back to an unkeyed digest.
The credentials are signed with the tenant's ECDSA key, ES256 on P-256 or ES384 on P-384, plus a detached ML-DSA-65 or ML-DSA-87 signature under NIST FIPS 204. Both public keys are published in the tenant's DID document, so a verifier can check either layer without holding a PasskeyBridge secret.
The HMAC-based hybrid envelope appears in two places only: legacy PQC-HYBRID credentials issued before the standards-based rollout or by tenants who opted into it, and the signature stored on a cross-reference row, which the platform verifies for itself. Neither is what a wallet or an external verifier checks on a current credential.
entity_profile_hash is a plain SHA-256 over values already stored, which keeps it recomputable by anyone holding the row. See Security & Cryptography for key management and rotation, and the blog article The EU Digital Identity Wallet and Carrier Signals for the sector timeline.
Data residency and Schrems III
No plaintext identifier reaches the cross-reference store, so the table holds no directly identifying data to transfer. Matching across pillars uses digests alone, and the inverse lookup requires both the original identifier and the server-held pepper.
Two qualifications belong in any assessment you write. Keyed digests of personal identifiers are still personal data under GDPR while the pepper exists, since the controller can re-derive them; the keying bounds who can reverse them, and does not remove the rows from scope. And the pepper is held by the platform, so a deployment pattern that depends on the tenant holding the key material is not available today.
The DSAR pipeline works off the same digests. shield-dsar takes a single keyed user_hash, a 64-character hex digest, and matches it against both vc_subject_hash and sim_signal_hash on cross-references, as well as the event tables, for access and erasure requests. One digest covers both pillars because both columns hold digests of the same keyed scheme. The workflow is in DSAR Workflows.
Not yet shipped
To set expectations precisely, PasskeyBridge does not issue PID credentials, does not operate as a Qualified Trust Service Provider under Article 24, and does not create Qualified Electronic Signatures. Those are sovereign functions reserved to designated national authorities and accredited providers.
Two further gaps are implementation rather than regulation. Revocation does not cascade: a carrier hard signal will not revoke a credential, a cross-reference or a passkey on its own, and any such flow is code you write against shield-vc-status and shield-cross-reference. And the ARF's wallet-side conformance profiles are not implemented; what exists is OpenID4VP presentation verification and OID4VCI issuance against the generic specifications.
What is shipped is the binding layer: the shield_cross_references table, the bind_to_signal_hash parameter on shield-vc-present, the entity_profile_hash composite key, standards-based credential signing with a published post-quantum key, and the zero-PII DSAR pipeline. That is enough to integrate a wallet flow ahead of the deadline without rebuilding the identity stack each time the Implementing Acts iterate.
Related from the blog
- The IDV Margin Compression: Why Per-Verification Pricing Is Collapsing Toward Zero by 2027intel · 16 min read
- The Missing Link in Identity Assurance: Cross-Referencing SIM Signals with Verifiable Credentialssecurity · 15 min read
- The Identity Stack Bifurcation: Consumer Wallets vs Enterprise Attestation Layersintel · 20 min read