2022.04 Vol.1

Bit of caveats

Macaroon Third Party Caveats

Continuing on the token theme of the last few posts. A quick recap on tokens: a token authorizes certain actions and can be trusted because it contains a signature which satisfies holder-of-key proof.

Macaroons are tokens with defined structure. Instead of the token being one long string, toss whatever you want in here, it is broken up into defined parts. Its not a ton of structure, it essentially boils down to: an ID, caveats (rules), and a signature. There is also a location field, but its not used very much. So not a lot of structure, but enough for protocols to be built on top of them.

The signature is pretty straight forward. It locks in the contents of the ID and caveats. Macaroon signatures are actually generated through chained HMACs. It starts with a root private key and the macaroon ID to generate the first signature. After that, every time a caveat is added the signature is re-calculated using the old signature as the key. This just means only the root private key holder can verify the macaroon. Doesn’t really effect what I want to dig into here though: third-party caveats.

Caveats are the “rules” of the macaroon. They are actually just strings, and it is up to the service generating the macaroon to define the types of caveats and enforce them. An example could be an expiration caveat which expires the macaroon at a certain time. It could be a string with a key and a timestamp (e.g. expire=1649876417). When the service is passed a macaroon with an expiration caveat, it checks the timestamp against the current time and only allows the request if it isn’t expired. This is an example of a first-party caveat. The caveat is defined and enforced by the macaroon issuing service.

Macaroons also have a protocol for a different service (a third party) to enforce a caveat. This is called a third-party caveat. It is straightforward for a service to check a first-party caveat, but how do they check if a third-party caveat? With macaroons! If a client wants to pass a macaroon with a third-party caveat to a service, the client must first go get another macaroon which satisfies the caveat. It then passes both the original macaroon plus this new one, called a discharge macaroon, to the service. The service follows a protocol backed by cryptography to check if the discharge macaroon satisfies the caveat. If it does, the service can assume the third party has enforced the caveat.

So, uh, what is that protocol? Third-party caveats have more structure than first-party since more than one service needs to understand them. They actually look a lot like a macaroon: a location to point clients to who they have to ask for a discharge macaroon, an vID that holds a new root key (more on that in a sec), and a second id called a cID which also holds the same new root key. vID and cID are both encrypted so this new root key isn’t exposed, however, they are encrypted with different secret keys. The vID is encrypted with the macaroon’s signature before the 3rd party caveat is added. This means only the issuer can decrypt the value. The cID is encrypted with a public key of the third-party service, so only the third-party service can read it. So the vID allows the issuer to read the new root key, while the cID allows the third-party service to read it.

That new root key is the root key for the discharge macaroon created by the third-party service. The issuing service defines it, passes it to the third-party securely through the original macaroon itself, and then verifies the discharge macaroon with it. The issuing service doesn’t even have to hold on to the new root key since its baked into the macaroon.

+----+
| ID |
+----+---------+
| exp=12345679 |
+--------------+-----------------+
| tp.com:ENC(Sig2,RK):ENC(PK,RK) |
+------+-------------------------+
| sig3 |
+------+

A macaroon with a first-party expiration caveat and a third-party caveat. Sig2 is the signature after the first-party caveat is added, RK is the new root key, and PK is the third-party public key

Third-party caveats can be extended in two ways. The issuing service can add some rules into the cID for the third-party service to validate. And in the opposite direction, the third-party service can add first-party caveats onto the discharge macaroon for the issuing service to validate.

Curious to see these things out in the wild.