I think that is probably secure, although I’d like to see a formal proof of correctness like the ones for macaroons in https://cs.nyu.edu/media/publications/TR2013-962.pdf. There are often subtle details to these things.
Using chained public key signatures in this way goes back to SDSI (http://people.csail.mit.edu/rivest/sdsi11.html#secoverview) and has more recently been used in Vanadium under the name “blessings”: https://vanadium.github.io/concepts/security.html It has some nice properties, but it is very expensive in terms of CPU costs and size of tokens. It also requires every hop to add a signature even if they’re not adding any new caveats. This is very secure but can add a lot of overhead, and of course means that all parties have to be aware of this format/protocol. I still think it’s much easier and more efficient to just use HMAC-based macaroons in most cases. You can upgrade them to PoP tokens by appending a “cnf” caveat (eg for mTLS). And you can still do things like the “phantom token” pattern where a macaroon-based “by ref” token is received by an API gateway, introspected, and then replaced with an equivalent (but short-lived) signed JWT for consumption by backend microservices. — Neil >> On 29 Nov 2019, at 22:13, Richard Backman, Annabelle <[email protected]> >> wrote: > > > That is the easiest way to let the RS verify the macaroon on the assumption > > that the RS is trusted. I’m not aware of an alternative for asymmetric > > crypto when the RS is untrusted other than using the signature-based > > macaroon variant or having per-RS keys. > > It occurred to me that my previous example of how to do layering with JWTs > was needlessly complicated. You can prevent removal of layered constraints by > constraining each inner layer to require a wrapper. Consider a "foobar" claim > that specifies a public key and indicates that the token must be presented > wrapped within a JWS signed with the corresponding private key. The wrapper > JWS may introduce additional constraints, and may or may not permit the > recipient to present the access token to others, depending on the value of > the "foobar" claim. For example: > > The AS generates an access token, with a public key registered by the client > as the value of the "foobar" claim. This registration could’ve happened via a > dev console, dynamic client reg., or as part of the token request. > <at_0> = JWS(<AS private key>, { > "iss": "as.example.com", > "client_id": "...", > "user_id": "...", > "scope": "a b", > "exp": <now + 1 hour>, > "foobar": <client public key> > }) > > To call rs1.example.com, the client wraps the token in a JWS signed with the > client’s private key. They further restrict the scope and expiration time, > and authorize the RS to use the token with other RSes by setting the value of > the "foobar" claim to the RS’s public key. > <at_1> = JWS(<client private key>, { > "token": <at_0>, > "aud": "rs1.example.com", > "exp": <now + 5 seconds>, > "scope": "a b", > "foobar": <RS1 public key> > }) > > To call rs2.example.com, rs1.example.com wraps the token in a JWS signed with > the RS’s private key. The RS prohibits rs2.example.com from further use of > the token by setting the "foobar" claim to null. > <at_2> = JWS(<RS1 private key>, { > "token": <at_1>, > "aud": "rs2.example.com", > "scope": "b", > "foobar": null > }) > > Similarly, the client can call rs2.example.com with a token that is > restricted from further use. > <at_3> = JWS(<client private key>, { > "token": <at_0>, > "aud": "rs2.example.com", > "exp": <now + 5 seconds>, > "scope": "b", > "foobar": null > }) > > > This pattern allows for layered constraints, local introspection, and local > validation. The requirements (that I’ve identified) are that: > The client must register a public key with the AS (this could be done during > the token request). > The AS must know whether or not to give the client a plain bearer token or a > token with the "foobar" claim (presentation of a key possession proof in the > token request could be enough). > Any recipient that wishes to validate the token must have the public key for > the AS. > Any recipient that wishes to add a layer must have a public key that is known > to its callers. > Any recipient that performs local validation must understand the meaning of > the "foobar" claim. > > I haven’t thought too deeply on this so I wouldn’t consider the idea fully > baked, but I’m curious to hear your thoughts on it. > > – > Annabelle Richard Backman > AWS Identity > > > From: Neil Madden <[email protected]> > Date: Wednesday, November 27, 2019 at 11:43 PM > To: "Richard Backman, Annabelle" <[email protected]> > Cc: Brian Campbell <[email protected]>, oauth <[email protected]> > Subject: Re: [OAUTH-WG] New Version Notification for > draft-fett-oauth-dpop-03.txt > > On 27 Nov 2019, at 20:30, Richard Backman, Annabelle <[email protected]> > wrote: > > > That is true, but is IMO more of a hindrance than an advantage for a PoP > > scheme. The very fact that the signature is valid at every RS is why you > > need additional measures to prevent cross-RS token reuse. > > The other methods you mention require their own additional measures in the > form of key exchanges/handshakes. And you still need to prove possession of > that shared key somehow. > > This is true. The difference being that the derived key can then be reused > for many requests. Because the key derivation is cryptographically tied to > this context the RS can’t replay these symmetric tokens anywhere else.. > > In some cases, “derive a shared key and encrypt this blob” is easier; in some > cases “sign this blob declaring your audience” is easier. > > The ECDH scheme does challenge-response to ensure freshness. This was > designed to match the anti-replay measures in the DPoP draft but without > requiring the server store any state. If you don’t need replay protection (if > TLS is enough) then you can indeed just sign the audience, or for ECDH you > can do completely static ECDH between the client’s private key and the RS’s > public key to derive a shared key that is the same for all time (until key > rotation). But in that case you may as well just return a symmetric key > directly from the AS... attached to a macaroon, say. > > > > > The easiest way to use macaroons with asymmetric crypto is to make the > > macaroon identifier be an encrypted random HMAC key that the RS can decrypt > > (or a derived key using diffie-hellman). You can concatenate multiple > > encrypted keys for multiple RSes. Alternatively in a closed ecosystem you > > can encrypt the random HMAC with a key stored in a KMS (such as AWS KMS) > > and grant each RS decrypt permissions for that KMS key. > > Is the “random HMAC key that the RS can decrypt” the root key used to > generate the macaroon? If so, how would you prevent one targeted RS from > using the root key and macaroon identifier to construct an arbitrary macaroon > for replay against another targeted RS? If not, how does the targeted RS use > the decrypted “random HMAC key” to validate the macaroon? Is there a paper on > this approach? > > That is the easiest way to let the RS verify the macaroon on the assumption > that the RS is trusted. I’m not aware of an alternative for asymmetric crypto > when the RS is untrusted other than using the signature-based macaroon > variant or having per-RS keys. > > I’m not really a fan of purely signature-based JWT access tokens because > those tokens often contain PII and so should really be encrypted to avoid > leaking details to the client (or anyone else if the token does leak). This > came up in the discussion of the JWT-based access tokens draft, which is why > I proposed https://tools.ietf.org/html/draft-madden-jose-ecdh-1pu-02 for use > in that draft. But if you’re doing encryption then you’re already down the > path of having per-RS access tokens (and keys) - the compact encoding of JWE > only allows a single recipient. > > > The KMS approach is just symmetric crypto mediated through a third party (and > has the same centralization problem as validation at the AS). > > > Clients can then later start adding caveats…, while RSes still don't have > > to make any changes…. > > DPoP only effectively prevents cross-RS replay if all RSes implement it, > > otherwise the ones that don't are still vulnerable. > This is because macaroons bake the proof into the “bearer” token (which is no > longer really a bearer token) in the Authorization header, whereas DPoP puts > it in a separate header. > > That’s not the only difference. The other is that the AS does the validation. > If the client appended the DPoP claims to the access token and signed the > whole thing, and then the RS took that and sent it to the AS introspection > endpoint to validate it, then that would have the same advantage of not > requiring any changes at the RS. > > But if you do this then there’s no longer any reason to use public key > signatures because the client and AS may as well agree a shared secret. (The > AS can always impersonate a client anyway). At which point we’re basically > back using macaroons. > > > draft-ietf-oauth-signed-http-request is another way to do this that doesn’t > rely on macaroons. > > > Your previous point was that they require "non-trivial work to use ... and > > require developers to learn a new token format". > By “non-trivial work to use” I was referring to work required from the > working group, that I did not feel was being acknowledged. > > Do you believe it’s a disproportionate amount of work compared to any other > draft the WG works on? > > > Looking back over the thread, I think my objection stems from you referring > to macaroons as an “access token format” when they’re really an applied > cryptography pattern. The “format” part would need to be defined by the > working group. For what it’s worth, I think it’d be interesting to explore > if/how the pattern could be applied to the JWT format, or what tweaks would > be necessary to make it work. If we could describe a way to create macaroons > that reuse the existing work on JWTs, that would be pretty cool. > > There are existing interoperable macaroon libraries right now that define a > common format [*]. Unless there was a compelling reason not to, I’d hope we’d > just standardize that. > > [*] Actually they’ve gone through a couple of iterations. I believe the > “libmacaroons V2 binary” format is what most now use. > > > > > That burden is significantly reduced when developers can just add a > > dependency and call a one-liner to add a caveat. > Libraries can certainly reduce the amount of work required by developers (and > here I mean client developers, RS developers, AS developers, and OAuth client > and server library developers), but come with their own concerns (e.g., > platform availability, licensing, maintenance and reliability, etc.). It > becomes one more dependency that developers have to consider. > > I’m not really sure what your point is here. *Any* new addition to OAuth has > to be implemented. Either that’s done with a library or you write your own. > > — Neil
_______________________________________________ OAuth mailing list [email protected] https://www.ietf.org/mailman/listinfo/oauth
