Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Wallet-provided nonce #92

Closed
tlodderstedt opened this issue Mar 11, 2024 · 24 comments
Closed

Wallet-provided nonce #92

tlodderstedt opened this issue Mar 11, 2024 · 24 comments

Comments

@tlodderstedt
Copy link

tlodderstedt commented Mar 11, 2024

Based on our work on an OpenID 4 VP profile for the Digital Credentials API, I believe the API would benefit from allowing Wallets to provide Verifiers with a nonce.

The challenge we see is that at least the eIDAS regulation requires the Wallet to authenticate the Verifier/RP based on a trust management, that is not the Web trust management. Articles 5a and 5b of the eIDAS regulation require the EU member states to run their own trust management infrastructure with dedicated certificates/attestations to be used for that purpose (see the OpenID 4 VP proposal for details).

That's why the OpenID 4 VP profile allows Verifiers to send signed requests through the Digital Credentials API. The fundamental challenge with this approach is that the signed request can be captured and replayed by anyone able to run a legit website in his browser. If an attacker would replay such a request through the Digital Credentials API, the wallet would provide the credential data to the attacker.

The wallet could use the calling origin as asserted by the platform. However, I see the following issues:

  • the web origin is not necessarily the identifier of the RP/Verifier with the external trust management, so the Wallet might not be able to match both identifiers. Or, the Digital Credentials API would impose restrictions on the identifiers used by implementations.
  • the web origin is authenticated using a different trust model - that's not the same as unauthenticated but the trust put into this mechanisms might vary, especially if talking about high assurance, government issued identify credentials.

I think we need additional countermeasures.

That challenge exists independent of the Digital Credentials API. OpenID 4 VP implementations today cope with that challenge by sending the data to an endpoint asserted in the signed request object. Additionally, we are in the process of allowing the wallet to issue a nonce that the Verifier must include in the signed request object.

I suggest to add support for a wallet provided nonce to the Digital Credentials API. This would allow the selected wallet (instance) to create a nonce and force the Verifier to add it to the signed request. Given the device/instance bound nature of that nonce, the request could not be replayed on a different device.

The alternative, I'm considering currently, is to allow for direct communication between Wallet and Verifier, so the Wallet could provide a nonce directly to the Verifier through a HTTP POST request and the Wallet could send the credential data directly to the Verifier through another HTTP POST request. This alternative would use existing OpenID 4 VP messages.

see the OpenID 4 VP profile proposal for details.

@timcappalli
Copy link
Member

The verifier should know its calling origin, and could include it as a parameter in the signed request. The web platform can pass the origin to the app platform and then to the wallet. The wallet can then validate that the signed request origin and calling origin match. Doesn't this address the concerns?

@tlodderstedt
Copy link
Author

Perhaps.

The fundamental challenge is whether the Wallet is allowed to trust in the origin asserted by the web platform.

If we assume so, I think it would require Verifier metadata trusted under the external trust management that would authorize the web origin along with requirements re the authentication of that web origin (e.g. qWACs only). As an example, if trust in the Verifier would be established using OpenID Federation, the Verifier's entity configuration could list legit web origins along with the respective trust anchors.

@ve7jtb
Copy link

ve7jtb commented Mar 11, 2024

I brought this trust issue up previously.

The options are

  1. have the verifier sign a request over a wallet provided nonce (to prevent replay)
  2. have the wallet encrypt the response with a trusted key eg qWAC( those are typically signing not encryption keys)

Without 1 some info could leak about the existence of a credential if not the actual contents.

To make 2 work on its own the wallet would need to:
A) trust the browser to validate the origin
B) be able to validate in trusted meta-data that a given web origin is legitimate for the RPID or equivalent of the verifier.

@leecam
Copy link
Collaborator

leecam commented Mar 11, 2024

Yeah i think you can do one or more of the following:

  1. Trust the calling origin
  2. When using the HPKE the reader key can root down to a CA the wallet trusts. This on its own doesn't stop the replay attack Torsten raises but does ensure an attacker can't decrypt the response
  3. Have the verifier include a signed timestamp into the request. This could enforce the same timeout that you'd have with a wallet provided nonce.

@ve7jtb
Copy link

ve7jtb commented Mar 11, 2024

