Good afternoon, good evening, or even good morning, depending on which part of the world you are listening to this talk from. And no, it does not get old. I still want to give a huge shout-out to the CryptoVillage organizing community for making this virtual con happen for us. Welcome to the second talk of the day from my side. In this talk, I'm going to touch base upon how to store sensitive information securely so that we can safeguard it reasonably well against any kinds of offline cracking attempts. Before we go ahead, I'd like to introduce myself. I'm Manasi Sheth. I work as a security researcher at a leading static analysis company called Veracode. My primary responsibility here is to be on top of the latest and greatest happenings in the security field, more specifically in the application security domain, and transfer that knowledge to make sure our customers are safe. I'm a huge crypto enthusiast. I have spent a lot of time, or actually a reasonable amount of time, understanding different base crypto blocks. Its practical implementations, how it can be used in the real world. I understand a lot of anti-patterns. I've spent a lot of time looking at different crypto implementations across programming languages, and I try to translate all this expertise into making sure our customers' codebases are safe and to help pick up any anti-patterns. Recently, I got thinking, why are we seeing so many data breaches in the first place? Eventually, I realized, okay, this is going to be a matter of when rather than if. So, what can we do to protect this information once it is already breached, mainly from any kind of offline cracking? How can we make the information cracking process very, very expensive that it is almost useless? So, in that quest, first place I'll definitely go for that matter of which first thing comes to anyone's mind would be Troy Hunt's most organized, most informative site about this called Have I Been Pwned. In this site, he keeps a database of all the breaches which has happened in probably last decade and gives out information about what might have happened, what has happened, and all those kind of neat stuff. So, instead of putting a big slide of shame kind of thing, like who got hacked and what were the reasons and stuff, what I tried to do was look at all the domains which were hacked and what kind of mechanism were they using for storing any kind of sensitive information. So, this is what my analysis says. There were around 453 unique domains, 13% were storing it in plain text, 20% as hashes without any salting, 30% as salted hash, around 15% still using key derivative functions, which is a great step, and roughly 23% decided not to disclose, which in my opinion is plain text, but no judgments here. So, looking at it, I was thinking, okay, why were they breached in the first place? Most of the times it was because they did not pay a lot of attention in other kinds of security issues in their applications. A lot of them were simple SQL injections where entire databases were dumped out. Open S3 buckets was not an uncommon thing I saw. There were a lot of unprotected endpoints, which kind of leaked this data. So, that was my first lesson learned, that why are these things happening? And now, then mainly my focus was towards thinking about, okay, this breach has already happened, or these breaches are always going to keep happening even for innocent reasons sometimes, when there is nothing innocent about security. So, my next thing was, okay, now what can... what happens after these breaches are done? And my first thought was like, oh, wow, the modern computer architecture is getting cheaper and cheaper and it's going to keep getting cheaper from now on. All these modern GPUs with extremely high parallelization capabilities can crack these things in minutes and making the cost much more... making the password cost much cheaper for any kind of offline mechanisms. With the whole Bitcoin mining philosophy of cracking, there are so many ASICs... sorry, application-specific integrated circuits out there, which makes the cost of cracking much more cheaper. There are literally trillions of hashes happening within seconds and this can be easily mapped to any kind of crypto-perimeters. All these things are going to keep making password cracking much more cheaper with time. So, what should we do? Well, the need of the hour was always stretching this information out so that it takes... we have to throw a lot of computational resources like CPU and memory to compute each password. With that, we are going to increase the speed with which the passwords are being calculated. This will greatly increase the offline cracking time and making us resilient towards any kind of modern computer architectures or brute-forcing or any kind of time-memory trade-off attacks or rainbow tables, dictionary attacks, all those kind of things. So, that's what we really needed. And how did we achieve that? Using key derivation functions. Well, this concept of key derivation function isn't specific to password hashing or secret information hashing. In fact, it came into existence for actually creating key material out of low-entropy inputs. But at that time, when everyone were in this cat-and-mouse race, people thought that KDFs are much better suited than just simply storing things as plain hashed or salted hashes. It can be much better safeguarded at that time. And thus, this whole philosophy of using key derivation functions, KDFs, came into picture. So, simply how this works? Well, underlying, there is still an algorithm which is used, which is iterated hundreds and thousands and sometimes a couple of millions of times on a basic crypto-primitive. This type of functions are called adaptive functions. And then much more matured ones are throwing a little or sometimes a lot of memory to this iterative process, which increases or almost quadruples the speed of cracking offline. And still, it takes password and salt, gives out a fixed length hash, and it's the work factor which can be tuned based on different hardware your application is being deployed. So, that's going to be the base of our talk today to protect our safeguard against most of the offline cracking mechanisms. Some design considerations I'd like to point out here is try to save your password hash and salt in completely different databases, or a distributed database, or maybe a database and a property file or something. They should not be close to each other. Obviously, you're not going to store a password in plain text anymore. I can even go as far as saying that maybe we can have different work factors for different information we are trying to safeguard, or even different logins can have different work factors and storing work factor per login, obviously. With increasing cost in memory or CPU, it should be routinely checked that work factors are incremented accordingly to keep making password offline cracking expensive. Lastly, how expensive is tolerable or acceptable? Industry standards are any kind of interactive login, a latency of around one second is very acceptable. So make sure you tune your work factors in a way that the output is calculated around a second. This is acceptable latency, and it still increases the offline cracking time by a huge margin. And lastly, if you are using your password or trying to save a password which is not going to be involved in interactive logins, like, for example, your hard disk encryption, a latency of around five to six seconds is quite acceptable in that scenario. So those are the things I'd like to say about key derivation functions in general. Let's start talking about different key derivation functions in existence, starting with adaptive functions. One of the oldest and the most widely adopted function was pbkdf2. Again, it came into picture because they really needed to generate keying materials. This function is the only government-approved function right now, so if you really have to comply by government standards, I really wish you don't, then this is probably your only option, unfortunately. This function is also used as different crypto-primitive blocks for other modern functions these days. Let's see how this actually works internally. So just like our generic KDF working, it still takes a password, a salt, gives out a password hash. You can actually configure the size you expect out of a password hash. This feature was more for... because there is always requirement of a fixed key size for any kind of block cipher being used for. The work factor is in terms of iteration count. Let's talk a little bit about the internal working of this algorithm. What it does is it runs the crypto-primitive it iterates over is a pseudo-random function, usually a HMAC. Based on the desired output length and the block size of the internal hash being used in the HMAC, different blocks are generated and those blocks are iterated for the iteration number of count times. Output is concatenated and that's your password hash. So you will see things a little bit in green color here. What I have done is I have written a tool which will do parameter tuning for me based on the rules of thumb I mentioned earlier about having a password being calculated in roughly 1 second for interactive logins and roughly 5 seconds for any kind of non-interactive logins. So since PBKDF is government approved, they say the iteration count or the work factor for this algorithm should be around 10,000, which is way lower. Please don't do that. Please increase something. For a reasonable hardware on which most of the typical web applications would be deployed in today's time EC2T2 instance with roughly 8 GB of RAM and x86 architecture. I ran this tool and the number of iterations were around 1.5 million for just 1 second of password calculation. Just imagine how off the government standards are here. So I'll highly motivate whichever algorithm you decide to use among all the algorithms you are going to talk about. Please run some kind of a tuning utility. Play around with your parameters. I'll be open sourcing this tool anyways. You can feel free to grab it and run it on your deployment hardware to tune your work factors accordingly. Some things you should be worried about when choosing this algorithm is please choose your password output length little less than or equal to the internal hash you are using. The reason being it unnecessarily takes a lot of processing power for a no value add. So that's one of my suggestions. In this algorithm, you can just configure the CPU time involved. There is no memory involved. It is still not at all resilient towards any kinds of brute forcing attempts because of the highly parallelized nature of very cheaply rentable GPUs in today's time. If you don't have to comply with the government standards, please move on. Next, a notable mention, Bcrypt is very commonly used. It is based on a already deprecated symmetric cipher called Blowfish. It involves a little bit of memory for its internal working. So that's what is slightly better than pbkdf. But again, that memory or amount of memory it will need to use is not tunable by the user. And again, it was designed for generating key materials and not with storing secrets or password hashing in mind. So how does this algorithm work internally? Again, we have a password, a fixed size salt, a password output as a password hash. Iteration count is specified in logarithmic way. So this is going to be 2 raised to 14 number of iterations. And internally, how it works is it has this very expensive BlowCypher key setup process which involves some memory. So it iterates around that key setup and the output is again iterated through a normal BlowCypher Blowfish algorithm and the output is given to the caller. It still is a little better than pbkdf because of the internal RAM usage, but it is still very susceptible to brute-forcing attacks, maybe slightly more expensive than the previous one. And I don't understand why to use bcrypt, though I've seen a lot of usages of that. If you don't have to even comply by the government standards, why not use the more modern memory-hard functions. Okay, let's start talking about a few different memory-hard functions. The first one being sCrypt. It's one of the very earlier generation memory-hardness built inside the function. It is having an increased adaptation in cryptocurrencies mainly due to the nature of it. In cryptocurrencies, they don't really need to worry about time-memory trade-off attacks like offline cracking needs to and cryptocurrencies need to be more worried about side-channel attacks, which is not the domain for offline cracking. So it's mainly due to the nature of the applications it's widely adopted in cryptocurrencies. It's not like it's better or less secure for other applications. It was still designed for keying material, but it saw a lot of promising breakthroughs in being used for offline cracking. Okay, let's see how this works. It still has password salt output password we are given. We can still configure the length we desire out of the output. And if you see, the work factor has increased from 1 to almost 3 at this point. And not all are going to be still giving us all the freedom to tune all kinds of resources, but that's a huge step in the right direction, in my opinion, with memory-hardness involved. There is parallelization. You can parallelize the computation. I would like to note here that not all implementations give this control to the user. They still do it the way it depends on the implementation. You would note that there is only one parameter which will control both the CPU resources as well as the memory involved. Basically, it does not differentiate between both the resources we can throw at this algorithm. So basically, going down the line, if we decide that memory is getting cheaper, so we should increase the amount of memory being used in the algorithm, we don't really have a choice here. And finally is the block size which is used internally. Most typical values are 8 or 16. It does not make a huge difference. Let's talk a little bit about how the algorithm works internally. While talking about PBKDF, it is used internally from Mac to generate a fixed size password which is looped through this crazy memory array with a lot of stream ciphers and XORing going on for the iteration number of count. And again, the output is made of fixed size by PBKDFing it again and the output is passed to the caller. If you are choosing this, any kind of brute forcing attempts by a huge margin compared to adaptive functions. But the way the memory is being used by the algorithm internally, it is still using adjacent memory arrays in the consecutive operations. What I am trying to say is depending on the value of the password, the consecutive arrays are chosen. So this is always going to be a predefined sequence of memory arrays based on the input password. And what this opens us is towards the side channel attacks. Again, as we spoke about earlier, there is no way we can tune the CPU and memories independently. And since there is a lot of crypto involved in this algorithm, we have HMAC, we have PBKDF, we have Salsa, StreamCyphers, we have Xoring, we again have PBKDF. The number of crypto involved increases, the implementations become more complicated, more error prone, the crypto analysis becomes more complicated and it's not as sneak, basically. So these are the things you should think about if you decide to go with a script. Still a huge step ahead of the adaptive functions. It still reduces the offline cracking cost by a huge margin, almost like a quadruple based on the number of iterations. It's a great choice. Well, don't they say save the best algorithm for the last? And that's what Argon2 is. Around 2015, there was this competition which took place and the goal of that competition was to come out with an algorithm which is specifically suited for storing secrets and safeguard it against any kind of offline cracking, obviously. It wasn't like before that no one was thinking about how to safely store information. Well, the breaches were happening. It was more like a cat and mouse race and the industry started looking at what current tools are there in the cryptography arsenal which we can apply to this particular problem quickly so that they can safeguard their information as long as they can from any kind of offline cracking mechanism. Well, Argon2, the winner of this algorithm is obviously going to take care of all those existing attacks we have been talking about so far. It's very resilient against any brute forcing or dictionary attack. It's very hard to just parallelize it and run computations and start cracking passwords in minutes. They were also very resilient about the modern computer architecture which started maturing, became much cheaper and they were very, very cognizant about it will keep getting cheaper with time. So it's obviously going to be very resilient against any application specific integrated circuit architectures or even FPGA arrays. A few years ago, there were limited implementations of this algorithm out of a direct cryptographic library. But that situation has greatly changed. So congratulations, you don't have to implement this algorithm yourself. You can pick up any programming language, any library in that language and for the most amount of time, you would be having this implementation ready to be plugged and played in your application. So let's see how this algorithm works. Well, you still have your passwords and salts. You still will get a fixed length hashed password. In terms of memory factors, you have three parameters now. One is obviously parallelization. You can configure it based on number of CPU cores you have at your disposal and you can tune the amount of CPU needed in terms of number of iterations and the memory which can be used in this algorithm in terms of memory size. So it basically decouples both the resources resource utilizations. Very unlike sCrypt and one of the huge advantages. There is some crypto analysis done on Argon2 algorithms which makes any iterations below 10 a little susceptible but it is more theoretical crypto analysis so don't freak out but choose a parameter which is at least greater than 10 and that's the reason. Talking about modes of operation which is crucial. There are two main types of modes in this algorithm. One is data-dependent mode which is what was there in the previous sCrypt algorithm as well and a data-independent mode which is the best option for password storage and the third one is a hybrid of both the modes working together. How these modes work for that let's start talking about the internal crypto of this algorithm. So how it goes is it first computes a hash of password salt and all these different parameters. Any hash can be used usually it is Blake2. And based on the value of the hash sorry before that there is this memory array which is dedicated to this based on the memory size we give. So just imagine it as rows and columns being populated iteratively for a number of iterations of times. So for data-dependent mode and data-independent mode how it is different? It's like each memory array is being populated in sequence but that sequence in data-dependent mode is decided by the value of the hash which is dependent on the value of the input password. And for data-independent mode it is completely random. So basically what it comes down to is the sequence of memory array populations per iteration is actually based on the input password. And this particular feature of this algorithm makes it susceptible to side channel attacks which is okay for cryptocurrencies but not very comfortable for any kind of password storage. And that's why you should use the independent mode where the sequence of memory array population is completely random and it takes away even that issue which a script had and which even this kind of attacks might have. So some design considerations. You have to tune your parameters a little more carefully and mostly because you have few more parameters to take care of. As we talked about the data-independent mode is more susceptible to any kind of side channel attacks and there is this hybrid mode where the first part of it is happening in independent manner and the next half is happening in the data-dependent manner. And with that we get the best of both worlds. We are resilient towards side channel attacks as well as we are much better with the time memory trade-off issues. So that's what I would say. Use Argon2 mainly in 2ID mode. We already looked at what is the best parameter options for an output for password computation within a second. Since Argon2 is the algorithm I am highly recommending to be used for any kind of sensitive information storage. I'd like to quickly show how easy it is to just start using this by any implementations you have access to. Just to record this demo, I had a forked off EC2 instance. These are the details of the instance. It's a typical standard T2 medium with 2 CPUs and 4GB of RAM. Currently available as of this moment is around 2.8GB. It's using x86 architecture on a Linux kernel. So let's see how quick it is to start using this. Well, I am going to use NACL's password module where the Argon2 ID is being supported. Well, a quick pro tip whenever given a choice of any crypto implementation you need if you have a choice, always go for NACL. It's very cleanly written by cryptographers rather than actual developers. They deprecate things which are no more secure or better options are available out there. They quickly deprecate all those things from anyone's view. So chances of making wrong choices are eliminated. Okay, coming back to the code, we are going to use that module and just to keep track of time, we are going to use the time module. Start time is going to be current time. Let's start storing the password. First, let's use the module hash.argon2id Why not use the hybrid one? Best of both worlds, right? And the string output for that. Giving the password long is better than higher, entropy shorter one. So that's what I choose for. Ops limit is the number of iterations around the memory array. Let's go for 14 till 10 is already theoretically crypto-analyzed. So anything about 10 is great. And the key thing, memory. It's again logarithmically given. We already know the answer from our tuning tool. So let's see. This is as short and sweet as it can get actually. And just to keep track of time, let's see how much it is. And hopefully this compiles and runs actually. Okay, so it took around 1.1 second. Can we go any higher? Let's see. Going from 27 to 28 which is 2.3 I'll leave that choice to you at this point. This is how we should typically tune any parameters. It's almost second nature to wonder how these functions actually compare against each other. There is not a lot of research done in this topic about how to actually put a dollar value or number of years of cracking a particular breach mechanism or There is no apples to oranges comparison as well you could imagine. But good notable work is being done by these two papers. One released in USENIX a couple of years ago and another one done in conjunction between Microsoft and Purdue University. In the most trivial way, what they do is they look at the latest hardware and the memory cost and just start calculations from there. And that's what I tried to do it as of yesterday. So for adaptive functions it's just going to be again a huge list of disclaimers here before I actually talk about this very very cautiously actually. I'm assuming the keyword, the password the sort, the output all those things are same across all these functions. I'm also assuming there is no electricity cost involved. Talking about these figures for adaptive functions it's just going to be as simple as number of iterations per cost of hardware. And the cost of hardware is much easier to calculate these days because most of the modern A6 hardwares for Bitcoin mining come with that statistics. So looking at one of the most leading hardware in that department coming from Antminer one of the best configured one is around $2500 and it promises to do 110 trillion hashes per second. And trillion is 10 raised to 12 if you are wondering as I was. So using that configuration with the number of iterations adaptive function is going to take so much time. The point being it's going to be extremely cheap to just crack these passwords considering this decently priced machines are going to be in common people's hand very soon. And similarly for memory hard functions and I want to stress here that this is only for memory hard functions which is running in the data dependent mode where the array used for memory calculations is based on the input passwords. So where again the cost is going to be more based on memory as well as the amount of time it takes which pretty much quadruples in memory hardness is going to be much more expensive compared to adaptive functions. And this is just for data dependent mode. For data independent mode well the cost might still be the same but the number of guesses is going to be exponential. So this is just sharing some statistics still in a more conservative tone. This is all what I wanted to speak about today around offline cracking different mechanisms we can use to safeguard ourselves. Key derivation function being the key of that. How to tune different parameters and what kind of design concentrations you should be doing while picking each one of that. Next I'd like to talk about a little bit about how all this information can be mapped to storing any kind of secrets you need to. For that you need to sit down and do a little bit of threat modeling for your own usages. Something like what is sensitive to your business? Do you need to comply with any GDPR requirements? Are you storing any personally identified information? Can it be used for crafting further attacks against the users whose information might be breached? How are you storing that information? Are you storing it in a database? Which fields are involved in that database? All those things need to be thought about and you can easily map all these KDFs to work for your own needs. Lastly, you must have come across countless suggestions and password hygiene requirements or tips over years since it is such a key aspect of any kind of authentication mechanism. Just for the completeness of this talk, I'd like to say a few things about it. Always choose a unique password. Password managers are great. Please use those. Longer passwords are better than shorter, higher entropy ones. This is what I typically do. I store my passwords in a password manager. This is the configuration I use while generating any new password. I choose a longer password and with a reasonable amount of entropy. The point I'm trying to make is there is a lot of crypto analysis done which points us towards the theory that longer passwords are better than shorter ones with higher entropy. A password whose length is 25 or 30 characters is far better than a password which is of 8 or 10 characters standard length with two special characters, one uppercase and two digits and those kind of things. So do that. The website we looked at about the data breached information, they have a nice API exposed by again Troy Hunt. It would be great to use that API in your websites or even password managers can start using that where if a password which has already been seen in a breach is being used, then they would be flagged and that would go a long way. Finally, in conclusion, please embrace adaptive key derivation functions. Use memory hard functions based on your choice and comfort level with the amount of crypto analysis done. Please don't do plain text or hashing or your own DIY designs. Those are all silly things in today's time. Consider upgrading your work factors based on the resources cost out in the market. Consider having unique work factors for information you are trying to save or for each different user as well. Password hashing suggestion longer is better than shorter with a higher entropy. Keep using passwords. Keep auditing your passwords for its existence in any breaches. And finally, I would like to conclude a huge thanks for giving me this opportunity to share my thoughts. My DMs are always open for any interesting conversations. I blog a lot about these things in much more detail than what a 45-minute slot is going to ever allow me to. And finally, you will find all these algorithms implemented in Java as well as the tuning tool on my GitHub repo. Thank you. That was talk how to store sensitive information in 2020 and do's, don'ts, and how-tos of cryptobuilding blocks used in Java. Thank you again to Mansi. We have them right here for a live Q&A, so please put your questions in the Discord CPB Q&A channel. So just to start off, what's the reason for high memory usage is a requirement for KDFs? This is from Discord, by the way. I understand it helps make implementing ASICs harder, but I don't understand why. Sure. So ASICs, in my opinion, really started coming into existence because of the underlying bit mining philosophy is increasing the computation over and over again much, much faster. The hardware is still expensive and throwing memory at it will just make it much more expensive for a widespread adoption, actually. And that would ultimately add to the cost of cracking passwords offline, in my opinion. Again, we have not seen the future. We don't know what quantum is going to get us, but for foreseeable future, for whatever the current theoretical crypto analysis says, that's what it is. Awesome. We have another question. When memory is the bottleneck, is there still an advantage to using ASICs, or does it revert to only negligible gain over general-purpose CPUs? Well, we still have a huge iteration factor, right? So it's a combination of both. So a general-purpose CPU can't be that highly paralyzed with the amount of memory required for each thread. So, yeah. Cool. Another question that we have is, does a salt need to be just unique instead of being that big to avoid having two passwords hash the same thing? This is in relation to 64 bits versus, say, 128 bits. Sure. Well, 64 bits is, like, the pure minimum requirement anyways from a standard which was written, like, at least half a dozen... I mean, at least five or six years ago. I don't remember right now. Actually, even more. This is PBKDF, probably. So, I mean, a little bit more salt. 128 is not unacceptable. It's not going to add hugely to the processing power. Yeah, it's not a hard requirement. Even CSPRNG is, like, maybe a little too much crypto kind of a situation. So, yeah. It's a little bit on the higher side, but that's okay, in my opinion. It does not add to the computation at all. Awesome. Another question we have, and there were some side parts to this, of course, in chat if you want to go further into them, as I saw. But the question was, any thoughts on using Libsodium, the fork, instead of NACL? I have had great experiences with it. Yeah, Libsodium is great. It is being adopted much more widely, as I stood corrected. NACL is last made at least five, six years ago. I have a slight preference for NACL just because I like the documentation and the names of the APIs and the ease with which they are taking away all the more options you give, the higher the chances of it actually going wrong. So that way I feel NACL is slightly better. But Libsodium is absolutely great anyways. So that's my personal preference. Awesome. Does anyone have any other Q&A questions? Please drop them into the Discord now. This is in the CPB Talk Q&A text channel. And we just wait for a little bit. Also, thank you so much for your talks. These are really lovely, and I'm really excited for the next one to be replayed again soon. Thank you. I'm having a lot of fun watching me talk for two hours. Oh, man, that's a lot of anxiety for me at least. That's fun. I think we have someone typing. Oh, no, just people love your talk. Thank you. Let's see if this last person has a question. All right. Thank you again, Mansi, for all your time with us. We hope you take care. Enjoy the rest of Crypto Village and DEF CON. We will love to see you in our Discord soon. Please stay safe, everyone. Please stay safe. Thank you. Bye-bye.