Class ResponseEncryptionKeyStore
- Namespace
- Tessio.Verifier.AspNetCore
- Assembly
- Tessio.Verifier.AspNetCore.dll
Holds one response-encryption key pair per session, for the lifetime of that session.
public sealed class ResponseEncryptionKeyStore : IDisposable
- Inheritance
-
ResponseEncryptionKeyStore
- Implements
- Inherited Members
Remarks
The key must be ephemeral per authorization request: SPEC: HAIP 1.0 §5 requires it ("Verifiers MUST supply ephemeral encryption public keys specific to each Authorization Request"), which OpenID4VP 1.0 §5.1 allows, and the conformance suite fails a verifier that reuses one. The reasons are real rather than procedural. A single long-lived key means its compromise retrospectively exposes every response ever encrypted against it, and a stable advertised public key is a correlation handle that ties separate presentations to one verifier.
By default the private half never leaves memory and never reaches disk. It exists from
{prefix}/start until the wallet responds or the session expires, typically minutes. This
mirrors Tessio.Verifier.AspNetCore.RequestObjectStore, which holds the signed request the same way and for the
same span.
Built with ResponseKeyDerivationOptions, the store derives each key from a master secret and a fresh salt (ResponseKeyDerivation) and saves only the salt, so another instance, or this one after a restart, derives the same key from it. That gives up part of the reason above: while a salt is kept, the master derives its key again and decrypts a response to it that someone captured, so the salt store should delete salts once they expire. Without them it is single process only: a second instance cannot decrypt a response encrypted against a key it never generated. Failing loudly is deliberate: a silent fallback to a shared key would quietly reintroduce exactly the defect this type exists to remove.
Constructors
ResponseEncryptionKeyStore(TimeProvider)
A store that generates each key and holds it in this process only.
public ResponseEncryptionKeyStore(TimeProvider clock)
Parameters
clockTimeProvider
ResponseEncryptionKeyStore(TimeProvider, ResponseKeyDerivationOptions)
A store that derives each key, so every instance sharing the master and the salts finds it.
public ResponseEncryptionKeyStore(TimeProvider clock, ResponseKeyDerivationOptions derivation)
Parameters
clockTimeProviderderivationResponseKeyDerivationOptions
Exceptions
- ArgumentNullException
The options, their master or their salt store is null.
- ArgumentException
The master is shorter than MinimumMasterLength bytes or all zeros.
Methods
CreateForRequest(DateTimeOffset)
Creates a fresh key pair for one authorization request, held until expiresAt.
public ResponseEncryptionKeyProvider CreateForRequest(DateTimeOffset expiresAt)
Parameters
expiresAtDateTimeOffset
Returns
Remarks
Keyed by the key's own kid (RFC 7638 thumbprint) rather than by session id, because for
direct_post.jwt the session correlation handle (state) is inside the encrypted
payload: at decryption time the only identifier available is the kid the wallet echoes
back in the JWE header. SPEC: OpenID4VP 1.0 §5 requires every advertised JWK to carry a
kid and §8.3 then requires the wallet to echo it, so requiring it here (no fallback) only
rejects a non-conformant response; Get(string?) returns null for an absent or unknown kid.
Exceptions
- ObjectDisposedException
The store derives keys and has been disposed.
Dispose()
Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
public void Dispose()
Get(string?)
The key with this kid, or null when it is unknown or expired.
public ResponseEncryptionKeyProvider? Get(string? keyId)
Parameters
keyIdstring
Returns
Remarks
A derived key this process does not hold is derived again from its stored salt and kept until it expires.
Exceptions
- InvalidOperationException
The salt stored for this
kidis too short or derives a different key, so the master differs from the one that created it, or the salt is corrupt.- ObjectDisposedException
The store derives keys and has been disposed.