openid / OpenID4VCI

60 stars 16 forks source link

Wallet Attestations and Nonces #71

Open tlodderstedt opened 10 months ago

tlodderstedt commented 10 months ago

Currently, implementations can protect the PoP for wallet attestations (https://datatracker.ietf.org/doc/draft-looker-oauth-attestation-based-client-auth) by:

The latter does not work for Pushed Authorization Requests. The proposal was made, to let the wallet request a nonce from the Issuer that can then be used for binding the wallet attestation. This came up in various discussions including the eIDAS expert group's touch point meeting on OID4VCI and discussions with member states.

The basic idea would be to define another endpoint, e.g. nonce endpoint, the wallet could be use to obtain that nonce.

peppelinux commented 10 months ago

why not reducing complexity by signing the request (request) with the private key where the public one is attested within the WIA (client_assertion)?

I believe we can satisfy all the requirements without adding anything new and preserving the current implementations

POST /as/par HTTP/1.1
Host: pid-provider.example.org
Content-Type: application/x-www-form-urlencoded

response_type=code
&client_id=$thumprint-of-the-jwk-in-the-cnf-wallet-attestation$
&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM
&code_challenge_method=S256
&request=eyJhbGciOiJSUzI1NiIsImtpZCI6ImsyYmRjIn0.ew0KIC Jpc3MiOiAiczZCaGRSa3F0MyIsDQogImF1ZCI6ICJodHRwczovL3NlcnZlci5leGFtcGxlLmNvbSIsDQo gInJlc3BvbnNlX3R5cGUiOiAiY29kZSBpZF90b2tlbiIsDQogImNsaWVudF9pZCI6ICJzNkJoZFJrcXQz IiwNCiAicmVkaXJlY3RfdXJpIjogImh0dHBzOi8vY2xpZW50LmV4YW1...
&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-client-attestation
&client_assertion=$WalletInstanceAttestation$
tlodderstedt commented 10 months ago

@peppelinux You mean the signature over the request is the proof of possession for the wallet attestation? That would mean to send a signed request object to the PAR endpoint, right? Something like this:

`POST /as/par HTTP/1.1 Host: pid-provider.example.org Content-Type: application/x-www-form-urlencoded

response_type=code &client_id=$thumprint-of-the-jwk-in-the-cnf-wallet-attestation$ &request=eyJhbGciOiJSUzI1NiIsImtpZCI6ImsyYmRjIn0.ew0KIC Jpc3MiOiAiczZCaGRSa3F0MyIsDQogImF1ZCI6ICJodHRwczovL3NlcnZlci5leGFtcGxlLmNvbSIsDQo gInJlc3BvbnNlX3R5cGUiOiAiY29kZSBpZF90b2tlbiIsDQogImNsaWVudF9pZCI6ICJzNkJoZFJrcXQz IiwNCiAicmVkaXJlY3RfdXJpIjogImh0dHBzOi8vY2xpZW50LmV4YW1...

That would reduce the attack surface as an attacker would need to replay the request. It does not work (out of the box) for token requests as there is no equivalent of a signed request there.

fmarino-ipzs commented 10 months ago

The token request can be bound to the authorization code. Regarding the client authentication, both private_key_jwt or draft-looker-oauth-attestation-based-client-auth might be used. @tlodderstedt do you see any issues on this?

peppelinux commented 10 months ago

That would reduce the attack surface as an attacker would need to replay the request. It does not work (out of the box) for token requests as there is no equivalent of a signed request there.

I assume that a jti within the JWT (signed) request avoids the possibility that issA+jti may collide with issB+jti or be used in a replay attack.

following this assumption, may we say that nonce is then not required anymore?

tlodderstedt commented 10 months ago

@peppelinux As I wrote in my initial comment, one time use (jti) is a possible option for replay prevention. However, one time use limits scalability and nonces offer more implementation flexibility and are considered best practice.

@fmarino-ipzs As I wrote in my initial comment, an authorization code cannot be used as nonce in a pushed authorization request. So we either stick to one time use or short expiration or we add support for nonces.

peppelinux commented 10 months ago

adding a new endpoint for satisfying the same requirement satisfied by jti, according to RFC, might sound overkill

fmarino-ipzs commented 10 months ago

My comment was related to your last sentence regarding the token request where there is no equivalent to the signed request. For the PAR, the jti approach provides the same benefit in term of replay attack without introducing a new endpoint.

tlodderstedt commented 10 months ago

@peppelinux Can you please describe why you assess use of a nonce as "overkill"?

As I already stated, jti requires one time use on the issuer side, which means shared, current state across cluster nodes. This is know to cause issues with scalability of systems. Besides this, nonces provided by the receiving party are best security practice.

tlodderstedt commented 10 months ago

@fmarino-ipzs

My comment was related to your last sentence regarding the token request where there is no equivalent to the signed request.

@peppelinux suggested to use a request signature as proof of possesion instead of the proof of possession in the wallet attestation pop. That would work for PAR but not for the token request, as there is no signed token request. So the question is whether the proof of possession is implemented as defined in draft-looker-oauth-attestation-based-client-auth (as JWT appended to the assertion) or as another private_key_jwt parameter. Not sure of the benefits of the later.

For the PAR, the jti approach provides the same benefit in term of replay attack without introducing a new endpoint.

I disagree. Please read my comment above.

fmarino-ipzs commented 10 months ago

From a security point of view (mitigation against replay attacks), I see no benefit in using a nonce over jti. On the contrary, using a nonce requires a new endpoint that does not come for free. But maybe I am missing something. Regarding the scalability issue, which has nothing to do with security or even PoP, could you give us some references?

peppelinux commented 10 months ago

My intent is to reuse what we already have without require any additional endpoints made for the issuance like the nonce endpoint might represent.

I look forward to the satisfaction of the security requirements with their implementation considerations, putting aside every personal taste, as an implementer, and above all the ideas to which we try to become attached.

Here I share the flow that a group of developers and analysts have implemented and tested on stage, which uses both PAR and token endpoint with DPoP. We continue working on that, then our approach is still open. Since this beautiful working group is enriched by mutual experiences it will produce for sure a solution that objectively move, replace or improves, in our minds, the flow that is shared below:

https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/pid-eaa-issuance.html#token-endpoint

I believe that for the value of decision making, it is important to highlight why some choices are preferable to others, then we will implement them exactly like Buddhist monks destroy their sand mandalas for creating new ones (without getting attached!).

In the previous flow the private_key_jwt is involved, the signed JWT contains aud, exp and jti. Then sub, aud and jti prevents the replay.

Probably you're trying to tell us that using the nonce endpoint it would give the benefit to reduce the cross checks above multiple JWS, HTTP Headers and HTTP parameters? If yes let's try to have a clear schema about the number of checks and the attested level of security given by these, then we'll definitively feel comfortable to adopting a solution instead of another

peppelinux commented 10 months ago

@peppelinux Can you please describe why you assess use of a nonce as "overkill"?

As I already stated, jti requires one time use on the issuer side, which means shared, current state across cluster nodes. This is know to cause issues with scalability of systems. Besides this, nonces provided by the receiving party are best security practice.

I think that this really needs to be confirmed with objective elements (since this chat of nonce vs. jti is long from more than a year, you know :sweat_smile:)

with the cap of implementer I assume to have a cluster of services, be they whatever entity that deals with JWTs and serves the token endpoint.

I require that a centralized or distributed storage is then used, where any transactions life time happen: creation, enrichment/update and pruning.

If I have a nonce endpoint, when I issue a nonce this would be stored in the storage shared between the nodes. This would be the same for any transaction or couple of iss-jwt (unique-together) -> stored in the storage, be it a nonce or a couple of issuer-jti.

There are other cases where the nonce is encrypted with a key that is know within the cluster, this doesn't require a storage, because any receiving party would validate by decrypting it (as some strategy does with the web cookies, for instance).

anyway, due to the important number of mixed endpoints we used to have in the OAuth2 framework, we know that anyway we need a layer of persistence where the transaction is tracked and enriched/updated. Then I don't see these scalability improvements or concerns of a solution vs another.

@tlodderstedt or @jogu, since you worked for decades in IETF and with success with all-about-nonce, could you please enlights why the nonce is a game changer in this context and the jti must then not be used?

jogu commented 10 months ago

The key differences with jti vs nonce:

nonce is generated by the authorization server and it doesn't actually necessarily need to store anything (sometimes it generates a hash or jwt in some fashion that means it can verify a value without needing to have stored it, or values can be communicated in advance to all servers).

jti is generated by the client. Many authorization servers don't check jti uniqueness (it's not mandatory to implement in most specs). Checking jti properly definitely requires a store that is shared across all regions the token can be presented in. The size of the store varies depends on the validity time of the token, which is also set by the client (iat/exp) in many cases, and the size is potentially unbounded depending on how fast clients might present valid tokens. Because the jti is client selected, if a client is compromised in some way the attacker can generate valid jwts for use (potentially days) later (nonces prevent that).

sietseringers commented 10 months ago

In the NL wallet, we would prefer to use nonces over jti primarily because we think there are security advantages over jwt:

Since a nonce is a value that didn't exist before the issuer generated it, a nonce-based approach just gives more guarantee that the pop that signs the nonce is truly "fresh".

We plan to use nonces or similar mechanisms everywhere else whenever something is disclosed or issued, as it is indeed considered best practice. Not using it here would be the one exception to that. Since the wallet attestation (at least in our setup) serves as a sort of anchor on which the security of a large part of the rest of the setup depends, we feel that this is not a place where we can or should ignore security advantages such as the ones above.

There are also advantages in the implementation, as previously mentioned. It can be implemented stateless if the issuer would e.g. sign the nonce into a JWT as @jogu suggests. And even if you do implement it using state, then it will be less state, that is easier to deal with.

jogu commented 10 months ago

A JWT with a jti intended for issuer A might be replayed to issuer B

Unless I have forgotten a situation, I don't think that is possible in the VCI spec as all JWTs should have aud values. (I think there's one situation in VP where it might be an issue [using signed request objects by value to shared custom url schemes where you're not sure which wallet will open] but I'm not sure nonces are usable in that situation either.)

peppelinux commented 10 months ago

@jogu I agree on the different nature between jti and nonce, their directions and audiences, even if a jti is issued by any kind of jwt issuer, be it AS, RP, Client, OP and so on, but I fully got your point and I agree that handling a single value (nonce, even if encrypted with many bytes within it) is less that handling an entire JWT in term of computational and storage costs.

Regarding the issue that some or many implementations doesn't properly handle the "uniqueness" of the jti would be better taken out, since we would say the same for every implementation that, for instance, supports X.509 without verifying properly a certificate chain; these consideration are too much relative to the technical debits of the implementations, even if, the value in your and also @tlodderstedt words may be found in two important aspects: 1) simplicity: a single value, issued by the actor that validates the flow where it is the main auditor; 2) implementation robustness: avoids the implementation weakness of mixing many properties taken from multiple sources, while a single value, when mandatory in the implementation (where jti is not) makes the difference in a completely arbitrary way.

@sietseringers thank you for join in this discussion, here my though:

JWT might mean many things depending on its payload, to avoid a replay the recipients are the traditional aud, iss, sub and jti. I agree with you that mixing multiple properties and then checking them may open the door to bug and implementation weakness. @jogu good catch for the aud, even if it is not the case for the private_key_jwt.

regarding the shared state I'm not convinced that nonce is different in this context, since it should be shared in that storage anyway. It is thin, it may be verified with encryption with internal logic of validation (then it may be reversible and not opaque to the issuer).

Well, now that I've forced you to share a walk along this road, and had at least for me a brilliant chat on pros and cons, the still opened points on this thread of jti vs nonce I think are enough to move to the actionable stuffs within this thread.

My scope is having a robust and secure interop with other parties, then it seems that we really want to go for the nonce endpoint. @tlodderstedt and @fmarino-ipzs if you agree I believe that this chat that started more than a year ago costs more than the implementation of a simple endpoint for the issuance of the nonce :sweat_smile: implementing it would costs less than 8 hours, not a problem, then ...

I would like to focus more on the scopes and then on the big picture of this issue. Is this intended to say that the WIA must be ephemeral and one-time-use?

If yes, I would say: it depends, maybe not. I assume that a WIA must be reused until its expiration and there might be cases where a WIA, bringin the public key attestation within it, should be reused along the same pseudonym (siopv2 id_token) that was issued along with it. Before raising too much ideas on that, I would say that probably it would time to say that it may be better to have multiple types of WIA for different scopes.

before deciding that WIA should be one-time-use, with a nonce, I would analyze with your help the impacts on the entire ecosystem for that. Then let's develop the nonce endpoint in few hours!

alenhorvat commented 10 months ago

Hi.

If my understanding of the discussion is correct, there are essentially two strategies (which are, IMO, compatible)

a) Passive sharing of a Wallet Attestation (jti-protection) b) Active sharing of a Wallet Attestation (nonce protection)

a) In this case:

b) In this case, the AuthZ./Token request is followed by a "VP or ID Token" (OID4VP or SIOP) request to the wallet asking for a Wallet attestation VC. Maybe there are other "request" protocols I don't know of

In EBSI, we implemented b). To me, a) is straightforward if JAdES is used as the wallet attestation can be transported as signer attributes.

I believe we'll see both cases.

jogu commented 10 months ago

Just to be clear on one point:

Regarding the issue that some or many implementations doesn't properly handle the "uniqueness" of the jti would be better taken out, since we would say the same for every implementation that, for instance, supports X.509 without verifying properly a certificate chain

These are slightly different cases:

In the case of TLS it is expected (and stated in many specs) that you must verify the certificate chain.

In the case of jti there is no required behaviour in any spec I can immediately recall that anyone must reject already seen jtis.

sietseringers commented 10 months ago

regarding the shared state I'm not convinced that nonce is different in this context, since it should be shared in that storage anyway. It is thin, it may be verified with encryption with internal logic of validation (then it may be reversible and not opaque to the issuer).

Generally nonces are treated as shared state like the jti, indeed. In that sense I agree they are the same, but there are still differences. I expect that the nonce-based approach will result in less state, since generally you only need to remember the nonce until the client sends its response (a signature over the nonce), which will be (almost) immediately after fetching the nonce. By contrast, a jti will need to be remembered until the end of the expiration window, which is much longer. So there will be more state that needs to be remembed, also increasing the difficulty of obtaining performance and scalability as mentioned by @tlodderstedt.

Additionally, I think the failure mode that I mentioned can really be a concern, in that it opens its own attack vector for a sufficiently powerful adversary:

That would be difficult so it might be considered hypothetical, but the nonce-based approach has no such potential attack vector at all.

Regarding the issue that some or many implementations doesn't properly handle the "uniqueness" of the jti would be better taken out, since we would say the same for every implementation that, for instance, supports X.509 without verifying properly a certificate chain

Well, there are sufficient examples of vulnerabilities originating in improperly validating an X.509 chain. I believe a standard should strive to make it as easy as possible to write implementations that are secure, and I believe the nonce-based approach has an advantage there.

alenhorvat commented 10 months ago

If the nonce is not computed from the information, it must be shared with all AS in the cluster. If the nonce is computed from the information, it is susceptible to similar attacks as jti. (If someone gets a jwt and jti it can get the nonce.)

However (as mentioned above)

Questions:

What's not mentioned in the flow is the credential offer. Could it contain a nonce?

If the Wallet attestation should be bound to a session (IMO, that's not necessary), one could go with the flow:

this way, nonces or their hashes are not transported more than 1x. The attacker must get to the wallet to get all the info. If that happens, it's game over anyway.


I don't see any issue with the flow presented by @peppelinux since the holder needs to authenticate anyway.

Is this question about reducing the burden of the AS?

peppelinux commented 10 months ago

@alenhorvat the point that you have raised Is very interesting, by adding a new unprotected endpoint then we should protect It in some way against the abuse.

both nonce and JWT.jti are then stored with the sole difference that a signed JWT.jti protects the write with an authentication, while nonce not, then the implementers must use some way to protect the nonce endpoint against the abuses

danielfett commented 10 months ago

If the nonce is not computed from the information, it must be shared with all AS in the cluster.

Both is not true for jti.

Also, there is no need for a new nonce endpoint to issue nonces; DPoP issues nonces in headers in error messages.

I wonder if the whole approach should not be structured more like DPoP, defining headers that can be used in requests of many types. Or even a DPoP extension, reusing the DPoP nonces and/or the DPoP PoP.

peppelinux commented 10 months ago

If there is a load balancer connecting the same client to the same node for a while, "global" nonces don't need to exist, they can be node-local.

it depends by the balancing type, the way you indicate is a sticky balancing that binds clients to endpoints (session persistence). This solution is awesome but works just for balancing and not for fault tolerance of the nodes, for this a shared storage is then needed.

Both is not true for jti.

indeed, a signed JWT.jti is authenticated.

I wonder if the whole approach should not be structured more like DPoP

I fully agree with you on this, here we have used DPoP in both the issuance and the presentation phase (for providing the WIA)

https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/pid-eaa-issuance.html#detailed-flow https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/relying-party-solution.html#remote-protocol-flow

alenhorvat commented 10 months ago

To prevent a reply attack, the server needs to keep track of "consumed tickets", whether it is jti, nonce, or DPOP JWT. Difference between client-generated "nonce" (jti, dpop jwt) and server-generated nonce: how long the information needs to be stored.

client-generated nonce: for the lifetime of the request server-generated nonce: until the nonce is consumed or lifetime of the nonce, at worst

from a security point of view, I don't see a big difference. From a capability point of view, it is similar. the new nonce endpoint would need some protection, and a server-generated nonce approach would be less memory intensive since most nonces are usually consumed quickly and can be removed (whether LB+sticky is used or distributed DB, ... it's a design decision)

peppelinux commented 10 months ago

The storage costs depends also to the retention policy for the signed requests and responses

Where identities are involved, with personal data, the retention duration Is established by gdpr and member states specific regulation

Then the persistence of both jti and nonce (this latter contained in the signed requests) would be the same

selfissued commented 10 months ago

I'll second the point that nonces can be self-authenticating and self-contained and therefore need not be stored. A common way to achieve this is for the nonce to contain content encrypted to the server that creates it.

tlodderstedt commented 10 months ago

Based on the discussion in the WG session today, I suggest the following design: a nonce is provided on demand with an error message from the PAR or token endpoint (as in DPoP). This way the wallet can send the wallet attestation (with a jti) and the issuer can decide whether it requires a nonce or not. And we don't need a separate endpoint.

What do people think?

peppelinux commented 10 months ago

It seems a good achievement to be further analyzed, below some notes in the wild.

We now for sure have realized the requirement of providing the Wallet Instance Attestation during both the issuance and the presentation phase, even if the presentation is out of scope here I would put on the table that we're also working on this issue: https://github.com/openid/OpenID4VP/issues/45#issuecomment-1711954514

following the elements taken in my understanding we're trying to put in place a discovery process, which purpose is to obtain the wallet instance attestation "before-any" other operations made by its audience (CI or RP).

If I'm on the right page, the discovery process ends with the acquisition of the wallet instance attestation, the verification of the PoP and the evaluation of the capabilities of the wallet instance.

We have this requirement both in the issuance and in the presentation phases.

Might we think to create a specialized endpoint to achieve this feature of wallet instance discovery, to be then the same implementation in both the issuance and the presentation phase, to be then reused as it is, like a kind of wallet instance registration endpoint? (even if that cannot be compared with a registration phase, but just a discovery where WIA is submitted to a trusted audience)

paulbastian commented 10 months ago

So after reading this long issue, I want to share my views:

For Authcode-Flow , especially PAR, the proposed nonce request is probably good. For Pre-Authorizedcode-Flow, we have the option to put the nonce into the credential offer or reuse the preauth code, that would then be slightly different to the Authcode Flow, but safe us one roundtrip. I'm unsure whether OpenID4VCI should strip off the option of jti, but I think that especially HAIP should do that

tlodderstedt commented 10 months ago

One thought just popped up that I would like to share with you. The proposal to use a nonce provided as part of an error response assume the wallet would produce a proof of possession without nonce, send a request, if required obtains a nonce from an error response, creates a new proof of possession, repeats the call. I think the underlying assumption is creating another signature for the proof of possession is cheap. What if that is not the case, for example if the respective key is managed in a remote HSM? In this case, the proposal causes another network roundtrip to the remote HSM on top. Would that be a problem? @sietseringers please have a look.

tlodderstedt commented 10 months ago

@peppelinux I suggest we discuss both topics and try to develop optimal designs for the different interfaces. However, I have doubts we can use the same solution simply because the assumptions re capabilities are different, at least in my mental model. An issuer in OID4VCI can host endpoints the wallet can directly call via HTTPS. The wallet in OID4VP, in contrast, offers redirect destinations but the rest of the communication is from the wallet towards the verifier (create presentation request, direct_post). So I would try to use the same principles but I don't know whether the same design can be used.

tlodderstedt commented 10 months ago

@peppelinux I think I was wrong. You are aiming at using the same pattern to present the wallet attestation to issuers and verifiers, right? As I wrote, the wallet sends requests in both directions since issuer and verifier are supposed to host endpoints. So yes, I think we can have a nonce endpoint / error responses with nonces and bind the proof of possession of the wallet attestation to them. I will add this to the advertisement flow. This also allows to present the wallet attestation for SIOP, where it allows the verifier to determine whether it trusts the wallet to securely manage the keys used for login.

alenhorvat commented 10 months ago

Is there a reason for not using OIDC Fed. entity statements as Wallet attestation?

  1. Wallet provider or certifying org is a Trust Anchor
  2. Wallet provider can delegate the role to other parties (intermediate)
  3. Entity statements can contain wallet instance metadata and can contain additional information as trust marks

Entity statements are bound to cryptographic material and can be transported in the signed request/response headers.

Edit: In some projects we also tested (and it works)

Note 1: here I'm using the term key attestation since the certificate expresses the validity/information about the key itself. In none of the cases certificates contained personal data (only public key + key related information). Note 2: key attestation lifetime can range from minutes to days, depending on the privacy requirements and whether or not we need to assume certificate issuer infrastructure is always available or not (some stakeholders rejected approaches where their authentication mechanism depends on the availability of the issuer infra - this was a business requirement) Note 3: for long-lived certificates, revocation is required. There's always a trade off. Note 4: for issuance we used either ACME for x509 or OID4VCI. Of course in all cases users are already onboarded to their wallets and bound their digital identity to it.

peppelinux commented 10 months ago

@alenhorvat

In Italy we use federation where the wallet provider is a leaf

The wallet instance Is not an organizational entity accreditated to a trust anchor/intermediate, then It is not a federation entity

The wallet provider provides a wallet instance attestation to its wallet instances

The wallet instance must contain the trust chain of the wallet provider, this can be both federation and x5c if required

Once we have made this important distinction about how trust model is intender for personal devices and organizational entity I can say that

A wallet instance attestation may be provided in the form of federation trust mark, however since the wallet instance Is not a federation entity this looses interest

A more interesting topic I would suggest: an RP should obtain a verifier_attestation in the form of trust mark, here the issue where I will propose a PR: openid4vp issue Number 32 (https://github.com/openid/OpenID4VP/issues/32)

peppelinux commented 10 months ago

@tlodderstedt we're getting in sync, please consider these:

advertisement endpoint doesn't sound good, in my understanding Advertising is the practice to attention to a product or service. I would suggest a better semantic shaped with a self explanation about its purposes. If It Is for the discovery let's call It wallet instance discovery endpoint.

Then It make sense returning a nonce for transation binding, then i would remove the transaction-id and then use just the nonce

I'm really interested on the Daniel's propose of using dpop then its nonce. The wia would be provided during the discovery using dpop. If dpop is discouraged rfc7521/client-based-attestation can be used.

In addition I would protect the holder from providing its wia to an untrusted party, this Is an implementation choice: establishing the trust before sending any information. Using the .well-known/jwt-issuer with a x5c within It or a federation entity discovery this can be achieved easily

During this discovery process (don't say engagement 🤣) we have a sort of mutual auth, where each party established the trust and exchanges their verifiable Key before any protocol specific transaction start, then It would be the same for issuance and presentation, the same endpoint with the same scopes and agnostic to anything might happen later on. Then It would reduce the implementation costs and propose a common and seamless way for different scenario.

An example: an entity that Is both a CI and an RP, as the (Q)EAA issuer. It would use the same Discovery endpoint just a single time, before presentation and issuance. If the entity Is the same, the trust is established once per transaction and the discovery may not happen twice from both the parties

alenhorvat commented 10 months ago

The process can be called a handshake, and the endpoint can be /syn ;)

There are several possible scenarios:

Even though we're talking about wallet/client authentication, authentication can be bidirectional, which leads to an mTLS-like exchange.

-> Issuer/Verifier offer/introduction (qr/redirect) -> issuer/verifier metadata discovery via .well-known -> handshake -> "active (nonce approach)": it can begin with wallet asking for issuer/verifier credentials, or just a nonce, server response can be VC/nonce. Wallet responds with wallet instance attestation + wallet metadata -> "passive (jti approach)": wallet learns about the issuer/verifier credentials from the location specified in the introduction; wallet signs the request and puts the wallet attestation either in the signature header (if jades approach is used) or in the payload; wallet also presents the wallet metadata at this point -> "combined": server asks, wallet performs a passive VC discovery (discussed in this thread) -> continue with the flow (authz request/response)

Note: the handshake part can be executed using OID4VP flow or SIOP. But also new endpoints can be introduced. If they are, please consider the possibility of mutual authentication (wallet must present VCs, issuer/verifier must present VCs, in both cases VCs are cryptographically bound)

@peppelinux I'd like to discuss further the federation and WIA since I believe there are two models possible (not in this thread since it's out of scope)

peppelinux commented 10 months ago

@alenhorvat for anything about federation we must use bitbucket because federation wont be migrated to GitHub until the next implementation draft

sietseringers commented 10 months ago

What if that is not the case, for example if the respective key is managed in a remote HSM? In this case, the proposal causes another network roundtrip to the remote HSM on top. Would that be a problem?

That would indeed cause extra HTTP roundtrips to our HSM.

However, probably most or all issuance sessions will involve issuers that the wallet is already aware of, using a trusted registry. Suppose we store in such registries whether or not an issuer requires a nonce, then if the wallet contacts an issuer of which it knows that it requires a nonce (which will be all of them in the dutch case), then it doesn't need to send an actual valid pop, because the issuer is going to respond with an error message (and the nonce) anyway. So we could just send a dummy pop instead so as to obtain the nonce. Not very pretty perhaps, but it would save the extra roundtrips.

alenhorvat commented 10 months ago

Informational:

Note that there's another option (we implemented 3 years ago in EBSI for a fully decentralised network of authZ servers without shared storage or sticky LB): Authenticated Key Encryption (we implemented AKE2). Long story short: The request is signed and contains an additional public key for encryption, the response is signed and encrypted according to AKE2.

pro:

con:

Attack is only possible if the adversary gets access to the device; then it's game over anyway :)

For details see: https://toc.cryptobook.us/book.pdf section 21.3 21.2 is also interesting to read to learn about the possible attacks when implementations are not correct

IMO, the cost of managing jti/nonce is probably lower than asking for a client-side decryption. But note that the security level can be higher (depending on the decryption key security).

paulbastian commented 10 months ago

If we have so much trouble with the nonce, I would like to step back one second and ask how much we lose if we do attestation at the token endpoint? It's nice to do at auth request and it definitely fits best there but is it strictly necessary?

pmhsfelix commented 10 months ago

+1 for using nonces with a design similar to what is used in DPoP, where the server responds with 400 and a challenge with a new nonce. Also note that jti does not guarantee freshness and using client generated timestamps has problems, even for non-attacking clients (e.g. devices with incorrect time-zones or daylight saving settings).

peppelinux commented 10 months ago

the problem of misalignments of datetime between servers involved in interop and digital signatures would represent a security risk with higher priority than jti, considering for instance the impacts on evaluating iat, exp

jti doesn't bring any scope of freshness but just the requirement to keep it persistent until the JWT is not expired

while jti impacts too much on the security and then the costs of the implementations, nonce can be provisioned at a first stage without any persistence by encrypting it with a symmetric key. That's why we found an agreement of the nonce, even if I would keep the nonce endpoint optional/recommended because there may be implementations that correctly implements jti as a replay attack mitigation/prevention, according to RFC 7519

alenhorvat commented 10 months ago

That's perfectly fine. If a new endpoint is introduced, why not simply request one or more VCs, where one of them is a wallet credential? The user can send a signed authZ request (with wallet configuration) and the AS responds with a VP request. Is the wallet instance attestation any different from a VC?

paulbastian commented 10 months ago

@alenhorvat We started earlier this year with wallet attestation as a VC but deliberatly chose not to do so.

  1. the concept is usable for a wider audience
  2. OAuth does not know VCs
  3. OpenID4VCI should not make a primary choice on a VC format imo
  4. its a technical tool and must be treated different from "normal" VCs
peppelinux commented 10 months ago

I think that the roles client/AS or the relation between who is the client and who's the AS, in the wallet ecosystem are more ... relative!

during the issuance the wallet instance is the client and the CI the AS, while in the presentation the RP is the client and the wallet instance is AS

this would force us to provide the wallet instance for different scope in different ways, while for the wallet ecosystem I believe that it would be better define a wallet discovery endpoint that could be used in the same way by wallet-instance, RP and CI, in all the flows and use-cases, as commented here: https://github.com/openid/OpenID4VCI/issues/71#issuecomment-1719958796

alenhorvat commented 10 months ago

@paulbastian with "VC" I'm not referring to a specific format :) I'll not use VC anymore. I don't have any "format" preference. See my comment above on multiple possible formats for wallet attestations ;)

@peppelinux , yes, the wallet can play both roles, depending on the scenario.

I believe there are 2 elements: discovery (metadata/configuration), and authentication.

If my understanding is correct, the issuer or verifier may ask the wallet instance to authenticate, and the proposal is to use the wallet instance attestation. In both issuance and presentation cases, when the wallet "requests access to the AS" (in one form or another), the AS may request a Wallet Instance Attestation, authenticate the user itself, or both.

I'm asking if Wallet Attestation is different because, during the issuance and presentation, the issuer/verifier might ask for additional tokens, so the token presentation flow should be supported anyway.

If it is from a design or security perspective important to split authentication tokens (Wallet Instance Attestation) from attestation tokens (I'm a student), then this should be reflected. If that's the case, the most appropriate place would be to start wallet authentication after calling the authorisation server

This is why I'm trying to understand the design proposed/discussed and the rationale behind it.

Informational: In the high LoA use case, eidas v1, we achieved this by simply authenticating the wallet instance using an x509 cert issued by the wallet provider (who is also a QTSP).

danielfett commented 10 months ago

Here are my notes from today's call on this topic:

tlodderstedt commented 10 months ago

Using attestation for the PAR means that the request was created by an attested party; it does not mean that at that moment I'm talking to the correct party.

Could you please explain what "correct" means and whether that statement also applies to the token endpoint?

peppelinux commented 10 months ago

In this flow a PAR request is used https://italia.github.io/eudi-wallet-it-docs/versione-corrente/en/pid-eaa-issuance.html#detailed-flow

step 6 (PAR request) and step 14 (token endpoint request) uses the wallet instance attestation as client_attestation (PAR) and client_assertion (token endpoint)

we cannot use it just on the token endpoint because this happens after the authorization and the user authentication, while within the authorization phase we need to attests the reliability of the wallet instance

the wallet ecosystem assumes that the wallet instance attestation provides the proof of reliability of the wallet instance, it is signed by a trusted third parties and it gives also evidence of the wallet capabilities (not just cnf.jwk)

according to the flow "pid-eaa-issuance.html#detailed-flow" the AS has the proof that it is always talking with the same party

alenhorvat commented 10 months ago

What is the role and purpose of the wallet instance attestation?

AS should get info about the wallet