I don't know that signing a timestamp is the equivalent to a nonce. The problem is that there is no audience in the nonce.
With our privacy concerns having an audience would be hard. In the timestamp case anyone can request one and immediately replay it to the browser. So it may address part of the issue but not all.

Part of the problem is that we have two worlds, one that is webauthn which is a 2 party model based on web origins.

The other is OAuth/Connect that are based on redirects where web origin is only available via Referer: headers and that is not to be trusted. This leads redirect based protocols to use identifiers for the parties and sign/encrypt messages end to end for security. Verifier/RP claims to be RPID X by signing a request , Wallet/AS looks up ClientID via internal DB or meta-data, Wallet/AS validates signature and determines encryption key and redirect URI for the response.

The question is how to integrate the models and provide the best security and privacy, given the wallet may be regulated to not trust the browser completely when it comes to validating the origin, and the web origin not directly relating the the current notion of clientID being an abstract identifier.

@leecam
Copy link
Collaborator

leecam commented Mar 11, 2024

Ah yeah, ack on the timestamp issue!

@tplooker
Copy link

Without 1 some info could leak about the existence of a credential if not the actual contents.

Not saying things are entirely equivalent here but isn't this the assumption that WebAuthn makes? E.g if the origin is compromised, an attacker could trick a webauthn provider to leak the existence of a credential?

Have the verifier include a signed timestamp into the request. This could enforce the same timeout that you'd have with a wallet provided nonce.

The other problem to consider with signed timestamps is clock sync issues that could occur between the RP and wallet, generally a clock skew has to be introduced to account for this which can further degrade the rudimentary replay attack detection by widening the accepted time window by the wallet.

have the wallet encrypt the response with a trusted key eg qWAC

Personally I think I find this solution more appealing then a wallet provided nonce, however we'd have to carefully consider relying on encryption alone here (assuming we aren't trusting the browser).

@tplooker
Copy link

The verifier should know its calling origin, and could include it as a parameter in the signed request. The web platform can pass the origin to the app platform and then to the wallet. The wallet can then validate that the signed request origin and calling origin match. Doesn't this address the concerns?

I'm not convinced that signing the request here does anything to provide the wallet with any greater assurance about who sent the request in the event you don't trust the web platform, because the entire request can just be replayed. It feels like the two core options we have are as @ve7jtb has pointed out, wallet provided nonce, or rely on authenticating the encryption key where I prefer the latter.

@timcappalli
Copy link
Member

Not saying things are entirely equivalent here but isn't this the assumption that WebAuthn makes? E.g if the origin is compromised, an attacker could trick a webauthn provider to leak the existence of a credential?

Yes, WebAuthn operates on the premise that the client and channel are trusted.

@RByers
Copy link
Member

RByers commented Mar 14, 2024

If we were to pursue a wallet-provided nonce, what would that look like in the API shape in practice? We can't share the nonce with the site until the user has consented to the data exchange and we don't even know which wallet is relevant until we show the credential picker. So I guess we'd be talking about adding a back-and-forth exchange after the user selects a credential? So the verifier first makes a request, then gets the nonce, then re-issues the signed request with the nonce? That seems pretty ugly and complex (eg. what happens if the second request is now different somehow?). Or maybe there's a simpler design I'm missing?

@tplooker
Copy link

If we were to pursue a wallet-provided nonce, what would that look like in the API shape in practice? We can't share the nonce with the site until the user has consented to the data exchange and we don't even know which wallet is relevant until we show the credential picker. So I guess we'd be talking about adding a back-and-forth exchange after the user selects a credential? So the verifier first makes a request, then gets the nonce, then re-issues the signed request with the nonce? That seems pretty ugly and complex (eg. what happens if the second request is now different somehow?). Or maybe there's a simpler design I'm missing?

As a part of deciding whether we need to explore this I'd like to understand the underlying threat model we are considering.

For example it would appear on the surface that the solution option 2) as raised by @ve7jtb

have the wallet encrypt the response with a trusted key eg qWAC( those are typically signing not encryption keys)

wouldn't complicate the web API design (e.g it could still be a single request/response), while guaranteeing that only the relying party could decrypt the response (under the assumption of sound encryption). It would also allow the wallet to authenticate the relying party independent of the web platform through the encryption of the response.

