Open awoie opened 12 months ago
In the example above, the producer info (apu
) is used by ECDH-ES as input to ConcatKDF which changes the derived symmetric key.
relates to #27
To prevent extremely lazy RPs from including the JWE in the path/query/fragment of the redirect_uri
, we could add further requirements on the redirect_uri
which the wallet must be able to check. The simplest although not best solution since it might not work in certain environments would be to say redirect_uri
eq response_uri
.
Update:
redirect_uri
.To prevent extremely lazy RPs from including the JWE in the path/query/fragment of the
redirect_uri
, we could add further requirements on theredirect_uri
which the wallet must be able to check. The simplest although not best solution since it might not work in certain environments would be to sayredirect_uri
eqresponse_uri
.
Alternatively, we could provide the redirect_uri
before the Authz Response object is sent to the Response URI, and make the wallet to append the state
parameter to the Redirect URI in case they received the state
parameter in the Authz Request Object. This way, there has to be no limitation on the actual length/value of the redirect_uri
since this way it won't be able to encode the Authz Response Object (vp_token
) in any form.
After some some internal discussions, the apu
approach would only work if the entire protected header is provided in the last step since the RP needs the same binary representation of the protected header to verify the authentication tag of the JWE.
Another approach would be to use JWE with direct encryption where a wallet-generated secret is provided to the RP in the last redirect. The secret is used to derive the symmetric decryption key to decrypt the JWE.
The following is the current flow using direct_post and an additional response_code:
sequenceDiagram
actor u as User
participant vf as Verifier
participant vb as Verifier Response Endpoint
participant w as Wallet
autonumber
u->>vf: Interacts
vf->>vf: Create nonce
vf->>vb: Initiate transaction
vb->>vb: Generate transaction_id, request_id
vb->>vb: Cache [transaction_id] -> request_id
vb-->>vf: Return transaction_id, request_id as state
vf->>w: Authorization Request (response_uri, nonce, state)
u->w: User authentication and consent
w->>w: Generate Authorization Response
w->>vb: Authorization Response (vp_token, state)
vb->>vb: Check if request_id from state is valid
vb->>vb: Generate response_code
vb->>vb: Cache<br/>[transaction_id] -> Authorization Response, response_code
vb-->>w: Return redirect_uri with response_code
w->>vf: Redirect to redirect_uri
vf->>vb: Fetch response data (transaction_id, response_code)
vb->>vb: Lookup cached data for transaction_id
vb->>vb: Validate received response_code
vb-->>vf: Response data (vp_token, presentation_submission)
vf->>vf: Validate nonce
vf->>vf: Validate response data
The idea is to force the verifier to maintain the session by encrypting response and providing the key to the response endpoint and redirect URI using secret sharing (HKDF(IKM, salt) = derived key). The red rectangles show what needs to be changed.
sequenceDiagram
actor u as User
participant vf as Verifier
participant vb as Verifier Response Endpoint
participant w as Wallet
autonumber
u->>vf: Interacts
vf->>vf: Create nonce
vf->>vb: Initiate transaction
vb->>vb: Generate transaction_id, request_id
vb->>vb: Cache [transaction_id] -> request_id
vb-->>vf: Return transaction_id, request_id as state
vf->>w: Authorization Request (response_uri, nonce, state)
u->w: User authentication and consent
w->>w: Generate Authorization Response
rect RGB(255,0,0,.1)
w->>w: Generate new random salt and key (IKM) and derive encryption key using HKDF
w->>w: Encrypt Authorization Response with derived key
w->>vb: Authorization Response (JWE(vp_token, state), IKM)
end
vb->>vb: Check if request_id from state is valid
vb->>vb: Generate response_code
rect RGB(255,0,0,0.1)
vb->>vb: Cache<br/>[transaction_id] -> Authorization Response, response_code, IKM
vb-->>w: Return redirect_uri with response_code
w->>vf: Redirect to redirect_uri and HKDF salt
end
vf->>vb: Fetch response data (transaction_id, response_code)
vb->>vb: Lookup cached data for transaction_id
vb->>vb: Validate received response_code
rect RGB(255,0,0,0.1)
vb-->>vf: Response data (JWE, IKM)
vf->>vf: Derive decryption key using HKDF from IKM and salt redirect_uri parameter
vf->>vf: Decrypt JWE
end
vf->>vf: Validate nonce
vf->>vf: Validate response data
We seem to be missing the step where the wallet checks if the verifier is trusted before sending the authz response.
It seems you are proposing to encrypt the whole authorization request and then provide the Verifier Response Endpoint with the IKM. That would allow the Verifier Response Endpoint to decrypt the message and provide its frontend with encrypted data. What threat do we want to solve here?
I also re-read the intial attack description and have to admit, I fail to see the problem. What is described in step (12)
"The RP checks if the response_code is valid (exists, not invalidated, etc.) without validating that the response_code belongs to the session and returns the success page."
should be prevented by the Verifier Response Endpoint requiring the Verifier Frontend to query the response using the transaction_id, which shall be gathered from the session (see https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-response-mode-direct_post-2, step (8)).
It seems you are proposing to encrypt the whole authorization request and then provide the Verifier Response Endpoint with the IKM. That would allow the Verifier Response Endpoint to decrypt the message and provide its frontend with encrypted data. What threat do we want to solve here?
Additionally, the salt is provided via the browser redirect, the IKM is provided to the Response Endpoint. Only with salt and IKM, the verifier can derive the key to decrypt the data. The verifier is forced to associate the response in the Response Endpoint with the session in the frontend, otherwise the verifier cannot combine salt and IKM. That is the idea.
should be prevented by the Verifier Response Endpoint requiring the Verifier Frontend to query the response using the transaction_id, which shall be gathered from the session (see https://openid.net/specs/openid-4-verifiable-presentations-1_0.html#name-response-mode-direct_post-2, step (8)).
Yes, if the verifier is compliant and diligent, then there is no problem. As stated, this proposal aims to enable the wallet to actively force the verifier to implement session validation correctly. Currently, if the verifier is negligent, the wallet has no way to determine whether the verifier is lazy nor has a mechanism to enforce diligence.
Anti-phishing for lazy RPs
Problem
When considering the same-device flow steps outlined below, the following attack is possible:
client_id
according to theclient_id_scheme
Authz Request parameter.response_code
parameter tied to the session, usually viastate
.response_code
parameter in the Redirect URI.response_code
via the frontend (if theresponse_code
was provided in the URL fragment) or backend (if theresponse_code
is part of the URL query string).response_code
is valid (exists, not invalidated, etc.) without validating that theresponse_code
belongs to the session and returns the success page.This attack can be mitigated if the RP implements
response_code
/session binding validation appropriately, requiring a diligent implementation.It is worth noting that the wallet and the user are unable to detect whether the RP's implementation is diligent. This issue was highlighted by the ISO/IEC SC17/JTC1 WG10.
However, many RPs, while genuine, often opt for the least implementation effort without disrupting the flow (similar to
nonce
handling in existing RPs).To safeguard users from lazy RPs, it would be beneficial to define a flow that does not necessitate the RP to be diligent and still allows the flow to proceed smoothly.
Proposed Solution
At IETF 118 a side conversation concluded that the only effective mitigation for this would involve encrypting the Authz Response and providing the last input of the key derivation function via the Redirect URI to the RP. In this manner, the JWE has some detached inputs for the key derivation function so to say.
The following proposed solution involves encrypting the Authz Response Object using JARM/ECDH-ES. In that case, the RP has to provide its public key via
client_metadata
orclient_metadata_uri
in the Authz Request. Alternatively, the RP is pre-registered. The process is outlined below:apu
value.epk
of the wallet.apu
value from the JWE. Important input to the key derivation function are now detached from the JWE. This input is not known to the RP yet.apu
parameter to the Redirect URI as a new parameter. Note that the wallet does not have to intercept the actual redirect, as the Redirect URI is provided as a JSON payload in the HTTP response body of the Response URI request.redirect_uri
parameter from the JSON response and redirects the user through the mobile browser to the Redirect URI with the additionalapu
parameter.apu
parameter, looks up the Authz Response received earlier in the flow, and can now decrypt it since it received the producer info.Alternatively, this approach works without requiring
client_metadata
/client_metadata_uri
orjwks
/jwks_uri
, and it can be implemented with a symmetric key for direct encryption, simplifying the implementation process.