 Please welcome our next speaker, Yuting Li. She's going to be speaking to us about cats, a tale of scalable authentication. Take the way, Yuting. Thank you. Okay, thanks. I'm Yuting, a software engineer building security infrastructure at Facebook. I'm gonna talk about our crypto auth token authentication protocol my team and I built to support authentication at scale. They're dubbed cats for short, because crypto auth token, but it's mainly just to make puns easier. So an overview of what I'll be talking about. I'm gonna be talking briefly about token authentication, just the basics, and then our different authentication models that we have, a pre-cat world and our infrastructure, the life cycle of a cat in a small use case, and then a scaled up version, and the post-cat world, the utopia we dream of. Okay, so about token authentication. This is like the most brief and small example of how it works. So say a user wants access to a very specific resource on a backend, but for some reason they don't either trust the backend service or they don't wanna send their full credentials over the wire again and again. So what they could do is send their credentials to an authentication service, which will authenticate their credential and give back a token. And then the user can then use this token to authenticate to the backend service. They can either keep this token to use to re-authenticate or this token can be single use. So that's like there's a lot of different variations. Cats is mainly a single use token, so that's what we'll be speaking with. So why do we use tokens? I touched a little bit upon this when I talked about the protocol. We first probably don't wanna trust the backend service entirely to send our full credentials to them or we want better security. And there's a question mark because if the token gets captured depending on the token protocol the token can still be reused, so security. Also the tokens can be made to be short living so that it really depends on how you want your tokens. So that's why we use tokens. And now on to our authentication models that we have. So there are mainly two different use cases we have. This is by no means an exhaustive list, but these are the most basic ones and the ones we see most. So the first one would be a user making a request to a web server which then makes a request to a backend service for some resource for the user. The backend service wants to make sure that the user is really who they say they are and not just someone pretending to be them. And other instance would be a backend service authenticating to another backend service but they want authentication per request. So this means we can't solely rely on TLS since TLS is per connection and not always per request. So both of these cases have the case of some entity be it a user or like a service wanting to authenticate to some other entity. You're probably questioning me at this point like there's other protocols that exist. Why are you making cats and why are you building something new? Well, we'll talk a little bit about the other ones that exist out there and why we chose not to use them a bit later, but we'll walk through what a precat world looks like and our infrastructure to kind of see the motivations to build cats and why cats are required. So in a precat world, this is what it looked like. Okay, cute laughter, but okay. So a precat world, we have lots of different types of tokens and these tokens are used for authenticating different entities. So say a user wanted to authenticate to a backend service that might be a different token protocol than like a backend service authenticating to another backend service. There was even different tokens for the same type of entity just depending on how they're trying to authenticate. So that's not great. We want to reduce the amount of token libraries we have to keep up, so that's one thing. Second thing was some of these tokens weren't very well-scoped in the sense that the tokens, if they were captured, they could be replayed to many different parts of our infrastructure. There isn't really a limit to where they could be used as long as that backend service received and used that type of authentication protocol. And also most of these tokens relied on public key cryptography. While this was usable in the past, we're getting more and more use cases where the QPS of the service simply will not allow for public key verification on every request. We needed a protocol that would be able to support millions of requests per second, but that didn't exist in a pre-cat world. Which brings us to our last point. Our authentication was not per request and we wanted it per request. So given all of these issues, we wanted to build something that would scalable, satisfy our use cases, and flexible to our many authentication flows. So things that is a must for this new authentication token are, A, we want to narrow down the amount of token libraries, which means that we want this token protocol to be usable for all different entities because that's the main motivation for having all these different libraries. B, we want, did I say one or B? Okay, B, we want them to be scoped down and flexible. We want them scoped down so they can't be replayed to multiple parts of our infrastructure. We want them to be flexible because we don't want to have to maintain all these different token libraries for all the different authentication use cases. And lastly, we want it to be scalable. So this is a tale of scalable authentication after all. We need it to scale. Given the large amount of users and user requests we want to authenticate on the order of magnitude hundreds of millions of requests per second, we want a token to authenticate every single one of these requests. So now we have our requirements. We can take a look at our infrastructure and why we build cats the way we did and why cats were necessary. This is what our infrastructure looks like. We have many, many, many, many, many, many user facing web servers, which then, which receive requests from users and then send out requests to other backend services to do different things. These backend services could be talking to other backend services either directly or through proxies. An important thing to note here is that these backend services have usually a super high fan in, but the amount of backend services is much, much, much, much, much less than the amount of users we have and user requests we have. So this is especially important for cats since we rely on having many more user requests than backend services that need authentication in order to not overwhelm our infrastructure. In addition to this, we also have different levels of trust within our backend infrastructure. Our goal is to limit the amount of trusted machines and to not give equal trust to all machines. So this is why we mainly use token authentication protocols. This is reason number one from the token slide. This is also to prevent against an internal threat model and out of our infrastructure, our web servers, which is the green ones, have the least amount of trust. We try not to trust them with any persisted long living credentials. Then there are the backend services that we trust the most. These services do thing like manage our keys as well as mint credentials. They mint service credentials in the form of certificates or user credentials in the form of sessions. And then we have everything in between. These are normal backend services and proxies that we trust a little bit more than web servers, but definitely not enough to allow them to create credentials or have access to persisted credentials. So CATS enable us to maintain the small root of trust and helps delegate trust to less trust in machines. Also within our infrastructure, we have a concept of identities. So these identities mean like different entities have a type as well as like a name to denote which specific entity is. And we rely on the trust that is already established for these entities, meaning that each entity is only trusted if they can prove they are who they say they are using an identity proof. And this comes in the form of certificates for services or sessions or other credentials for users. So now that we have some background on the infrastructure, the requirements and what we want, let's look at the life of a CAT and how CATS fit into our infrastructure. As a brief overview, this is what CAT authentication looks like. We have a user wanting to authenticate to a backend service, extremely similar to any other token protocol at first glance. A user will send credentials to the authentication service which will dub the CATS service from now on. The CATS service validates and then sends something back and then the user is able to send a CAT which is a token to the backend service and then authenticate themselves as well as the request. So what makes CATS different in this case is the thing that the CATS service sends back. To explain what that is and how it's used, let's look at the life of the CAT from the top. So we say a user wanting to authenticate to a backend service but then there's obviously a web server sitting in between but we'll just say user to backend service for simplicity. So before we start authenticating any CATS, the backend service does need to do one thing. It needs to ask the CATS service for its verification key on startup. The service uses its service identity along with its certificate to authenticate by a TLS to our CATS service to prove that they are the backend service, they say they are. In our infrastructure, TLS is bidirectional so the client also authenticates to the server. So after they authenticate, they will receive a verification key also known as a once-derived key. What this key is, I'll explain a little bit later once we see all the keys in action. So now they have their key, we can start looking at the life cycle of a CAT. So when a user makes a request, we know that the specific backend service requires authentication with CATS. So to create a CAT, we need to first get the user to send a request to our CATS service for the user's CAT creation key. The user will send along its identity proof which is its credential to prove they are who they say they are and which backend service they're trying to authenticate to. So two pieces of information. The CATS service validates the user's credential and sends back the creation key. So this is a twice-derived key. So this is the mysterious thing that the CATS service sends back and really what makes CATS different. So let's take a dive into what these CATS keys are. So the verification key that I spoke about earlier that the backend service gets, it's actually the result of a pseudo-random function applied to a CAT root key along with the backend services identity. This creates the verification key that the backend service will get. Okay, and then another derivation is applied onto that key again using the same pseudo-random function but this time with the verification key and the user's identity. So we get the creation key which is the twice-derived key and now it makes sense why it's twice-derived. And then the user gets this. This means not every user and backend service pair will have a unique key but the verifying backend service will have the ability to re-derive these keys. And this is why it's so important that we have many, many more requests than backend services verifying the CATS. So this is the secret sauce that makes the CATS different. It's the key derivations. It allows us to not need pre-shared keys between every user and backend service pairing allowing us to share keys pretty much on the fly. So back to regular schedule programming, the user gets their creation key. Now what? Now we can locally create our CAT and we no longer need to interact with the CATS service. So the user can create the CAT. The CAT contains some information like the user's identity, the backend services identity, creation time exploration payload and an HMAC over all of this information using the creation key that we just got back from the CAT service. So this allows for a pretty flexible CAT. The scope of it can either be really large or super scoped down by having additional data in the payload. And it's completely up to the adopters of these CATS to show how scoped down they want their authentication to be. So the CAT gets sent along with the request to the backend service. When the backend service receives this CAT, the first thing they need to do is re-derive the user's CAT creation key. Because they have the CAT and they have the user's identity, they can apply the same pseudo-run function we talked about earlier onto its own verification key and the user's identity to get the creation key. With the creation key or the twice derived key, the backend service validates the CAT and the CAT's job is complete. So there are a few things that the CATs depend on that are already built into our infrastructure. It relies on the trust given to all the entities by our most trusted services. The services use certificates to authenticate via TLS and users use sessions to authenticate themselves, sessions or other credentials. Both parties are required to give their identity proofs in order to get their keys. So now we've talked about CATs in a small scale. Let's scale it up to multiple users making multiple requests throughout the day to the same backend service. I would put more backend services because that's more like real life but then it just gets kind of messy. So what the user first needs to do when it wants to authenticate to a backend service is to ask the CAT service for its CAT creation key. So it would normally look like this. No, give me my CAT creation key. But actually you only need to make one request for the key from our CAT service first and then the subsequent request will query the cache for the key and the key expires within the cache. Cache lookup is much faster than the query to our backend service. The entry is encrypted with a secret key that only the users has that is maintained via their session. So that's a different story. And this makes for great scalability simply because each user will usually want to authenticate to the same backend service multiple times throughout the day. At the same time, this is all happening, the backend service will query the CAT service for their verification key, but they'll only need to do this once on startup and maybe every so many minutes. And this key allows for the backend service to locally rederive all the user's creation keys. So the users have all the keys, they can make their own CATs, they send it along to the backend service, each of these arrows has a CAT in it. The operation for derivation and verification of the MAC is much faster than public key verification, hence much more scalable in our case. So this is just one example of how CATs are used as the most complex, but we have other one backend service that's indicating to another. So now that I've talked a little bit about how CATs work and how they live in our infrastructure, you're probably looking at this and wondering, you Ting, this looks a little bit like JWTs and a little bit like Macarons. Why not use existing things? Why are you building something new? Well, let's take a look at these. So a CAT, a JWT and Macarons walk into a bar. I don't have a punchline for that, so if you do, you can let me know and I will present it the next time I speak. Okay, so JSON web tokens. What the user needs to do to authenticate to a backend service is to send their credentials to authentication service. And then the authentication service will send back a JWT, which they can then use to authenticate to the backend service. This looks pretty similar to the CAT authentication one, except for the fact where the user gets back a token instead of a key. This means that if we want every request and the payloads of those requests to be authenticated, the user will need to query for a single token from the authentication service on every request. Usually that would be okay if the QPS wasn't too high, but we need a scalable authentication system that will allow for millions of requests per second and querying a service for one of these tokens every single time is just not scalable. So with CATs, users receive the keys instead and is able to mint the token themselves for every request. Since we're able to cache the key, we only really need to talk to the CAT service once every hour or so. Like I said before, subsequent requests make, they can use the same key since the key is per user per verifier. So because of this limitation, we decided not to use JWTs. Okay, what about macarons? This picture doesn't really tell you what macarons are except that they're a token used for authentication. So macarons uses HMAC to scope down tokens with certain caveats that a request would need to fulfill in order for the token to be valid. So CATS seems similar to macarons at first because of all the key derivations and all the HMAC layering. But macarons use key derivations to create the token itself but CATS use the same technique to establish a shared secret between a user and a back-end service. This technique allows us to create a token that works for our infrastructure given our different routes of trust. So now we're finally at the post-CAT world. This is what a post-CAT world look like. Q laughter again, but okay. Post-CAT world, we have a much more flexible token for authentication. This means that all sorts of entities that live within our infrastructure can use the same token protocol for authentication. There's some token libraries that we can deprecate and which allows us to maintain many much, much less token libraries. So we're constantly upgrading this to fit with more use cases to hopefully deprecate more. So now we have a much better scoped token as well. It's better at preventing replay attacks since a token cannot be used to authenticate to any other entity other than the one it was already made for. It's also much more flexible for scoping dependent on what the specific use case of the token is for. All the fields inside the token, such as the timeout, the payload allows for either a really scoped down and short-lived CAT or a not scoped down and pretty long-lived CAT. And we finally have authentication for every request. Since they're super lightweight and efficient, then more efficient than other tokens we have, we're now able to produce authentication for every request and bind the tokens to these requests. It allows for better security overall. Our messaging infrastructure actually uses this to authenticate every message sent and that's about a few million requests per second and they're not the only ones who are using this CAT protocol. They're also much faster due to their symmetric nature. Previously, we use a public key crypto but now we use private key crypto. The verification is hundreds of times faster. This lowers our GCP spent overall on authentication and allows authentication in more places. So in conclusion, CATs have helped make large-scale authentication more efficient. It has also helped make our authentication more secure and it has helped me get a prasheen next to my desk. Questions? All right, give it up for you, Ting.