 Alright, next up please give a warm welcome to Scott Arsazowski, he's going to be giving a talk called Mill Way Jose, Designing Cryptography Features for Mirror Mortals. Welcome Scott. Hey, thanks for having me here. As she said, my name is Scott Arsazowski, you might know of me from some of my open source projects online through Paragon Initiative Enterprises. You can blame me for getting lip sodium into PHP 7.2 and for rewriting it in PHP. My Twitter handles Cypher Coder with PHP in it and sometimes I live up to that name. Which kind of goes into the whole point why we're here today. Everybody in security knows that you should not roll your own crypto, right? That's common knowledge. It's actually repeated a lot on the internet and everyone knows it. It's common wisdom and there's good reasons for it. If you follow the work of Bruce Schneier, you know that amateurs produce amateurs cryptography. It's very difficult to get right. You might think it's secure and then find out you have a timing side channel. Even people who have been experts in the field for years make mistakes. And the problem is that the buck usually stops there. Developers are told don't build your own. But that's it. They're just told not to. So when people say, okay, instead of rolling my own, what should I do? They're often told something like this. And I copied that from my friends at Latakora. So most developers work, they do line of business software development of some form. Crowd apps, moving data from point A to point B. Sometimes they do really cool stuff with machine learning. But usually if they need encryption it's to protect data they're storing. So they have business goals. So they just need to solve problems. And they need easy to use tools that will just solve the problem and get out of their way. Without exposing them to, you have to select this particular configuration for it to be secure and you have all these options to research and learn about. And let's take a look at one really egregious example of this. It's called Jose, the JavaScript object signing an encryption standard. Some of you might be familiar with it. It's a family of standards with several RFCs. Most of them relate to authenticated tokens either signed using public key cryptography or secret key cryptography or they're encrypted. People use them for all sorts of things. They're supposed to be used for short lived claims. Like if you have a third party download server and you want to say, yes, they can download this file once. That's the envisioned use case. People abuse them for like stateless sessions and all that. And it was never really designed for that use case in mind. So if you look at the RFC, you can see a lot of text that explains pretty much what I just said. The important thing is that a JWT pronounced jot uses either encryption or a signature algorithm. So consequently, if there's a JWS or JWE vulnerability, it probably applies to your JWT library. And there's implementations of JSON web tokens in pretty much every language. Most developers use them whether they realize them or not. And here's what they look like. Now you have a header, you have the message, and then you have your signature. Now let's take a look at the JSON web signatures real quick. You have this thing called an ALG header, which was the red in the slides up here. That tells you what algorithm you're using and what type it is. There's different options. You have HS256, you know, HMAC SHA256. You have RS256, RSA with PKCS1V1.5 and SHA256. And you, of course, have none. This is the textbook bad API design mistake. It's mixing symmetric and asymmetric cryptography in the same context. And unfortunately attackers are the ones who provide the tokens, which contains all this metadata. So they can choose the header. And so, you know, this sounds dangerous. And it actually gets worse than that. Because in the RFC that defined JSON web tokens, it says the header parameter must be present and must be understood and processed by implementations. And if you've ever worked with software developers, you'll know that that means obeyed, right? They don't parse standards the same way that people that write them do. They say, oh, it must be processed and understood. That means we have to adhere to whatever a token says. And this has led to vulnerabilities that we've probably all heard of before in JSON web token libraries. JSON web encryption is now much better. Look at these key encryption options, right? This is for encrypting a key to the later on encrypted token body. Now, if you noticed, we have three asymmetrical ones and one symmetric key option. This was clearly not the result of careful designing of use cases. This was a design by committee of trying to solve the 99% problem rather than the 50% problem at day one. Furthermore, the ECD-HES, which is looked at Curve Diffie-Helman encryption, something like that. The way they specified it in the Jose standards opened the door to invalid curve attacks, which allow an attacker to keep feeding you tokens and then steal your private key. The reason why this is the case is that they say it must specify the X and Y coordinate of your public key point. What it should have done is either only specified one of the coordinates and a signing bit or make point validation a explicit part of the standard. And they did neither of these. So that's some of the problems built into the Jose standards. There's a more general problem. So they say, okay, let's give developers options. My friend Taylor Hornby wrote a blog post about this where he called that reasoning by Lego. And you see a lot of this in the internet, especially early 90s and early 2000s crypto designs, like people who do Mac and encrypt, not Mac then encrypt or encrypt then Mac, people say, well, we have a secure Mac function and we have a secure encryption function. This should be secure. But it's not the two pieces that make the protocol secure. It's also how it's joined together. So to put this in layman's terms that a developer would understand. You're building a brick wall, right? But instead of putting bricks in lane mortar and then putting more bricks and more mortar, instead you have this mesh with empty space of just mortar in place. And you're supposed to slide the bricks you prefer into place. You know, the architects who designed this brick wall insist that it gives you more flexibility. Would you trust that wall to hold up the roof? That's how cryptography libraries and standards have been built historically. TLS is no exception, although 1.3 is a hack of an improvement. So to pwn or jot to pwn, there are a lot of ways for the standards to go wrong. We've seen some of them in CVEs over the past three years. We'll probably see even more. There's a lot of RFCs that I haven't even had time to delve into the specify weird cipher modes. And then there's implementation security risks and user error. Somebody could just say, okay, I'm gonna make the, you know, JSON web token key their username. And then I'll use that for HMAC validation. That would be a user error. And then implementation specific would be a library that like has a timing attack vulnerability in the, you know, Mac validator. Whenever I would raise these criticisms of the standard to the people who design and advocate for Jose libraries, or Jose, the response was always, well, you should use one of these libraries. You should use this one. We've vetted this one. Don't use the other one. That doesn't really solve the problem. It shifts the blame from the badly designed standard onto the libraries developers and their users, the people who are trying to choose a library. And if we want to build secure systems, this is an anti-pattern. That bears emphasizing. And there's an entire series of anti-patterns at play here. So the people who design standards say, let's give users lots of choices. In case one of them turns out not to be a good one, then we can just deprecate that down the line, which never happens. You still see systems using RC4 today. Their advocates will blame the implementation rather than the standard. Security experts will say, we'll look at this and say, okay, well, don't use the standard. Don't use JWT. And then developers roll their own crypto because they're not given an alternative. And this is what happened with JSON web tokens and the other half of my talk that I wanted to go into. So can you really blame developers for rolling their own crypto when they're not given a viable alternative to bad designs? I say no. If our goal is to stop badly written cryptography from being put out in the world, which means having non-experts design it, we need to design a better standard that's easy to use securely and hard to use insecurely. With that in mind, I designed POSETTO, platform agnostic security tokens. You can see how it's pronounced there. When I set out on this, I had a few design goals in mind. I wanted to minimize runtime negotiation. That's where you would say, okay, well, you have these ciphers you can choose from. Now pick from this list. I wanted that to be completely trimmed down as much as possible. I wanted versioned tokens built in versioning. So that way, if you have version one and version two of the standard, and then version one becomes critically insecure where you don't want to run anymore, and version two is implicated because of some new attack, you can design version three and then migrate everyone onto that. Like let's say a practical quantum computer comes out and that breaks all of elliptic curve cryptography. So each version has its own one true cypress suite. You don't have a lot of options. It's either, I'll go into what the options are. You're using version one or version two or version three. Less knobs and levers to people to play with and get wrong. And that's pretty much it. That's the main design philosophy I took that was different from the pose standards. So since I showed you a JSON web token, this is what the structure of a possedo looks like. You have a version, pretty obvious from everything I've said. You have a purpose. They're called local and public. And the difference is not subtle. Local is symmetric key authenticated encryption. Public is just a digital signature using RSA for version one or ed25519 for version two. And you have an optional footer. The reason for an optional footer is because some people want to do a key rotation. So they would want a unencrypted part of a local token. They could store like a key identifier that references a specific public key or a specific secret key. Everything is base 64 URL encoded as in defined in the RFC and joined by period characters just like JSON web tokens are. So the version defines the Cypher suite. The purpose tells you what it's doing. And the footer is optional for key rotation schemes. Envision use cases is pretty much the same use case that JSON web tokens is supposed to be solving. Short live, one time, third party access tokens. That was all it was designed for. They throw all this complexity and machinery on top of it for solving every use case with one tool. And more specifically, I want to eventually integrate this into something like OpenID Connect so people can use this instead of JSON web tokens for OAuth. So as I said, there's two versions defined in the initial run. There might be future ones when quantum computers become a more imminent threat. Compatibility mode uses open SSL. Like the ciphers that almost everybody's familiar with. RSA, AES uses SHA-2. Specifically, it uses AES-256 in counter mode. HMAC, SHA-384, and Encrypton Mac. And then it uses HKDF to split the keys. I use SHA-384 just as a standard hash function. There's no reason 256 wouldn't have worked or 512 wouldn't have worked. But there are a lot of people who will look at it. One subtle decision made somewhere in one standard and reapply it everywhere. So if I went with SHA-256, developers would assume that SHA-256 was secure. Never mind that there's length extension attacks and whatnot if they decide to build their own MAC protocol with that. And then public key signatures. If you've worked with RSA, you'll probably be relieved that PKCS 1V1.5 is not here. Instead, you get the probabilistic signature scheme, the secure RSA signature scheme with a hard-coded set of parameters. And then you have the recommended version. This is the one I actually want people to use, but they might not have Libsodium, which is what I'm using as a basis for this. Specifically, it uses the extended nonce SHA-20 Poly 1305 AEAD mode. And then it uses ed25519 for signatures. So let's look at one. So in this example, the payload is foo and the footer is bar. This is a local token. It's color-coded. Okay, it seems to show up pretty well on the slide. You can see that you have the version, the purpose, a payload, and then a footer at the end. And here's the key that actually generated this if anybody wanted to download one of the many libraries that implement POSEDO and play with it at home. And here's a public payload. You can see that the string changed from local to public and it's a little bit longer because it's a digital signature. But you see the first four letters of the yellow, that will not change because that's actually plain text. And then if you look at it, oh yeah, here's the keys. Without a footer, it just looks like this. And if you go back and forth quickly, you'll see that the first four letters of yellow did not change because that's bar in base 64 URL. That's what they look like. That's what developers will see. They don't really need to know all of the internals, but considering this is a crypto village talk, I'm gonna go into them a little bit. So for all versions, there's a couple of rules that I wrote into this proposed standard that will eventually hopefully be taken seriously by the IETF and accepted as an RFC. I have a first draft submitted to them, got some feedback, I'm working on the second one right now. The first rule is to, for randomness, is to use u-random. Specifically, or more broadly, the operating systems cryptographically secure random number generator. You're not allowed to use a linear congruent generator or a marxine twister or any other insecure user space design. That's a standard rule. If you're doing that, you're deviating from the standard and you shouldn't do that. You know, no in-CCA2 insecure public key cryptography. Specifically, I would call out this padding mode for RSA. It will never be allowed. If anybody suggests that the answer's gonna be no, closed, won't fix, works as intended. And the reason is because I hope I'm pronouncing his name right, Blikenbocker, had that 1998 padding oracle attack that's still relevant 20 years later. It's old enough to drink. I think it's high time we stopped building that padding mode into new protocols and designs. And Robot just won a Pony Award at Black Hat, so congratulations to Hanobock and his team. And finally, when possible, do everything in constant time. This includes Bay 64 URL encoding. I will post these slides online afterwards. That actually links to a PHP library that implements Bay 64 encoding without any cache timing side channels. So another thing that's built into the standard is what's called pre-authentication encoding. This isn't really an immediate security concern, but there's a class of attacks based on how you feed data into your message authentication code called mechanicalization attacks. So basically, this is just a proactive measure to ensure that every message doesn't create collisions from having multi-part messages. So you can't just append a bunch of data and have it match a signature that has a footer that originally didn't have a footer so you can forge it or anything weird like that. So what we do here is we prefix it, we take the number of pieces and we turn it into a string, a binary packed string representation of that number and little indian as 64 bit. And then for each piece, we do the same exact thing, we prefix it with the length in the same encoding format and then we put the string. This might sound a little weird, I'll get to what it does in just a second. So when a passato is being encrypted, instead of just feeding the cipher text into the message authentication code, you know, HMAC, it actually does the pre-authentication encoding and takes that output of all those pieces and then uses the HMAC of that. So this will authenticate not only the header v1.local, it'll also authenticate the nonce which is generated randomly, the cipher text and the optional footer. The same thing happens with the RSA signature, it covers the header, message and optional footer, which will be just an empty string followed by null bytes if you didn't specify one. For version two, subtle difference between how the crypto-AEAD interface works, it already authenticates the cipher text, you can specify your additional data, so it just packs together the header, the nonce and the footer for version two. And that's because Libsodium and other implementations of the same protocol already authenticate the cipher text, this is just the additional data. And then add25519 is exactly what we'd expect if you looked at the RSA slide previously. So let's compare that to you real quick. JSON Web Tokens has a lot of options, including unauthenticated options. It promotes reasoning by Lego, which you have two pieces that doesn't necessarily mean they're secure if they're not glued together properly or in the right order. And it's often abused for stateless tokens. That's something that most people will criticize JSON Web Tokens for and they don't really look further because that's what most developers are doing. Pasoedo, you have two options, you have version and purpose. Everything's authenticated and your local tokens are always encrypted so you don't accidentally leak sensitive information for a local system. It does its job and it gets out of the way. People can still abuse this for stateless tokens, but it doesn't have the built-in habit in the community yet. And I've been actively discouraging it when people ask if they can use it this way. So hopefully that won't be a re-emergent issue. If it is, of course, that's what the security industry's here for is to tell them not to do that. If you'd like to learn more about the standard I'm trying to build, you can go to pasedo.io or github.com slash paragonie slash pasedo. And you can look and see the RFC draft as it's being written and marked down. You can look at the reference implementation and PHP. If you look at the first URL, there's like implementations in about 11 other languages. That number is gonna go up when I get home because I have a couple to review when I get back. So that's just an example. A very important one in my opinion, but there's a more general topic here. It's about designing cryptography for humans. Using the examples that I just gave you, if we want developers to build secure software and we give them crypto building blocks that are actually secure, that won't fail them. But in order to do that, we have to know how to design cryptography for humans to use safely, you know, mere mortals, humans, developers, whatever noun you wanna use there. So you should have an opinionated interface. This should be your API for any encryption library. You have your message, your key, and an optional data to include in the authentication tag. Your decryption should be exactly the same. You shouldn't have to specify a mode. You shouldn't have to even know what a nonce is, let alone the subtle difference between a nonce and an initialization vector, which does anybody here not know the difference between a nonce and initialization vector, aka IV? Okay. So you should have hard-coded version protocols, which means that, like Paceto, you have version one and version two. You don't have version one in ECB mode and version one in CBC mode and version one in counter mode. You just have version one. And they should be vetted by cryptographers. If you're not a cryptographer and you want to design one, you should hire one to audit your design, like give you a protocol review, give you recommendations, et cetera. And if a vulnerability is ever found in the current version, a new one should be published with a better hard-coded cypher suite instead of giving people options. Because if you give developers options, a lot of them are gonna choose partly, and that's how we end up in the situation we're in today. This is different from what a lot of other people will believe in. When you're designing cryptography libraries and you're working with keys, don't just accept binary strings or byte arrays. Encapsulate them in an object, specifically a typed object. If you have a signing secret key for asymmetric cryptography, that should be distinct from your symmetric secret key. Even if they're the same bit length, even if you generate them from the same randomness device, you wanna keep a little bit of type safety there. That also discourages them from using their passwords as their key, which if you've worked with PHP developers, that problem is more commonplace than we even wanna get into. And there's an additional benefit here. I'm not really at liberty to go into how often this one turns up in my work, but it's a lot. Stack traces and uncaught exceptions and error codes ending up in automated bug reports, sent in emails or published to Jira or track, left in the bug tracker for years and years and years. So when you go in and you look at their bug tracker, you can just grab for password and you can find a lot of their users real passwords because they're being passed around the strings and the programming languages that people work with don't assume it's sensitive. It just goes, oh, it's a string. I'm gonna include it in the stack trace to be helpful. So if you encapsulate it in an object, you can actually hide it from stack traces easily. My friend Taylor, who coined the phrase reasoning by Lego, actually wrote a PHP library. You've probably heard of it as defuse slash PHP encryption. And that was one of the main departures from the norm that went to that design. Always logically separate symmetric from asymmetric cryptography. So that way people don't try to use RSA to encrypt something that doesn't need the overhead or complexity of asymmetric cryptography. If you're storing it in a database and your application that's writing it needs to be able to read it, you don't need RSA. Just use AES. Java and their built-in cipher is the probably most egregious example of violating this rule. Failure remotes. So when PHP 7 was being discussed and they were adding a new cryptographically secure random number generator API to be consistent regardless of OS and all that, the original run would return false if it couldn't generate random number securely, which PHP is a weekly typed language. A lot of people would say, you know, set this letter of a password to an index of an array based on the random int that was just generated. So if you have a random integer and it's set to false, it's gonna return a zero, you're silently gonna end up with passwords that will look like the letter A repeating 10 times. So the best thing to do for designing a library for developers to use is to throw exceptions or whatever equivalent error handling mechanism the programming language allows. This way, if they catch the exception, they can fail gracefully. They can have their pretty error page. They can silently encrypt and email a report to the developer saying, hey, this broke and it shouldn't have, you know, this is a high alert, fix it now. Or they can just log into a bug tracker, whatever they wanna do. If you fail closed in a way that doesn't allow them to catch the exception, like if you just have a fatal error, developers won't use the new API. An unpredictable, you know, white page or a failure scares developers. It's like, okay, well, this is gonna crash. It's gonna make my system unstable. I'm gonna get the call at two in the morning to fix something that, you know, is a random error. And that just makes them nervous. They won't wanna use it. And they'll go back to using RAND and MT-RAND forever. And then, you know, the exception being the mid-ground between the two. And you don't wanna fail open, like the first example bit. Eventually, that was ironed out and when 7.0 launched, PHP does the right thing. Okay, so just a quick recap. Oh, actually, this is the recap. The blame game doesn't solve insecurity. Telling developers don't use JDUT, like many security experts did last year and not giving them something viable to use as an alternative. They're just gonna either keep using it out of spite and they're gonna stop listening to you or they're gonna invent their own or they're just gonna say, screw it, let's not even sign anything, let's just store a token that's completely plain text. If you don't give them an option that's better, they're gonna probably choose a poorer one because that's all the options they have. The only way to really prevent people from designing their own cryptography and implementing it and publishing it in a way that, the one time I amateur designs here is to give them something that already solves a problem for them that's easy enough to use to where they won't have the incentive to get creative. This won't stop arrogant people from thinking that they're smarter than the people who invented AES, et cetera, because, well, we're not gonna get through to arrogant people anyway, they think they know better. Your API should always be simple to understand. Whenever I talk about crypto documentation, I say every asterisk is a disaster risk. I strongly believe in preferring version protocols over cipher agility and error prone standards should be deprecated in favor of safer ones for developers to use. At that point, do I have any questions? All right, let's give it up for Scott. Thank you.