Skip to main content

Wallet Unit Attestation (WUA) explained

A Wallet Unit Attestation, WUA, is what an EUDI Wallet shows a PID Provider or Attestation Provider during issuance to prove it is genuine. It is not one document but two, both signed by the Wallet Provider: a Wallet Instance Attestation (WIA) about the wallet app, and a Key Attestation (KA) about the secure storage of the keys a credential will be bound to.

The problem a WUA solves

An issuer about to hand a PID or a company registration credential to a wallet has no built-in way to tell a certified EUDI Wallet from a modified app or a script replaying an authorization flow. Nor can it see whether the key the credential will be bound to lives in certified secure hardware, or in ordinary storage it could be copied from.

The WUA closes both gaps. The Wallet Provider vouches for the app through the WIA and for the key storage through the Key Attestation. The issuer checks both before issuing, and keeps checking their revocation status afterwards, so it can revoke what it issued if the wallet or its key storage is later found to be compromised.

A WUA is used only during issuance. The ARF forbids a Wallet Unit from presenting a WIA or Key Attestation to a Relying Party, which keeps wallet details out of every presentation.

The actors and who trusts whom

Five roles are involved, and only four of them ever handle a WUA. The WSCD (Wallet Secure Cryptographic Device) or a keystore generates and guards the keys, the Wallet Provider vouches for the app and the keys, and the issuer relies on that instead of assessing the wallet itself.

Issuers trust a WUA because the Wallet Provider’s signing certificate, sent in the x5c header, chains to a trust anchor on the Trusted List for Wallet Providers. The Wallet Solution itself is certified by a conformity assessment body, and the WIA carries that certification information.

Two attestations under one name

TS3, the EUDI technical specification for WUAs, splits the attestation in two because the app and the key storage are different things, checked at different endpoints and revoked for different reasons.

Wallet Instance Attestation (WIA)Key Attestation (KA)
AttestsThe integrity of the Wallet Instance, meaning the appThat one or more keys were generated in and are held by a named WSCD or keystore, and how well that storage resists attack
Sent toThe Authorization Server, in the Pushed Authorization Request and the Token RequestThe Credential Issuer, inside the proofs of the Credential Request
LifetimeLess than 24 hoursChosen by the Wallet Provider, and may be longer than a WIA
Revocationclient_status: the revocation state of this Wallet Instancekey_storage_status: the revocation state of the WSCD or keystore
Needed forEvery issuance, device-bound or notDevice-bound credentials only, including every PID

Both are JWTs signed by the Wallet Provider with ES256, ES384 or ES512. A Key Attestation is used for one issuance only, and a WIA is never reused towards a different issuer, so issuers cannot link requests from the same wallet.

How a Wallet Unit obtains its WIAs and Key Attestations

TS3 leaves this step to each Wallet Provider, since it happens inside one wallet product. The ARF only requires the Wallet Provider to verify the app’s integrity before signing a WIA, and to verify that the attested private keys really sit in the named WSCD or keystore before signing a Key Attestation. A typical flow, using platform attestations such as Google Play Integrity or Apple DeviceCheck as evidence, looks like this.

What a WIA and a Key Attestation contain

Neither carries an iss claim: the issuer identifies the Wallet Provider from the signing certificate in the x5c header. The examples below are decoded, with header and payload separated by a dot.

Wallet Instance Attestation

{
"typ": "oauth-client-attestation+jwt",
"alg": "ES256",
"x5c": ["MIIC..."]
}.{
"sub": "https://wallet.example.eu",
"wallet_name": "ExampleWallet-mobile",
"wallet_version": "2.3.0",
"wallet_link": "https://wallet.example.eu/about",
"wallet_solution_certification_information": "https://wallet.example.eu/certification/2-3-0",
"exp": 1789329600,
"client_status": {
"status": {
"status_list": { "idx": 48213, "uri": "https://wallet.example.eu/status/wia/17" }
},
"exp": 1791936000
},
"cnf": {
"jwk": { "kty": "EC", "crv": "P-256", "x": "...", "y": "..." }
}
}
FieldWhat it tells the issuer
typ / x5cThat this is a client attestation, and the Wallet Provider’s certificate chain to check against the Trusted List
subThe wallet type, the same for every installation, so it cannot be used to track a user
wallet_name / wallet_versionThe Wallet Solution as listed on the Trusted List, and its version
wallet_solution_certification_informationWho certified the Wallet Solution; the exact content is still to be defined
expTechnical expiry, less than 24 hours after the integrity check
client_statusA Status List entry for this Wallet Instance, and the date until which the Wallet Provider keeps it up to date
cnfThe key that signs the proof of possession sent along with the WIA

Key Attestation

{
"typ": "key-attestation+jwt",
"alg": "ES256",
"x5c": ["MIIC..."]
}.{
"iat": 1789257600,
"exp": 1791936000,
"certification": "https://wallet.example.eu/certification/wscd/secure-element",
"key_storage": ["iso_18045_high"],
"user_authentication": ["iso_18045_high"],
"attested_keys": [
{ "kty": "EC", "crv": "P-256", "x": "...", "y": "..." },
{ "kty": "EC", "crv": "P-256", "x": "...", "y": "..." }
],
"key_storage_status": {
"status": {
"status_list": { "idx": 3, "uri": "https://wallet.example.eu/status/ka/1" }
},
"exp": 1794614400
}
}
FieldWhat it tells the issuer
typ / x5cThat this is a key attestation, and the Wallet Provider’s certificate chain to check against the Trusted List
iat / expWhen it was issued and when it technically expires
attested_keysPublic keys whose private keys the WSCD or keystore generated and holds; several keys allow batch issuance
key_storage / user_authenticationHow well the storage, and the user authentication that unlocks the keys, resist attack; a WSCD is always iso_18045_high for both
certificationThe certification of the WSCD or keystore, from which the issuer can tell whether it is a WSCD
key_storage_statusA Status List entry for the WSCD or keystore, and the date until which the Wallet Provider keeps it up to date
nonceThe issuer’s c_nonce, present only when the Key Attestation is sent as an attestation proof

