HAIP 1.0 — implementation notes

What CodeB’s EU Wallet Verifier implements against the OpenID for Verifiable Credentials — High Assurance Interoperability Profile published by the OpenID Foundation. Section-by-section, with honest gaps flagged.

No certification claim. The OpenID Foundation runs a formal HAIP conformance-certification programme. CodeB does not currently hold that certification. What follows is our own implementation record against the HAIP text — we describe what the code does and cite the sections we implement.

Summary

AreaStatusWhat we implement
Credential formatImplementedSD-JWT VC with KB-JWT holder binding
Signing algorithmImplementedES256 (P-256 ECDSA), IEEE P1363 signature encoding
Client Identifier PrefixImplementedBoth x509_san_dns and x509_hash; wallet chooses via ?client_id_prefix=
Signed Authorization Request (JAR)Implementedtyp: oauth-authz-req+jwt, x5c in JOSE header, per-tenant verifier key
Query languageImplementedDCQL only (Presentation Exchange deliberately not shipped)
Response modeImplementeddirect_post.jwt with JWE-encrypted response
Response encryptionImplementedECDH-ES + A128GCM, ephemeral P-256 keypair per session
Key Binding JWT verificationImplementedKB-JWT signature + nonce + aud checked
x5c chain handlingImplementedEvery VP x5c chain parsed, stored per tenant, exposed to downstream RPs via the verified-claims file
Transaction data (per-session)ImplementedSigned request payload accepts arbitrary transaction_data that the integrator validates per use case

Section notes

§3 Credential format — SD-JWT VC

The verifier parses IETF SD-JWT VC compact form, walks the disclosure array, verifies each disclosure hash against the signed payload’s _sd array (SHA-256 by default; SHA-384 / SHA-512 accepted per registry). The KB-JWT is required — a VP without one fails the verify step. Verified claims land at App_Data/<tenant>/vc-claims/<user>.json and are relayed through the standard OIDC userinfo endpoint under a vc claim group.

§4 Cryptographic suites

Signing: ES256 exclusively for verifier-side JWS. .NET Framework 4.7.2+ ECDsa.SignData(byte[], HashAlgorithmName) emits IEEE P1363 (raw r||s) natively — no DER-to-P1363 conversion needed. Wallet-side signatures accepted: ES256, ES384, ES512, EdDSA (Ed25519 via BouncyCastle).

Response JWE: alg = ECDH-ES, enc = A128GCM, ephemeral P-256 keypair minted per session and destroyed after use. NIST SP 800‑56A Concat KDF, RFC 7518 §4.6 / §5.1 shape.

§5 Client Identifier — both prefixes shipped

The default is x509_hash (OID4VP 1.0 §5.9.3, wallet-preferred: no DNS resolution needed at wallet-side, works on isolated networks). x509_san_dns is available for callers that request it via ?client_id_prefix=x509_san_dns. Both derive from the same per-tenant verifier X.509 leaf, so switching prefixes doesn’t force re-issuance.

§6 Signed Authorization Request (JAR)

Request body: JWT with typ: oauth-authz-req+jwt, x5c in JOSE header (single self-signed cert in dev, operator-supplied chain in production), ES256 signature. Payload fields per HAIP: client_id with the prefix scheme applied, response_type = vp_token, response_mode = direct_post.jwt, response_uri pinned to our ?vp-response=<session>, nonce, state, client_metadata including the JWK for response encryption, dcql_query object.

§7 DCQL query language

Only DCQL is shipped. Presentation Exchange 2.0 is not implemented — DCQL is the OID4VP 1.0 Final mandatory query language and the EUDI reference wallets implement DCQL first. Adding PE would double the query surface for a use case the tender does not require.

§8 Response processing

JWE decrypted with the ephemeral P-256 private key held server-side for the session lifetime. The wallet’s ephemeral public key is read from the JWE header (epk). Inner JWT is the VP with the SD-JWT VC + disclosures + KB-JWT. Verifier walks: (1) JWS signature over the SD-JWT, (2) disclosure hashes match _sd, (3) KB-JWT signature over the SHA-256 of the JWT+disclosures, (4) KB-JWT nonce matches our request nonce, (5) KB-JWT aud matches our client_id, (6) status of each disclosure recorded on the tenant’s VC claims file.

§10 Trust framework

Trust anchor is a per-tenant explicit X.509 leaf, suitable for pilots and controlled deployments. Every verified presentation carries an __iter field (visible in the tenant’s VC claims file and in the audit log) so operators and downstream RPs can see exactly which trust regime backed a given assertion. The current runtime writes iter=1 (per-tenant explicit anchor) into App_Data/<tenant>/vc-claims/<user>.json.

What we do not claim

What we do claim

Evidence you can inspect

Iteration — what shipped since these notes were first written

Spec reference: openid4vc-high-assurance-interoperability-profile-1_0 · last reviewed 2026-07-12