Very good afternoon, good evening, or even good morning, depending on which time zone you are listening to this talk from. Isn't it interesting how we all are so very well virtually connected through this platform? And for this to happen, I'd like to give a huge shout-out to the Crypto Village organizing community for making this happen. I know it has been hard in the middle of this global pandemic, but we are still connected. That's great. It's always a good idea to introduce yourself. I'm Manasi Sheth. I work as a principal security researcher for a leading static analysis company called Veracode. We are located just outside Boston. We have over 2,000 unique customers across different verticals using all kinds of programming languages. What my job is there is to be on top of all the latest and greatest happenings in the security domain, more specifically in the application security domain, see how those things are implemented by different languages and frameworks, and then translate all that knowledge into finding anti-patterns in our customers' code bases. I'm not a cryptologist or a crypto analyst or even a mathematician. I'm just a huge crypto enthusiast like a lot of people here. I like to be on top of all the practical aspects of cryptography. I've spent a lot of time understanding details of different crypto primitives and crypto mini systems. I've seen a lot of crypto implementations across languages. And I try to use all this information to make sure our customers' code bases are secure. So why cryptography? Well, I think cryptography is everywhere. It's the basis of a lot of things you are currently seeing around you. You would be connected through Wi-Fi. You would be having some Bluetooth devices. You might be using e-wallets to buy coffee or something. You might be looking at emails. You are listening to this through a video conferencing software which is supposed to be secure. You might be doing some Bitcoin mining. What I'm trying to say is it's everywhere. It's used right now by you, your family, your friends. Everyone you know and care about and hope are secure enough from any kind of online threats. In this hyper-connected world, we rely a lot on crypto to keep us secure. We rely on a lot of crypto services like confidentiality of our digital assets or authenticity and integrity of our digital transactions. Like maybe e-banking or e-wallets or whatever. Or maybe code signing of a software we are trying to buy. We need cryptography to help us be secure for all these things and much, much more than that. Then why are we seeing so many crypto failures? Well, I'm sure a lot of people would be having this reaction like, oh no, not again. Why are things failing now? This is going to break so many new protocols and so many different protocols across different systems and how to mitigate it. It's like this. Thinking a little deeper while looking at different use cases of crypto failures. What I feel is cryptography is hard. Only more than a couple of dozens of people across decades completely understand all aspects of cryptography. And then how it is being interpreted by people who actually implement the APIs or libraries for further usage in larger systems. Well, I have all respect for these implementers. They have to understand the hardest parts of cryptography. They have to use their domain development experience to write these libraries. And then even worry about, and then we expect them to even put some more weightage into how these APIs are designed. How it will be well understood by the most well-meaning security conscious developers. So that they don't make any mishaps of using these implementations. I wish the implementers took it a little bit ahead than that. And took some responsibility of writing much cleaner documentation, much cleaner APIs. Secure defaults, deprecating legacy or insecure algorithms right out of the sight of developers. Maybe we might be seeing a few lesser crypto failures at that point. In my opinion, it's almost undebatable. 85% of crypto failures we see are coming from not bad implementations. But bad usages of those implementations. So hopefully we all share some responsibility in educating users of these crypto APIs. Before going any further, I would like to make some cryptographic disclaimers. No matter how tempting it is, please don't roll your own crypto. All these great implementers have spent way too much time understanding all different aspects of a crypto algorithm. And provided us with the implementation which a lot of times is time-tested, crypto-analyzed. Please use that. It's for us to use it and be safe. I understand not always we have an option to choose an implementation. But if you do, I would highly suggest you use this implementation called NSEL. It's written by cryptographers. Documentation is very clean. They have all kinds of secure defaults just out of the box. Everything else is not even visible to us. And most importantly, a lot of modern programming languages, I'm sure Python, PHP, they have some kind of a wrapper implementation of this library. Even Lipsodium, another favorite, is a fork of NSEL. So use one of that and you will go a long way. Crypto is crucial to be secured, but that does not mean we ignore everything else in the system. We still have to worry about all kinds of application security attacks, all kinds of networking. We have to make sure our endpoints are secure. We have to take care of all those things. We should not be relaxing the moment we got our crypto right. Let's see what we are going to talk about today. Any crypto system you come across will usually have some kind of basic crypto primitives, which is a cryptographically secured random number generator. It's quite a mouthful. I'm just going to call as CSPRNG going ahead. There will be some kind of an encryption scheme going, usually symmetric encryptions. Maybe some kind of one-way hash functions. And no crypto system works, none of these crypto primitives work in isolation. So most of the crypto applications are a mix or match of these basics crypto primitives and those ultimately become building blocks to have a complete crypto system. So we will be spending some time into looking into some of the most common crypto applications, namely message authentication codes, how to actually store any kind of secure information and digital signatures. OK, let's start by first looking at CSPRNG, which is used almost everywhere. So what is CSPRNG? It's like this huge set of digits or zeros and ones, which needs to execute few, which needs to exhibit few properties. Namely, it should appear completely random. We should not be able to locate any kind of pattern in the way those digits are. Secondly, it should be completely, it should be unpredictable. So knowing or looking at the current digit, we should not be able to predict which is the next digit, which is going to come out of this generator. And lastly, we should never be able to reproduce the same set of digits again. Now, where is this used or where is it required? Well, it's everywhere. It's like any kind of keying material you would be generating for any kind of cryptographic algorithm. Namely, you would need keys to be generated for your encryption, for your message authentication codes. Depending on the mode and the algorithms being used for your initialization vectors or nonces or like for storing secrets, you would need it for soil generation. Basically, it's going to be used every single place. Now, how to actually generate this? There are three main blocks or three main areas. There are three main parts of any CSPRNG algorithm. Obviously, the output which you most care about. And the algorithm which basically churns and gives you this output. But knowing the algorithm, it's extremely trivial to get the output. What makes the output actually CSPRNG or cryptographically secure random is the way this algorithm is actually initialized or seeded, the way it is called. For the seeding to happen, the input to the seeding part of the algorithm should be of the highest entropy possible. Basically, the output is depending on how high the entropy or the source of randomness of this seeding sources. Well, it would have been ideal if we could get the randomness from external thermal elements and stuff, but that's not practical. So we had to rely on what the computer is going to offer us. So right now, the highest source of entropy is what the operating system is going to provide us. And what it does is it picks up all kinds of different entropy elements going on in your system at a particular time. Like there could be interrupts in your programs or disk IO or the way your mouse and keyboards are moving or even your kernel or user level entropy in their programming land. All these things are collected and then fed into the algorithm. And that's what makes your CSPRNG output extremely secure or extremely random. It's almost safe to say that the entropy provided to this seeding algorithm is what makes your entire crypto system secure. Or it's directly proportional, at least that way. So what are some of the do's and don'ts around it? Well, since the source of entropy is most important, let's start talking about that first. Always go with what your operating system provides us. There are different mechanisms which the operating system provides. Most importantly, either you go with a blocking source of entropy or a non-blocking. What does that mean is non-blocking is till a sufficient quantity of entropy is reached, the algorithm is not seeded. So basically, our algorithm is blocked till the entropy is filled out. And non-blocking is whatever entropy is available, just feed the algorithm. That's what operating system gives us. Well, most of the cryptographers are pessimistic people. They'll say, oh my god, you have to use blocking source, you have to have some level of entropy. Well, I beg to differ. 80% of the applications don't really need that high level of entropy. You would be fine with using a non-blocking source for most part. Second thing is, you should just be aware of, in this virtual machine era, where all the applications are deployed on virtual machines, even the source of entropy is coming from the guest operating system and not the actual host, which has a far longer list of entropy sources, which even the operating system provides. What I'm trying to say is, the entropy provided in an application deployed in a guest operating system might not be at par with what a host can provide. It's better to go for non-blocking, otherwise the applications might never really start, because they are just sitting and waiting for a desired set of entropy. It's an active area of research, there are mechanisms to avoid it, but the most important step is first to be aware of that situation. And lastly, I've seen countless examples, even some implementations, which rely on the milliseconds and microseconds as a source of entropy. Just don't do that, please. There are obviously some attacks because of a lower source of entropy. One which comes to my mind is the Sony PlayStation attack. The digital signature used for signing that particular piece of game was used, the entropy used to generate the digital signature was of very low randomness. So this is out there, it happens all the time. The next is, what algorithm to use? Well, it's always better to rely on algorithms which are based on hashing or hash-based MACs, or even good symmetric ciphers. Government standards approved only these ones, it's better to use that. Unfortunately, not a lot of implementations provide this out of the box, but please use that if they are actually being provided. If not these, then usually there are some good library-specific algorithms. But make sure those library-specific mechanisms are using operating system source of entropy, and not anything other than that. If you are using that, be a little more conscious and Google around if there is anything wrong with the library implementation. For example, SHA-1 PRNG, a few versions of Java ago, it was okay as long as it was not seeded initially before use. But if it was seeded initially, it used to just go in some initial state and always give that output. It's fixed now, but there could be such idiosyncrasies lying somewhere. And there are different algorithms based on mercery twisters, like the older dual EC1s, again the classic Sony hack. Even some insecure symmetric cipher based, just don't use any of that anymore. And finally, I just had to put it on the website, on this slide, please don't use random math, there is nothing cryptographically secure about that, it does not exhibit all the properties we require for CSPRNG. Next, let's talk about one of the most famous crypto primitive called encryption, almost synonymous to cryptography. Well, we obviously need our data which is stored at rest or in transit to be confidential. So that is one of the biggest property we expect. But over time, we realized that our data, whenever it is online, which is as we saw 95% of the times, we needed some kind of authenticity as well as integrity out of that data in transit. And why so? To avoid or to safeguard or to mitigate against all kinds of padding oracle attacks where an adversary can just sit in between the sender and receiver and start tampering with the data being sent to the receiver and get all kinds of information back from the receiver. We all are aware of all kinds of issues with TLS protocols, most notably Beast and Poodle, which had all these issues. Well, historically, what used to happen is for authenticity and integrity, they will use another primitive called MAC in conjunction with encrypting the data. It used to work, but there are a lot of issues with that. It was slow, it had a lot more crypto systems going on. So the crypto analysis became difficult. It was much more fragile. So what the greater crypto community came up with was providing an authenticated encryption right out of the box. And this is another property we expect out of AE, which is authenticated encryption. We need authenticity as well as integrity. So these are the three properties we expect out of any modern symmetric encryption scheme. And how is this achieved? Well, at the core of it, we still have a symmetric block cipher running. It takes a symmetric key, it takes plain text, it throws away ciphertext. But there was yet another detail, yet another information, which was like a nice to have feature called associated data. What that is, it would be nice to have some plain text being transferred as plain text on the receiver side. But it still takes part in the authenticity of it. So for example, imagine a sender is sending a packet to a receiver. So the IP address of the receiver needs to be in plain text, but the payload needs to be encrypted. So that kind of a scheme is what is the most common thing or you should, that's what you should be using going ahead. It is called as authenticated encryption with associated data. So just coming back to this block diagram. Authenticity and integrity is provided by this authentication tag, which is computed based on associated data, which is plain text as well as plain text data. And obviously the symmetric key. Authenticated data comes out as plain text. And we obviously get the ciphertext. And similarly, on the opposite side, we have decryption, which takes care, which takes the same symmetric key, the ciphertext, the authentication tag, and the plain text of associated data. If nothing of the keys or the ciphers or the AAD is tampered, then the output of this decryption is going to be plain text. That's how simply it all works. Do's and don'ts. Well, you can use any good block cipher. AES is one of the most commonly used safe block cipher here in the United States. If we talk more globally, we have ARIA, Camellia, and CAST6 too. There are only few block cipher modes which provide authenticity and integrity services. GCM being the most famous. The rest of the modes are either expensive or patented. Patents expire at different times in different countries. So AES-GCM is the most famous scheme. And hopefully you would be using GCM. Just be very careful you are not using the same key and initialization vector for GCM modes more than twice to encrypt different sets of data. I'd like to introduce you to a new kid on the block, which is based on a stream cipher called Chacha. And it comes with its own authenticity scheme called Poly1305. It's a promising alternative or a backup plan if AES-GCM fails. If it fails, the world cannot all of a sudden be insecure. So our great cryptographers have already come up and it has already been implemented in a lot of modern programming languages. And to go a little further, I think Google and Cloudflare are already using this in their TLS implementation. So knowingly or unknowingly, right now even 30% of our websites use this anyways. It's three times faster than AES-GCM scheme. So a lot of slower hardware, even your Android phones or IoT devices needed an authentication scheme. And this is a promising alternative for that. So embrace that. There is no harm in using that already. And lastly, stay away from all other block ciphers, all other stream ciphers and all other modes. There are just way too many to list on the slide. I've just listed the most common ones out there. Okay, let's see how both these primitives work in action. And for that, let's go into our development environment. I'm choosing to show these API usages in Java for two main reasons. One, Java is one of the most chaotic crypto implementation I have seen. And second thing is, it's one of the most commonly used enterprise programming language. So it's extremely important that we pass the message on how to use it securely. Okay, I have done nothing more than setting up a simple Java Griddle project with some of the dependencies we might be using. Let's just start with first creating a mandatory crypto driver class where we will be using all different APIs through. Simple HelloWorld, where we are going to say hello to CryptoVillage. And let's see if this works. Super! Okay, let's start building an authenticated encryption scheme. And for that, what all do we need? We need a SIM key used in encryption as well as decryption. Use a CSPRNG ID for the GCM mode. We need the plain text to be encrypted. We need a plain text A associated data. And finally, the actual encryption and decryption APIs. For that, let's do a little bit of designing before we go into the coding session. And for that, I have opened this particular link which gives us all details about all algorithms coming out of the box of JDK in the latest Java version. What all do we need here is, let's start with symmetric keys. How to generate a symmetric key. And for that, we need to rely on this algorithm class called KeyGenerator. And look at the options it provides us. We just have to be safe and secure and use the AES algorithm because we are going to use AES GCM scheme here. Next, we need to generate a CSPRNG. And for that, we need to go into the secure random class, pick up the right algorithm and go by the default OS entropy for the seeding of that algorithm. And the actual encryption and decryption scheme is defined by the Cypher class where we have to configure it with the scheme we plan to use in terms of transformation. What does that scheme mean? To use the right block Cypher out of all these options. Use the right authentication mode. All these are unauthenticated mode. Use just the GCM and the required padding. GCM underneath is mainly just a counter mode, which is a stream Cypher, which does not need any padding. So that's how we will be defining our Cypher scheme. See the amount of moving blocks here. So let's get into coding. Symmetric keys. For that, let's go and create a symmetric class we are going to use for other primitives as well. A dedicated package for it. We'll return a secret key, which is nothing but a holder class to hold your symmetric keys to be transported around. Which takes the algorithm. And we can decide the size of the key we plan to generate. I choose 256. Even for whatever reason, you need to choose 128. Don't lose sleep over it. It's fine. This just puts us all the more ahead to be future compliant. Or future safe. Or future proof us. Key generator algorithm as we saw. Let's create an instance of it based on the algorithm. Initialize it with the right key size. And just generate it actually. Okay. Nothing in Java works without some kind of exceptional handling. Please don't do this. This is just to keep the demo shorter. So this is how a symmetric key would be generated. Let's use it here. And let's define the algorithm we plan to use this with. String a-algo is going to be a-es. Okay. Let's use this. With a-algo. Perfect. This should give us a nice looking safe, secure key. Next is generating IV. For that, let's first decide the size of the IV we plan to generate. Well, a good IV is anything above 128. I'll go for 256 because, hey, why not? It's defined in bytes, so 32. First, I'll initialize an array which will hold this IV for me. Of the IV size. Secondly, the more important one is actually generating it correctly. We decided to use drbg which provides us with 128 bits of security strength. Default is based out of hash. And just populate this guy. I want a secret key. SecureRandom. This should give us a good CSPR engine. And let's do some exceptional handling here. Perfect. The message we want to encrypt. Associated data. We can specify the IP address. And pass that around. A more common thing to do is actually do something like this. But in the interest of time. Finally, let's get to the most interesting part, which is how to write an encryption scheme. We'll return a ciphertext from the encryption function. Think about the block diagram. We'll take the plain text message. The associated data. The secret key. And the IV. Similarly, decryption will return a plain text byte array. It will take the ciphertext in byte array. If you again need the associated data, the secret key and the IV. The first steps for doing any encryption or decryption is initializing the cipher object as we saw. And which is common for both encryption as well as decryption. Let's do some code reuse here. And create a private method which will return an initialized cipher object. For both our encryption as well as decryption. For that, we'll first need the mode which is either encryption or decryption. It will take the associated data, the secret key as well as the byte array for your GCM. First, we will define the transformation which we saw. AES with GCM and no padding. Perfect. Next, we need to further define how the GCM mode is going to work. And for that, we need to go into the details of configuring the algorithm. And you have to trust me here. I am defining the output size of the authentication bit we expect and the IV it needs. Finally, we are just going to initialize the cipher algorithm with the mode. Sorry, this is the mode which is going to be passed here. Then, the next is the secret key. The algorithm spec we defined. And finally, some amount of randomness for the underneath algorithm to work. And this uses the default secure random algorithm which is native PRNG in non-blocking mode which is fine for most of the applications we are going to write encryption schemes for. Finally, just adding the associated data to it. And this should give us a nice configured cipher algorithm for use in both encryption as well as decryption. After some exceptional handling, obviously. Super. Let's go back to our encryption scheme. This passing on the mode which is the encryption mode here. The associated data, secret key and IV. Sorry, this is in caps. Next is we are going to... We are going to make the cipher object ready to start encrypting our message. For that, we are just going to take the message and convert it into bytes. But we are going to store this in a byte array to be returned properly to the driver class. And for that... And return this. This should do the encryption for us. And similarly, in the decryption side, we are going to set it in the decrypt mode. Pass on the associated data, the secret key and the initialization vector. Get back a nicely initialized ciphertext just which is used in... exactly as it would be used in the encryption mode. And prepare yourself for getting the plain text in a byte array to be returned back to the driver program. Ciphertext array to be... Let's store it in the plain text because there would be some exception handling. Okay, this should do the decryption for us. Let's put all these things together in the driver class. Let's prepare it to hold the ciphertext array from the authentication code. Before that, we have to use this class. First, let's encrypt the message. Pass on the associated data, the secret key and the IV. And get back plain. Plain text array, passing the ciphertext, associated data, secret key and the IV. And let's hope everything works. To preprint the byte array of plain text. And we got back the decrypted version of the message we encrypted. Super! Okay, after going through this frantic coding session, let's take a look at a little more simpler crypto primitive. It does not involve any keys, which is great news. This is called as hash functions or message digests or even checksums. And how it works is it takes a message, goes through this hash function and depending on the hash function is used, it will generate an output of fixed size. Where is it used? It's used in a lot of different crypto applications, most famously in generating machine authentication code for providing us with the authenticity and the integrity services. It's also used in digital signatures for similar services. It's used for any kinds of integrity checks or checksums, file integrity checks or even hard disk integrity checks and those kind of stuff. What kind of properties do we expect out of it? Well, it should be collision resistant. We should never be able to get hold of two different messages, which results in the same hash. It should be one-way, also called as a first-permise, which means that knowing the hash output, we should never be able to go back to the original message which generated that hash. And finally, it should be unpredictable, meaning we should never be coming up with two... knowing one message, we should not be able to generate second message to generate the same output. And what kind of a security strength do we expect? Well, security strength of 112 is going to be soon considered unsafe. Soon meaning after a decade though. So, which is not a lot of time in a cryptographic perspective. So, if we are using it, might as well let's use a hash function which at least exhibits 128 bits of security or even more. And the security strength of a hash depends on actually where it is used and which of these properties is the most important in that application. So, yeah, you have to be a little careful in which thing to use. But I have a simple pseudocode for that. What to do is just worry, just use the safest one which is SHA-3 or one of these three algorithms which has been recently made into the safe hashing list. SHA-224 is okay for MAC as well as digital signatures right now. But for digital signatures, it just provides 112 bits of security which is soon to be deprecated so why not just avoid it. And please don't use message digest anymore, MD family of algorithms for any of the applications. SHA-1 is still okay for HMAC but why bother. So, what I would do is I would just blindly use any SHA-256 or 384 kind of algorithms. Okay, next let's see the first symmetric key-based application called message authentication code. For any crypto systems which needs integrity or authenticity, this is one of the most famously used scheme. Most famously, you might be seeing it used in API authentication in most of the web applications right now providing APIs for usage. How it works? There is a sender and a receiver. There is a message. There is a symmetric key involved which your sender and receiver both has a copy of. The message and the key makes it into a MAC algorithm, generates a MAC and this MAC along with the plaintext is passed on to the receiver where this process is repeated. And if the incoming MAC is same as the MAC computed at the receiver side, well, the message received has authenticity as well as integrity checks in it. Pretty simple, straightforward. Let's see what to do and what not to do. There are a few different kinds of MAC algorithms. Some are based on hash, some are based on block ciphers. Avoid all the block cipher ones. They are a little bit more complicated involving two different keys, one for the actual MAC, one for the actual block cipher underneath. It's just complicated. Avoid using that. Use a hash-based MAC called HMAX. The key used for symmetric key use at least of size over 128 even if you have options to use 96 or 112 bit keys. And the reason being, based on this and the underlying hash, the security strength of your MAC is decided. So, there is a little bit of math here which I don't want to go into detail but just use this. And finally, protect your symmetric key on both the sides. This goes without saying but I still have seen a lot of things failing because of that. Let's see how both of this actually works in action. For that, let's go back in the development environment to see how to use this. Well, guess what? Surprise! I'm not going to do the entire coding here because it's fairly straightforward using the right APIs and making the right algorithmic choices. I'll just point you to the options available and which ones to pick. Let's first look at the way of generating message digest which is happening through this particular class. Look at the amount of options they provide. They provide still the MD family of algorithms. They also obviously provide SHA-1 and SHA-254. It's not out of scope or out of use yet. And it has the good ones, the SHA-2 and the SHA-3 family. Choosing one of these should keep you in safe areas. Similarly, for generating message authentication code, luckily we don't have to write the whole scheme from scratch. We have a corresponding API provided. Java does not at least provide block ciphers which is a good news. It provides a lot of algorithms based for HMAC. You have HMAC schemes using underlying MD5 family of algorithms, SHA-1, SHA-2, SHA-24, all the SHA-2 and SHA-3 family of algorithms. For safely storing your secrets or mainly generating keying material based on low entropy inputs, there are options also provided by the map class. It's good to know if you need to use that. The coding is as straightforward as the cipher object where you just initialize it with the right algorithm. Prepare the initialized object with the data you need to map or find a digest for and just do that with the doFinal method. That's pretty standard. In the interest of time, I'm not going to code that up for you. Going ahead with the applications, let's next look at how to actually store your secrets or the information you want to safeguard, be it your personal identifiable information or some GDPR compliance information, and most importantly, password. This application is very famously called as password hashing or password storage, but in my opinion, it can be extended to safeguard any kinds of secrets. Well, it's a well-known fact, don't ever store your data in plain text. It's also a big no-no to store it in a hash or just a simple salted hash. Modern GPUs and A6 hardware are very good at just parallelizing it and just cracking all this information. So, terrible idea in this day and time. What you should be doing is using something called as key derivation functions, which is inbuilt to be more expensive to be computed. There are a few different kinds of key derivation functions. Again, at the core of it, it will take a password, a salt, and then a hash. But there is something called as a work factor, which can be configured to add a way to artificially increase the computation time needed to generate this hash. What kind of work factors are available depends heavily on the algorithm used. There are a few algorithms called as adaptive functions, where we can just configure a repetition of a number of times the core primitive would be computed for thousands, even sometimes a couple of millions of times. And another category of functions are called as memory hard functions, where there is some amount of memory involved in doing this computation. I have another talk later in the day today, which talks extensively about all these kinds of functions. I'll highly encourage if you are interested in this to log in to watch that. Let's move on to talking about public key cryptography or asymmetric encryption. Well, it is still very similar to asymmetric keys, except for there is a private key involved to encrypt your message and a public key involved to actually decrypt back that message. And the core principle or the core property is having the public key, which is a public information, we should never be able to go back and get hold of the private key. Traditionally, these keys were computed based on prime numbers, which over time has proven not to be the best option. The algebra involved or the exponents needed to choose this prime number was getting increasingly fragile. These schemes are constant reasons of padding oracle attacks. And most importantly, we have a more promising option available in terms of elliptic curves. So I would highly encourage us to embrace this. They are much more compact. And most importantly, most of the public key cryptography based applications have a good amount of schemes out of the box available for this usage. So briefly, let's talk about how this elliptic curves work. There is this algebraic equation defined, which basically defines a particular curve. And the public key and the private key is based on the number of points on this curves and actual x and y coefficients of those points. And this expression contains, this algebraic expression contains all these kinds of coefficients, which is important in defining the curve. Everything except the private key, which is the number of points chosen on the curve is a public information. And again, given the public key, we cannot get hold of the private key. Elliptic curves is not such a modern or a newer thing. It is in existence since 1985. And over time, there have been lots of curves which have came into picture or being proposed. Not all of them are secure. I won't get distracted with any of those. What I use is Edward curves with specifically these two ones are specified on the slide 255.19 and 448. NIST has also approved a lot of curves. Not all of them provide security, which is 112 bits. So while picking NIST curves, be careful about the security provided out of the box. And it's NIST, there is always this conspiracy theory going on that there are backdoors. So my preference, stay away from it. But if you have to use it, there is nothing yet wrong about it. And as I said, stay away from everything else. The good thing is most of the public key applications comes with a bundled scheme to use these curves right out of the box. You don't have to deal with choosing key sizes or any of that. For digital signatures, you have an Edward curve based scheme similarly for key exchange. And a symmetric encryption scheme is also available called ECIS. With its own drawback, it can't encrypt more than 256 GB of data. This scheme is not yet very famously used or commonly used, but it's there. Next, let's look at one of the most common public key based application called digital signatures. So just like in a physical world, when we sign a document, you are saying that you are providing authenticity, integrity and a way of not walking away from the document, which is the non-reputation service while putting that simple signature on a piece of document. Similarly, we have a construct in digital world called digital signatures. How does that work? Again, there is a plain text message whose hash is computed, passed through a signature algorithm and the signature is computed. This signature along with the plain text message is passed to the receiving side where the same operation is performed again. The output hashes are compared. If they are same, then the services are achieved out of the box. Something very similar to Mac, except for there is a private key involved on the sender side or for signing. And for verification, there is a public key of the signer involved. So that's how simply the digital signatures work. Let's look at some of the do's and things to avoid. Digital signatures over time have been very famously used by RSA algorithms or the Al-Gamal key-based DSA algorithms. If you have a choice, just avoid that. In fact, DSA is already going to be deprecated in the next DSS standard coming out of the government body. RSA is fragile as we spoke earlier. ECDSA is another common thing used. It has its own issues of how well the nonces are generated. There was a talk talking in detail about this earlier today. Hopefully, you got a chance to look at that. As we spoke earlier, use Edward curves. Underlying hashes use SHA-2 or SHA-3 family providing at least 112 bits of security. Avoid MD families and SHA-2 and even SHA-24 algorithms. Let's see how this actually works. For that, let's go to the development environment again. Back to our CryptoDriver class. Let's see how we can implement digital signatures using Java. What all do we need from planning and designing? We need ECC based key pairs. We need signer side of the code and the verifier side of the code. Now, I have a little bit of a bad news for you. The signature classes and the public key algorithms coming out of the box from Java does not provide us with Edward keys. Edward key based key exchange algorithms are available but not for signatures. But worry not, we have a pretty good shortcut for this. There is a third party provider for Java called Bouncy Castle which provides us with this and it seamlessly works with all the JDK APIs we have been looking at so far. So, let's go ahead and use that. And for that, let's first go ahead and create an ECC keys generator class. Again, just like secret keys, this class is a holder for the public and the private keys. And for us to use this third party provider, the only thing extra we need to do is add it on our class part to be picked up by the algorithms. And after that, it's pretty much business as usual actually. Let's make sure we use the required algorithm. And this is going to be provided by this Bouncy Castle provider. And we just have to generate key pairs at this point after obviously adding the much needed exception handling here. Let's go back to our driver class and start using this. Key pairs... And let's see the signer and the verifier side of the code. It's just creating a simple message for us to sign. Let's go ahead and create a different package to help us with this signer and verifier APIs for digital signatures. We are going to return the actual sign for the message. We need private key and that's it actually. And similarly on the verifier side, we are going to return a boolean saying all the services we expect the scheme to provide are actually met or not. And actually pass the signature. Okay, let's go ahead and coding up. Again we are going to use the signature API class. Making sure we are using the right algorithm we plan to use. Next going ahead and preparing this class for actually signing messages with the private key. And then adding the message we wish to do just like we did it in Cypher as well as looked at doing it in Mac or Message Digest. And this is just going to hold a final signature for us. Actually sign it. This is what we are going to return. Let's add some exception handling here. Similarly on the verifier side. We are again going to use the same AdWords course. This time we are going to prepare it for verifying with the public key. Preparing it with the message to be verified. And finally actually verifying the message. Signature.VerifyWithSignProvided And let's return this guy. Cool, this is the signer and the verifier side. So let's go back to the driver class and actually use this. Let's collect the signature in this particular array. The message and the private key. And let's see if we could verify this. This time we are going to give it a public key. And the actual signature. And let's see if all this works. Cool. So just to show how easy it is to use elliptic curves. Don't be discouraged. Embrace that. It's really simple. And let's go back to our slides. This is what I wanted to talk about in all different crypto primitives and its building blocks and some most commonly used applications. Before I conclude, I want to point out that key management is a huge issue. If you would have noticed across all the things we spoke about, we always needed some kind of a key material. We have to be conscious about how we are generating it, how we are reusing it, how we are distributing it, how well we are storing it, how well we are refreshing it on a timely basis. And unfortunately, all these aspects of actually managing your key material is usually homegrown. Which is one of the biggest reasons why any crypto systems fail. And something which is not very widely talked about or honestly not even paid a lot of attention to. The worst things I have seen is hard-coded keys and even worse is actually those keys getting checked in into open GitHub repositories. There are many attacks around that recently which comes to mind as PrimeFace's WebLogic-based key attack. Just be conscious about that. Don't ignore this. It's huge. Finally, I hope next whenever you look at any new crypto failure, hopefully it does not happen often, or you are looking at understanding previous use cases, you might find something which was on the don'ts list or something on the cautious list in today's talk. This small community of ours shares the biggest responsibility of making the internet a safer place and keeping it secure. And that was what I hope you have shared enough in this one hour. Finally, I would like to conclude by giving my contact information. My DMs are always open. I talk a lot about all these things and a few other topics on my employer's blog post in much more detail than what I could ever cover in just 60 minutes. And all the codes we worked up today is more polished and in a real world API-based usage dockerized on the GitLab repo here. So feel free to poke around. Thank you.