Where a WUA travels during OpenID4VCI issuance

The two attestations go to different places. The WIA authenticates the wallet as an OAuth client at the Authorization Server. The Key Attestation goes to the Credential Issuer with the request for the credential itself.

Pushed Authorization Request with the WIA

POST /par HTTP/1.1
Host: issuer.example.eu
Content-Type: application/x-www-form-urlencoded
OAuth-Client-Attestation: <WIA JWT>
OAuth-Client-Attestation-PoP: <PoP JWT signed with the WIA cnf key>

response_type=code
&client_id=https%3A%2F%2Fwallet.example.eu
&scope=company_registration
&code_challenge=<S256 challenge>
&code_challenge_method=S256
&redirect_uri=<wallet redirect URI>

The client_id matches the sub of the WIA. The Token Request carries the same two headers. Because the Credential Issuer never sees the WIA, the Authorization Server has to pass its client_status on, for example inside the access token.

Credential Request with the Key Attestation

POST /credential HTTP/1.1
Host: issuer.example.eu
Authorization: DPoP <access_token>
DPoP: <DPoP proof JWT>
Content-Type: application/json

{
"credential_configuration_id": "company_registration",
"proofs": {
"jwt": ["<proof JWT: Key Attestation in its key_attestation header, signed with attested_keys[0]>"]
}
}

With the attestation proof type, the request carries "proofs": { "attestation": ["<Key Attestation JWT>"] } instead. There is no proof of possession then: the Wallet Unit passes the issuer’s c_nonce to the Wallet Provider, which puts it in a freshly signed Key Attestation.

The WUA lifecycle

A WUA is short-lived and single-use on purpose, but the revocation information behind it outlives the token itself. The first three stages are routine; the fourth only happens when something goes wrong.

Outside the EUDI Wallet ecosystem, some Wallet Providers issue wallet attestations with a very short lifespan and no status reference at all, relying on expiry instead of revocation; OpenID4VCI makes the status claim optional. TS3 does not allow that for EUDI Wallets. The WIA already lives less than 24 hours, yet both the WIA and the Key Attestation must carry a status reference that the Wallet Provider maintains for at least 31 days, because PID Providers use it to revoke PIDs long after issuance.

WUA, WIA and KA: which term means what

The terminology changed while the specifications matured. TS3 called the WIA a Wallet App Attestation until version 1.1, and used WUA for what is now the Key Attestation until version 1.5. Older articles and ARF drafts therefore use the terms differently.

TermWhat it coversWho receives it
WUAUmbrella term for the two attestations belowPID Providers and Attestation Providers, during issuance only
WIAThe Wallet Instance, meaning the appThe Authorization Server, in the Pushed Authorization Request and Token Request
KAA WSCD or keystore and the keys it holdsThe Credential Issuer, in the proofs of the Credential Request
  • Wallet Attestation
  • Wallet Unit
  • Wallet Provider
  • Attestation Provider
  • Status List
  • Trusted List
  • OpenID4VCI

Frequently asked questions

Is a WUA the same thing as a Wallet Instance Attestation?

No. Since version 1.5 of TS3, WUA is the umbrella term for two attestations: the Wallet Instance Attestation (WIA), which covers the app, and the Key Attestation (KA), which covers the WSCD or keystore holding the keys. Older documents use WUA for what is now called the Key Attestation, which is why the terms are often confused.

Does a verifier ever see the WUA?

No. ARF requirements WUA_07 and WUA_24 allow a Wallet Unit to present a WIA or Key Attestation only to a PID Provider or Attestation Provider during issuance, never to a Relying Party. A verifier checks the credential instead: its signature, its device binding and its revocation status. If the wallet behind a PID is revoked, the PID Provider revokes the PID within its 24-hour check cycle, and the verifier sees that through the PID’s own status.

How long is a WUA valid?

A WIA expires less than 24 hours after the Wallet Provider checked the app’s integrity. A Key Attestation can be valid for longer, at the Wallet Provider’s discretion. Separately, each carries a status maintenance date, client_status.exp or key_storage_status.exp, until which the Wallet Provider keeps the revocation status up to date. The Wallet Unit must always be able to present one whose date is at least 31 days away, and a PID must expire before that date.

What happens if a wallet unit is compromised before its WUA expires?

The Wallet Provider revokes the Wallet Instance on its WIA Status List, or, for a vulnerability in a type of WSCD or keystore, the Status List entry for that storage. A PID Provider checks the WIA and Key Attestation status behind every PID it issued at least once every 24 hours and revokes the PID when either is revoked. Attestation Providers may do the same. Expiry of the WUA itself is not what removes the wallet; the revocation of the credentials issued to it is.

Can one Key Attestation cover more than one key?

Yes. attested_keys can list several public keys from the same WSCD or keystore, which is how batch issuance works: the issuer binds each credential in the batch to a different key, and one Wallet Provider signature covers all of them. When the Key Attestation travels in a jwt proof, the Wallet Unit signs that proof with the first key in the list only.

Sources

  1. TS3: Wallet Unit Attestations used in issuance of PID and Attestations
  2. EUDI Wallet Architecture and Reference Framework
  3. OpenID for Verifiable Credential Issuance 1.0
  4. OpenID4VC High Assurance Interoperability Profile 1.0
  5. OAuth 2.0 Attestation-Based Client Authentication
  6. Token Status List
note

This page is informational and does not constitute legal advice. For authoritative guidance consult the European Commission and the OpenID Foundation directly.