If we don't think this solution works, I'd be keen to understand why, if the assumption is that the attacker can defeat encryption, which is one of the possible POV's I've heard, then any wallet provided nonce solution here is also vulnerable because TLS can be defeated. To be clear I don't believe we should be making this assumption, I just wanted to share that observation.

The only other concern I've heard about this proposed solution is that if the web origin is compromised, an attacker can get a credential presentation from a wallet, even though its encrypted and it cannot decrypt it. Which again I do question how big of an issue this is. Other then knowing a credential was presented to the origin from a wallet, what is leaked?

On the flip side I find the wallet provided nonce solution quite complex for little benefit, it requires the wallet to interact directly with the verifier through network requests which could have negative privacy consequences and just generally increases the implementation complexity for both the wallet and the relying party.

@tlodderstedt
Copy link
Author

tlodderstedt commented Mar 15, 2024

The question whether encryption is the solution is less a technical than a question of perception by users and regulators. Same with origin authentication.

Authentication with the web origin might work for web authn, but the context we are in differs in some aspects: in Web Authn, the origin is used for creation (issuance) and use of one authentication credential and this credential is always presented to this origin only. Digital Credentials presentation is different as it may result in the disclosure of a huge number of credentials with substantial amount of PII to a third party. Think of personal identification, health data, and so on. So users as well as regulators might want to make sure the party the data is disclosed to can truly be identified (to be able to sue/prosecute them if needed) and they might not necessarily trust in the web PKI. Just take a look into the respective requirements in the eIDAS regulation.

So if we cannot rely on the origin authentication (in regulated use cases), should we trust in the encryption of the response (assuming the encryption key can be linked to the legit recipient)? I'm not sure as it means the wallet would ask the user for consent to disclose data to a party we have not authenticated as recipient of the actual request. This is like saying "eh, we are not sure but even if you disclose the data to an evildoer, don't worry, it's encrypted."

I'm sorry if that sounds paranoid, but I'm simply not sure whether this is the way we want to pursue.

I think having a wallet provided nonce is a straightforward solution to the problem as it ensure the correct context of the authentication. There is a reason it is best practice in secure protocol design that the party consuming a signature should provide the challenge/nonce used in it.

If that is impossible with the Digital Credentials API design, I suggest to pursue the alternative design I have meanwhile added to the OID4VP over Digital Credentials API proposal. It is to send the initial request (used to select the wallet) through the API and then let the Wallet obtain the signed request directly from the Verifier through a HTTPS endpoint.

@RByers
Copy link
Member

RByers commented Mar 15, 2024

