Security Architecture
This page provides an overview of EUDIPLO's security architecture, including cryptographic algorithms, token validation, DPoP support, and secret handling policies.
Cryptographic Algorithmsโ
EUDIPLO uses ES256 (ECDSA with P-256 curve) as the primary signing algorithm across all protocols:
| Operation | Algorithm | Curve | Notes |
|---|---|---|---|
| Access Token Signing | ES256 | P-256 | JWT signed by issuer AS |
| SD-JWT VC Signing | ES256 | P-256 | Credential signed by issuer attestation key |
| mDOC Signing | ES256 | P-256 | Mobile Security Object (MSO) signed via COSE |
| Status List Signing | ES256 | P-256 | OAuth Token Status List JWT signed by issuer |
| Trust List Signing | ES256 | P-256 | ETSI TL or OpenID Federation metadata signed by trust anchor |
| VP Token Signing | ES256 | P-256 | Verifiable Presentation signed by wallet |
Rationale:
ES256 is the EUDI Wallet Architecture Reference Framework (ARF) baseline requirement and is widely supported across EUDI ecosystem implementations. Alternative algorithms (RS256, EdDSA) may be added in future releases based on interoperability requirements.
Key Loading and Storageโ
EUDIPLO enforces asynchronous key loading to prevent blocking the main application thread during key retrieval from external KMS providers.
Key Providersโ
All signing keys are managed via pluggable KMS providers:
| Provider | Description | Use Case |
|---|---|---|
db | Database-stored keys (encrypted at rest) | Development, testing, small-scale deployments |
vault | HashiCorp Vault Transit secrets engine | Production environments with centralized key management |
aws-kms | AWS Key Management Service | Cloud-native deployments on AWS |
pkcs11 | PKCS#11 Hardware Security Module | High-security environments (air-gapped, FIPS compliance) |
http | Remote KMS microservice | Custom key management infrastructure |
csc | Cloud Signature Consortium (CSC) API | Remote signature services |
See Key Management for provider configuration.
Secret Handling Policyโ
EUDIPLO enforces a zero-secret-export policy for private key material:
| Scenario | Policy |
|---|---|
| Private Keys in Configuration Bundles | โ Never exported โ Private keys are always generated or stored in the KMS provider and are never included in configuration bundles. |
| Private Keys in API Responses | โ Never returned โ The Key Chain API only returns public key material (JWK public key or X.509 certificate). |
| Environment Variable Placeholders | โ
Allowed in kms.json โ Secret references (e.g., ${VAULT_TOKEN}) are permitted for KMS provider configuration. |
| Encryption Keys | โ ๏ธ Database-only โ Encryption keys (for decrypting VP Tokens) are always stored in the database. These are never loaded from external KMS providers. |
Audit Logging:
All private key operations (signing, key generation, key rotation) are logged via the AuditLogService for compliance tracking. Logs include:
- Timestamp
- Tenant ID
- Key Chain ID
- Operation type (
sign,generate,rotate,delete) - User/service identity (if authenticated)
Token Validationโ
EUDIPLO enforces strict JWT validation for all token-based flows (access tokens, VP tokens, credentials).
Required Claimsโ
| Claim | Description | Validation Rule |
|---|---|---|
iss (Issuer) | Token issuer identifier | Must match expected issuer (tenant URL or configured external AS) |
aud (Audience) | Token audience | Must include EUDIPLO's tenant base URL |
exp (Expiration) | Expiration timestamp | Token must not be expired (current time < exp) |
nbf (Not Before) | Not-before timestamp | Token must be valid (current time >= nbf) |
iat (Issued At) | Issuance timestamp | Token must not be issued in the future (current time >= iat) |
Clock Skew Tolerance:
EUDIPLO allows a 30-second clock skew for exp, nbf, and iat validation to account for minor time synchronization differences between systems.
Access Token Validation (OID4VCI)โ
When a wallet presents an access token at the credential endpoint, EUDIPLO verifies:
- Signature: Verify JWT signature using the issuer's public key (from JWKS or X.509 cert)
- Issuer: Check that
issmatches the expected AS endpoint - Audience: Check that
audincludes the credential issuer URL - Expiration: Check that
expis in the future - Session Correlation: Extract
issuer_stateand correlate with active session - DPoP Binding (if enabled): Verify
cnf.jktmatches the DPoP proof key thumbprint
Example Access Token:
{
"iss": "https://eudiplo.example.com/tenant1/issuer",
"sub": "wallet-client-id",
"aud": "https://eudiplo.example.com/tenant1",
"exp": 1234567890,
"iat": 1234567800,
"issuer_state": "session-uuid",
"client_id": "wallet-client-id",
"cnf": {
"jkt": "dpop-key-thumbprint"
}
}
VP Token Validation (OID4VP)โ
When a wallet submits a VP Token, EUDIPLO verifies:
- Decryption: Decrypt JWE using the configured encryption key (if VP Token is encrypted)
- Signature: Verify each credential's signature using the issuer's public key
- Nonce: Verify the
nonceclaim matches the session'swalletNonce - Audience: Verify the
audclaim matches the verifier's client ID - Trust Validation: Verify the credential issuer is trusted (via trust list or federation)
- Status Check: Verify the credential is not revoked or suspended (via status list)
- Claims Validation: Verify presented claims match the DCQL query
See Presentation Architecture for detailed verification flow.
DPoP (Demonstrating Proof-of-Possession)โ
EUDIPLO supports DPoP (RFC 9449) to bind access tokens to the wallet's public key. This prevents token theft and replay attacks.
DPoP Flowโ
DPoP Proof Structureโ
The DPoP proof is a signed JWT included in the DPoP HTTP header:
{
"typ": "dpop+jwt",
"alg": "ES256",
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "...",
"y": "..."
}
}
.
{
"jti": "unique-jti",
"htm": "POST",
"htu": "https://eudiplo.example.com/tenant1/issuer/token",
"iat": 1234567800
}
Claims:
| Claim | Description |
|---|---|
jti | Unique JWT ID (prevents replay) |
htm | HTTP method (POST, GET) |
htu | HTTP URI (request endpoint URL) |
iat | Issued-at timestamp |
Validation:
- Verify JWT signature using the
jwkclaim - Verify
htmmatches the HTTP method - Verify
htumatches the request URL - Verify
iatis recent (within 60 seconds) - Verify
jtihas not been used before (replay prevention)
Configuration:
DPoP is enabled per issuance configuration:
{
"dPopRequired": true
}
Wallet Attestationโ
EUDIPLO supports Wallet Attestation (OAuth 2.0 Client Attestation PoP) to verify the wallet provider's trustworthiness before issuing credentials.
Wallet Attestation Flowโ
Attestation Headersโ
| Header | Description |
|---|---|
OAuth-Client-Attestation | Wallet provider's signed attestation JWT (includes wallet's public key) |
OAuth-Client-Attestation-PoP | Wallet's proof-of-possession JWT (signed with wallet's private key) |
Attestation JWT:
{
"iss": "https://wallet-provider.example.com",
"sub": "wallet-instance-id",
"iat": 1234567800,
"exp": 1234567890,
"cnf": {
"jwk": {
"kty": "EC",
"crv": "P-256",
"x": "...",
"y": "..."
}
}
}
PoP JWT:
{
"iss": "wallet-instance-id",
"aud": "https://eudiplo.example.com/tenant1",
"iat": 1234567800,
"jti": "unique-jti"
}
Validation:
- Verify attestation JWT signature using wallet provider's public key (from trust list or JWKS)
- Verify attestation is not expired (
exp) - Extract wallet's public key from attestation (
cnf.jwk) - Verify PoP JWT signature using wallet's public key
- Verify PoP
audmatches the issuer URL - Verify PoP
jtihas not been used before (replay prevention)
Configuration:
Wallet attestation is enabled per issuance configuration:
{
"walletAttestationRequired": true
}
Session Security (OID4VP ยง13.3)โ
EUDIPLO implements the OID4VP ยง13.3 session security model to prevent session fixation and replay attacks.
Wallet Nonce Separationโ
The walletNonce is a wallet-facing session identifier that is distinct from the internal session ID. This prevents attackers from enumerating or guessing session IDs.
Flow:
- EUDIPLO creates a presentation request with
nonce: walletNonce - Wallet includes the nonce in the VP Token
- EUDIPLO correlates the VP Token with the session via the
walletNonce - EUDIPLO never exposes the internal session ID to the wallet
Database Schema:
@Entity()
export class Session {
@PrimaryGeneratedColumn('uuid')
id: string; // Internal session ID (never exposed)
@Column({ unique: true })
walletNonce: string; // Wallet-facing nonce (exposed in protocol)
// ...
}
Response Code (Same-Device Redirect)โ
For same-device flows (e.g., verifier and wallet on the same device), EUDIPLO generates a one-time response_code to prevent session fixation attacks.
Flow:
- Wallet submits VP Token to
/direct_post.jwt - EUDIPLO validates the VP Token
- EUDIPLO generates a one-time
response_codeand stores it in the session - EUDIPLO redirects the wallet to
redirect_uri?response_code=xxx - Verifier exchanges the
response_codefor the verification result
Security Properties:
| Property | Enforcement |
|---|---|
| Single-Use | Response code is consumed after first use |
| Short-Lived | Expires after 5 minutes |
| Random | Cryptographically random (32 bytes) |
| Session-Bound | Only valid for the session that created it |
Attack Prevention:
This prevents an attacker from:
- Embedding a stolen
redirect_uriin a malicious QR code - Correlating the wallet's session with a different verifier's session
- Replaying a
response_codefrom a previous presentation
Secret Handlingโ
EUDIPLO enforces strict policies to prevent accidental exposure of secrets, private keys, and user PII.
Secrets in Configurationโ
| Secret Type | Storage | Policy |
|---|---|---|
| Private Keys | KMS provider (never in config files) | โ Never exported or included in config bundles |
| Database Passwords | Environment variables | โ
Must use ${DB_PASSWORD} placeholder in config files |
| KMS Tokens | Environment variables | โ
Must use ${VAULT_TOKEN} placeholder in kms.json |
| Webhook Secrets | Environment variables | โ
Must use ${WEBHOOK_SECRET} placeholder in webhook config |
| API Keys (Attribute Providers) | Environment variables | โ
Must use ${API_KEY} placeholder in attribute provider config |
Configuration Export:
When exporting configuration bundles via the management API:
- Private keys are never included (only public keys and certificates)
- Secret placeholders are preserved (e.g.,
${DB_PASSWORD}) - Sensitive session data is excluded (user claims, VP tokens)
Secrets in Logsโ
EUDIPLO uses Pino logger with automatic secret redaction:
| Logged Field | Redaction Policy |
|---|---|
| Access Tokens | โ Never logged (even redacted) |
| Private Keys | โ Never logged |
| User PII | โ Never logged unless explicitly enabled for debugging |
| DPoP Proofs | โ ๏ธ Logged at debug level only (contains public key, not secret) |
| VP Tokens | โ ๏ธ Logged at debug level only (for debugging failed verifications) |
| Credential Claims | โ ๏ธ Logged at debug level only (for debugging issuance) |
Audit Logging:
The AuditLogService persists compliance events to the database. Audit logs include:
- Timestamp
- Tenant ID
- User/service identity
- Operation type
- Success/failure status
- Redacted request/response payloads (no secrets or PII)
HTTPS and TLSโ
EUDIPLO requires HTTPS in production for all external endpoints:
| Endpoint Type | HTTPS Requirement | Notes |
|---|---|---|
| Issuer Endpoints | โ Required | All OID4VCI endpoints must use HTTPS |
| Verifier Endpoints | โ Required | All OID4VP endpoints must use HTTPS |
| Webhook Endpoints | โ Required | Outbound webhook requests use HTTPS |
| Management API | โ Required | All API endpoints must use HTTPS |
| Local Development | โ ๏ธ Optional | HTTP allowed when NODE_ENV=development |
TLS Configuration:
EUDIPLO does not terminate TLS itself. Deploy behind a reverse proxy (e.g., NGINX, Traefik, AWS ALB) to handle TLS termination.
Certificate Trust:
For external KMS providers (e.g., Vault, AWS KMS), EUDIPLO validates TLS certificates using the system's default trust store. Custom CA certificates can be added via the NODE_EXTRA_CA_CERTS environment variable.
CORS (Cross-Origin Resource Sharing)โ
EUDIPLO enforces strict CORS policies for browser-based wallet interactions:
| Endpoint Type | CORS Policy |
|---|---|
| Protocol Endpoints | โ CORS enabled for all OID4VCI/OID4VP endpoints |
| Management API | โ CORS disabled (API access requires server-to-server authentication) |
| Digital Credentials API | โ CORS enabled for DC API endpoints |
Allowed Origins:
By default, EUDIPLO allows CORS requests from all origins for protocol endpoints (to support wallet apps from any domain). For production deployments, configure the CORS_ORIGINS environment variable to restrict allowed origins:
CORS_ORIGINS=https://wallet.example.com,https://app.example.com
Rate Limitingโ
EUDIPLO includes built-in rate limiting to prevent abuse and denial-of-service attacks:
| Endpoint Type | Rate Limit | Window |
|---|---|---|
| Token Endpoint | 10 requests/min per IP | Rolling 60-second window |
| Credential Endpoint | 20 requests/min per access token | Rolling 60-second window |
| Offer Endpoints | 100 requests/min per tenant | Rolling 60-second window |
| Management API | 60 requests/min per API key | Rolling 60-second window |
Configuration:
Rate limits can be customized via environment variables:
RATE_LIMIT_TOKEN=10
RATE_LIMIT_CREDENTIAL=20
RATE_LIMIT_OFFER=100
Security Checklistโ
Before deploying EUDIPLO to production, verify:
- โ HTTPS enabled for all external endpoints
- โ
KMS provider configured (not using
dbprovider in production) - โ Environment variables used for all secrets (no hardcoded secrets)
- โ Session cleanup enabled with appropriate retention policy
- โ Rate limiting configured for protocol endpoints
- โ CORS origins restricted to trusted wallet domains
- โ Audit logging enabled and persisted to secure storage
- โ TLS certificates valid and trusted
- โ DPoP enforcement enabled for production credential issuance
- โ Wallet attestation enabled for high-security use cases
- โ Trust list validation configured for credential verification
Next Stepsโ
- Cryptography: Key Management and Algorithms
- Chained AS: Authorization Architecture
- Key Management: KMS Providers
- Session Management: Session Lifecycle
- Issuance Flow: Issuance Architecture
- Presentation Flow: Presentation Architecture