+1 to better understanding the threat models. I've written a threat model for the Chrome API that I can probably produce a public version of, but it can assume little about the credential and wallet ecosystem and so is pretty limited to the generic case which isn't likely to be helpful in this eIDAS-focused discussion (in which I'm definitely not qualified to produce a threat model).

In terms of @leecam's signed timestamp suggestion for thwarting replay attacks, isn't it fairly standard to use a timestamp plus a random value as a nonce? If the verifier generated a nonce like that, then the wallet just has to verify that the timestamp is recent and also that the nonce wasn't previously seen within the timestamp acceptance window. I think that's almost as strong as having a wallet-generated nonce (and much simpler to implement).

Also +1 to having the wallet encrypt the response using a verifier-supplied certificate (whether that's qWAC or something else in other contexts). That seems like the primary mechanism for ensuring confidentiality of the response. But presumably @tlodderstedt has a threat in mind where just replaying a request without being able to read the response could be harmful, which I can definitely imagine (eg. exhausting the wallet's limited supply of tokens like, I think, the ISO-18013 MSO).

@tplooker
Copy link

tplooker commented Mar 17, 2024

So if we cannot rely on the origin authentication (in regulated use cases), should we trust in the encryption of the response (assuming the encryption key can be linked to the legit recipient)? I'm not sure as it means the wallet would ask the user for consent to disclose data to a party we have not authenticated as recipient of the actual request. This is like saying "eh, we are not sure but even if you disclose the data to an evildoer, don't worry, it's encrypted."

I think we need to reframe here for these "regulated usecases". I dont think this API should be conceptualised or designed to have two distinct ways of a wallet authenticating the RP. Rather the wallet authenticating the RP via the attested origin from the browser should be table stakes and something that is mandatory for a wallet to perform. Authenticating the RP via an external mechanism (like an independent certificate chain) should be viewed as an additional layer not an alternative.

I'm sorry if that sounds paranoid, but I'm simply not sure whether this is the way we want to pursue.

I understand the driver here but I think its important to point out that even if an API design was pursued where there was never any trust in the web platform assumed, trust is still required in the web platform because you are after all in a web browser. Everything the user did up until the point that the website they are visiting calls this API and everything after requires a level of trust in the browser.

@tlodderstedt
Copy link
Author

tlodderstedt commented Mar 20, 2024

I'm ok with viewing the external mechanism as additional layer. My focus is on getting this additional layer secured appropriate. As I explained, I think a signed request including a wallet provided nonce is the most appropriate design taking into consideration secure design best practices. There are two options: 1) the DG API provides that feature or 2) wallet and verifier sort that our directly. I would prefer (1) but (2) works, too, and don't requires changes to the API design.

@OR13
Copy link
Contributor

OR13 commented Mar 26, 2024

This thread needs flow diagrams to be comprehensible.

The use of the word "nonce" is also extremely ambiguous, because it could be random, signed or encrypted.

@tplooker
Copy link

tplooker commented Apr 15, 2024

Thinking about the OpenID4VP proposal referenced in this issue, after discussing with others more, I don't think the approach achieves the security properties it desires, specifically "authenticating the RP before sending the response". This is because the request sent over the browser API is inherently replay-able.

For example consider example.com is the verifier origin making the credential presentation request using this OpenID4VP proposal, if an attacker simply obtains a fresh valid OpenID4VP request from the verifier backend (example.com's backend) and sends it through the browser api from its own origin (attacker.com) or through a compromise to the verifiers origin (example.com). The wallet on receipt of the the request will resolve the request_uri and conclude the verifier is "authenticated" (e.g as the sender of the request), after obtaining user consent it will generate and send a response back through the API to the attacker.

This to me makes it clear that the wallet provided nonce doesn't meaningfully improve the assurances around authenticating the verifier prior to generating and sending the response. So I would prefer we explore for usecases that require it, providing a way to authenticating the response encryption key as an additional web platform independent mechanism for authenticating the RP.

@tplooker
Copy link

Here is a diagrammatic depiction of the above, based on the current OpenID4VP proposal

image

@tlodderstedt
Copy link
Author

@tplooker which of the messages is replayed?

@tplooker
Copy link

Replay might be a bit presumptuous, but basically the attacker site gets a genuine request from the attacker backend, which includes a genuine request_uri that will resolve. It then plays it through its own (attacker) origin, basically because all the relying party authenticate is based on the response to the request_uri, the wallet will "authenticate" the request and send the response, even though it is sending the response back to the attacker.

@tlodderstedt
Copy link
Author

tlodderstedt commented Apr 15, 2024

@tplooker the attacker can get access to credential data.

Counter-measures:

  • encrypted authorization response
  • direct post response
  • the wallet could use the asserted web origin as additional threat signal (if the origin could be resolved through the external trust framework) - already mentioned in the PR
  • the wallet could also send the origin with the POST request to the request_uri, that would serve a similar purpose

@tplooker
Copy link

tplooker commented Apr 16, 2024

Agree with all the above as counter measures other then the last one

the wallet could also send the origin with the POST request to the request_uri, that would serve a similar purpose

The only way I see this one working is if the origin sent here is the attested origin provided by the browser? Perhaps that is what you meant?

IMO though fetching the request from the request_uri with a wallet provided nonce would appear not to make sense now from a security perspective, because if we have concluded that the signed request coming back from this endpoint doesn't actually reliably authenticate the verifier / relying party, could we not revert to a simpler model which just relies on the verifier / relying party, sending the wallet the request directly over the browser API, if not, what does fetching the signed request from the request_uri do security wise?

@RByers
Copy link
Member

RByers commented May 1, 2024

Discussed on the call now that this can probably be closed based on conversations at IIW, but will wait for confirmation from @tlodderstedt.

@tlodderstedt
Copy link
Author

yes, we have found an appropriate solution that is described in the OID4VP DC API Profile. I close this issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants