Stan Drapkin. High-level cryptography hooks in .NET
Stan Drapkin is a security and compliance expert with more than 16 years of experience working with the .NET Framework (since .NET 1.0-beta in 2001). Unfortunately, he himself does not write articles in Russian, so we agreed with him to release a translation of his report with DotNext Piter . This report took the first place at the conference!
Symmetric cryptography, asymmetric, hybrid, high-level, low-level, streaming, and modern elliptical cryptography. Fifty-six minutes of video about cryptography, and much faster - in the form of text.
')
Under the cut - video, slides and translation. Enjoy reading!
My name is Stan Drapkin, I am the technical director of a firm specializing in information security and regulatory compliance. In addition, I am the author of several open source libraries that have been very well received by the community. Who of you heard about Inferno ? This library demonstrates the correct approach to cryptography in .NET, and TinyORM implements a micro-ORM for .NET. In addition, I have written several books that may be relevant to the topic of today's article. One of them, 2014 edition - “Security Driven .NET”, the other, 2017 - “Application Security in .NET, Succinctly”.
First we will talk about what I call the four stages of crypto enlightenment. Then there will be two main topics, the first one will be about symmetric cryptography, the second about asymmetric and hybrid. In the first part, we will compare high-level and low-level cryptography and take a look at the example of streaming cryptography. In the second part, we will have many “adventures” with RSA, after which we will get acquainted with modern elliptical cryptography.
So, what do these stages of crypto enlightenment look like? The first stage - “XOR is so cool, look, mom, how can I!” Surely many of you are familiar with this stage and know about the wonders of the XOR function. But, I hope, the majority of this stage has outgrown and moved on to the next one, that is, learned how to perform encryption and decryption using AES (Advanced Encryption Standard), a well-known and highly appreciated algorithm. Most developers who do not visit DotNext are at this stage. But, since you follow DotNext and are familiar with the reports on the dangers of low-level APIs, you are most likely at the next stage - “I did everything wrong, you need to switch to higher-level APIs”. Well, for the sake of completeness, I will also mention the last stage - the understanding that with the best solution to the problem, cryptography may not be necessary at all. To reach this stage is the most difficult, and there are few people on it. One example is Peter G. Neumann, who said: “If you think that the solution to your problem lies in cryptography, then you don’t understand exactly what your problem is.”
The fact that low-level cryptography is dangerous has already been discussed in many .NET reports. You can refer to the report of Vladimir Kochetkov 2015, "Pitfalls System.Security.Cryptography" . His main idea is that at each stage of working with low-level cryptographic APIs we, without knowing it, make many decisions, for many of which we simply do not have the relevant knowledge. The main conclusion is that ideally, instead of low-level cryptography, you should use high-level. This is a remarkable conclusion, but it leads us to another problem - do we know exactly how high-level cryptography should look like? Let's talk a little about it.
We define the attributes of a non- high-level cryptographic API. For a start, such an API will not come across as native to .NET, rather it will look like a low-level shell. Further, such an API will easily be used incorrectly, i.e. not as it should. In addition, it will force you to generate a lot of strange low-level things — nonce, initialization vectors, and the like. Such an API will force you to make unpleasant decisions, to which you may not be ready - to choose algorithms, padding modes, key sizes, nonce and so on. He also will not have the right API for streams (streaming API) - we will talk about how the latter should look like.
In contrast, what should the high-level cryptographic API look like? I believe that it should first of all be intuitive and concise for both the reader of the code and the writer of the code. Further, such an API should be easy to learn and use, and it should be extremely difficult to use in the wrong way. It should also be powerful, that is, it should allow us to achieve our goal at the cost of a small effort, a small amount of code. Finally, such an API should not have a long list of restrictions, warnings, special cases, in general - there should be a minimum of things to keep in mind when working with it, in other words - it should be characterized by a low level of interference (low-friction), just work without any reservations.
Having dealt with the requirements for the high-level cryptographic API for .NET, how do we find it now? You can try to just google, but that would be too primitive - we are professional developers, and this is not our method. Therefore, we investigate this problem and try out various alternatives. But for this, we first need to make for ourselves the correct idea of ​​what is authenticated encryption (Authenticated Encryption), and for this it is necessary to deal with the basic concepts. They are the following: plaintext P (plaintext), which we translate into ciphertext C (ciphertext) of the same length using some secret key K (key). As you can see, so far we are working with a very simple scheme. In addition, we also have an authentication tag T and nonce N. An important parameter is N̅, that is, reuse of nonce with one key. As many of you probably know, it leads to a violation of the confidentiality of the text, which is obviously undesirable. Another important concept is AD (Associated Data), i.e. associated data. This is optional data that is authenticated but not involved in encryption and decryption.
Having dealt with the basic concepts, take a look at the various options for cryptographic libraries for .NET. Let's start with the analysis of Libsodium.NET. How many of you know her? As I see, some are familiar.
nonce = SecretAeadAes.GenerateNonce(); c = SecretAeadAes.Encrypt(p, nonce, key, ad); d = SecretAeadAes.Decrypt(c, nonce, key, ad);
This is C # code that is used to encrypt with Libsodium.NET . At first glance, it is quite simple and concise: the first line generates nonce, which is then used in the second line, where the actual encryption takes place, and in the third, where the text is decrypted. It seemed - what difficulties could there be? To begin with, Libsodium.NET offers not one, but three different methods of symmetric encryption:
Time
nonce = SecretAeadAes.GenerateNonce(); c = SecretAeadAes.Encrypt(p, nonce, key, ad); d = SecretAeadAes.Decrypt(c, nonce, key, ad);
Two
nonce = SecretAead.GenerateNonce(); c = SecretAead.Encrypt(p, nonce, key, ad); d = SecretAead.Decrypt(c, nonce, key. ad);
Three
nonce = SecretBox.GenerateNonce(); c = SecretBox.Create(p, nonce, key); d = SecretBox.Open(c, nonce, key);
Obviously, the question arises - which of them is better in your particular situation? To answer it, it is necessary to climb inside these methods, which we will now do.
The first method, SecretAeadAes , uses AES-GCM with 96-bit nonce. It is important that he has a fairly long list of restrictions. For example, when using it, you should not encrypt more than 550 gigabytes with one key, and there should not be more than 64 gigabytes in one message with a maximum of 2 32 messages. Moreover, the library does not warn about approaching these restrictions, you need to track them yourself, which creates an additional burden on you as a developer.
The second method, SecretAead uses a different cipher suite, ChaCha20/Poly1305 with a significantly smaller 64-bit nonce. Such a small nonce makes collisions extremely likely, and this is the only way to use this method - with the exception of quite rare cases and provided that you are very well versed in the subject.
Finally, the third method, SecretBox . Here you need to immediately notice that the arguments for this API are missing associated data. If you need authenticated encryption with AD, this method will not work for you. The encryption algorithm used here is called xSalsa20/Poly1305 , nonce is large enough - 192 bits. Yet, the lack of AD is a significant limitation.
When using Libsodium.NET some questions arise. For example, what exactly should we do with the nonce generated by the first line of code in the examples above? The library does not tell us anything about this, we have to figure it out on our own. Most likely, we will manually add this nonce to the beginning or to the end of the ciphertext. Further, we could get the impression that AD in the first two methods can be of any length. But in fact, the library supports AD no longer than 16 bytes - after all, 16 bytes is enough for everyone, right? Go ahead. What happens with decryption errors? In this library, it was decided to throw exceptions in these cases. If the data integrity in your environment can be compromised, then you will have many exceptions that need to be handled. What if your key size is not exactly 32 bytes? The library does not tell us anything about it, these are your problems that do not interest it. Another important topic is the reuse of byte arrays to reduce the load on the garbage collector in intensive scenarios. For example, in the code we saw an array which was returned to us by a nonce generator. I would like not to create a new buffer every time, but to reuse the existing one. This is not possible in this library; an array of bytes will be re-generated each time.
Using the scheme we have already seen, we will try to compare the various algorithms of Libsodium.NET .
The first algorithm, AES-GCM, uses 96-bit nonce (the yellow column in the picture). It is less than 128 bits, which creates some discomfort, but not too significant. The next column is blue, this is the place occupied by the authentication tag, with AES-GCM it is 16 bytes or 128 bits. The second blue number, in parentheses, means the amount of entropy, or randomness, contained in this tag - less than 128 bits. How much less - in this algorithm depends on how much data is encrypted. The more encrypted, the weaker the tag becomes. Already this should give us doubts about this algorithm, which will only increase if we take a look at the white column. It says that repetitions (collisions) of nonce-s will lead to falsification of all ciphertexts created by the same key. If out of, say, 100 of your Ciphertexts created by the shared key in two, there is a nonce collision, this nonce will lead to an internal authentication key leak and allow the attacker to forge any other ciphertext created by this key. This is a very significant limitation.
Let's move on to the second method Libsodium.NET . As I said before, here for nonce, too little space is used, only 64 bits. The tag takes 128 bits, but contains only 106 bits of entropy or less, in other words, significantly below the security level of 128 bits, which in most cases they are trying to achieve. As for forging, the situation here is somewhat better than in the case of AES-GCM. The nonce collision leads to the falsification of ciphertexts, but only for those blocks in which the collisions occurred. In the previous example, we would have been forged 2 ciphertext, not 100.
Finally, in the case of the xSalsa / Poly algorithm, we have a very large nonce with a size of 192 bits, which makes collisions extremely unlikely. The authentication method is the same as in the previous method, so the tag again takes 128 bits and has 106 bits of entropy or less.
Let's compare all these numbers with the corresponding indicators of the Inferno library. In it, nonce occupies a colossal space, 320 bits, which makes collisions almost impossible. As for the tag, everything is simple with it: it takes exactly 128 bits and has exactly 128 bits of entropy, no less. This is an example of a safe and secure approach.
Before getting acquainted in more detail with Libsodium.NET , we need to understand its purpose - unfortunately, not everyone who uses this library is aware of it. To do this, refer to its documentation, which states that Libsodium.NET is a C # wrapper for libsodium . This is another open source project, the documentation of which says that it is a fork of NaCl with a compatible API. Well, refer to the documentation of NaCl , another open source project. In it, as a goal, NaCl is postulated to provide all the necessary operations for creating high-level cryptographic tools. This is where the dog is buried: the task of NaCl and all its shells is to provide low-level elements, of which then someone else will be able to build high-level cryptographic APIs. These shells themselves were not thought of as high-level libraries. Hence the moral: if you need a high-level cryptographic API, you need to find a high-level library, and not use a low-level wrapper and pretend to work with a high-level one.
Consider how encryption works in Inferno .
Here is a sample code in which, as in the case of Libsodium , each encryption and decryption takes only one line. Key, text, and optional associated data are used as arguments. It should be noted that there are no non-s, there is no need to make any decisions, in case of a decryption error, it simply returns null, without throwing exceptions. Since the creation of exceptions significantly increases the load on the garbage collector, their absence is very important for scenarios with processing large data streams. I hope I managed to convince you that this approach is optimal.
Out of interest, let's try to encrypt a string. This should be the simplest scenario that everyone can implement. Suppose that we have only two different string values: "LEFT" and "RIGHT".
In the picture you see the encryption of these lines using Inferno (although for this example it does not matter which library is used). We encrypt two strings with one key and get two ciphertext, c1 and c2 . Is everything correct in this code? Is he ready for production? Someone may say that a problem is possible in a short key, but it is far from the main one, so we will assume that the key is used the same, and it is of sufficient length. I mean something else: with the usual cryptographic approaches, c1 in our example will be shorter than c2 . This is called length leaking — in many cases, c2 will be one byte longer than c1 . This may allow an attacker to understand exactly which string is represented by the given ciphertext, “LEFT” or “RIGHT”. The easiest way to solve this problem is to make both strings have the same length — for example, add the character to the end of the string “LEFT”.
At first glance, length leaking is perceived as a somewhat contrived problem that cannot be encountered in real applications. But in January 2018, Wired magazine published an article with a study by the Israeli Checkmarx company, under the headline "The lack of encryption in Tinder allows outsiders to track when you navigate the screen." I will briefly retell the content, but first a rough description of the Tinder functionality. Tinder is an application that receives a stream with photos, and then the user navigates the screen right or left, depending on whether he likes the photo or not. The researchers found that, although the commands themselves were correctly encrypted using TLS and HTTPS, the command data for the right hand held a different number of bytes than the data for the left. This is, of course, a vulnerability, but in itself it is not very significant. More significant for Tinder was the fact that they themselves sent the streams with photos via regular HTTP, without any encryption. So the attacker could get access not only to the reactions of users in the photo, but also to the photos themselves. So, as you can see, length leaking is a very real problem.
Now let's try to encrypt the file. Immediately I must say that in Libsodium.NET file encryption or, more broadly, stream encryption is not implemented by default, it has to be done manually there - which, believe me, is very difficult to do correctly. Inferno does much better with this.
Above, you see an example taken with little or no change from MSDN. It is very simple, here we see one stream for the source file and another for the destination file, as well as a crypto stream that converts the first to the second. In this code, Inferno is used only in one line - in the one where the conversion occurs. So, we have a simple and at the same time fully working and proven solution for encrypting a stream.
It should be remembered that when encrypting with the same key, we have a limit on the number of messages. They also exist in Inferno , and in this library they are clearly written on the screen. But at the same time in Inferno they are so large that in practice you will never reach them. In Libsodium.NET, the restrictions are different for different algorithms, but in all cases they are low enough to be likely to exceed them. So it is necessary to check whether they will be achieved in each individual scenario.
We should also talk about authentication of associated data, since this is a topic that is not often covered. ADs can be “weak”: this means that they are authenticated, but they don’t participate in the encryption and decryption process. On the contrary, "strong" AD change this process itself. Most of the AD libraries I know are weak, while Inferno uses the second approach, where AD is used in the encryption / decryption process itself ...
It should also dwell on what level of security high-level cryptography should strive for. In short, my answer is: 256-bit encryption with a 128-bit authentication tag. Why is needed so big key? There are many reasons for this, each of which is significant in itself, but now I would like you to remember one thing: we need to protect ourselves from possible bias (bias) when generating cryptographic keys. Let me explain what is meant by bias. For a random-bit generator with no bias, for each bit, the probabilities of taking the value 0 or 1 are equal. But suppose that our bit generator will take the value 1 with a probability of 56%, rather than 50%. At first glance, this bias is small, but in fact it is significant: 25%. Now let's try to calculate how much entropy we will get when generating a certain number of bits by our generator.
In the picture you see the formula by which this calculation will be made. It is important that there are only two variables in it: bias, which we have already spoken about (bias), and the number of bits created by the generator. We assume that bias is equal to 25% - this is quite an extreme case, in practice you most likely will not work in systems with such a distorted random number generator. Anyway, with a 25% bias and a 128-bit key, we get only 53 bits of entropy. Firstly, it is substantially less than 128 bits, which are usually expected from a random number generator, and secondly, with modern technologies such a key can simply be brutalized. But if instead of a 128-bit key we use 256-bit, then we get 106 bits of entropy. This is already quite good, although less than the expected 256. With modern technology, it is almost impossible to crack such a key.
At the end of the first part of the report I will summarize the interim results. I recommend everyone use a well-written cryptographic API. Find the one that suits you, or send a petition to Microsoft to write it to you. In addition, when choosing an API, you should pay attention to the presence of support for working with threads. For reasons already explained, the minimum key length should be 256 bits. Finally, it should be borne in mind that high-level cryptography, like any other, is not perfect. Leaks can occur, and in most scenarios, their capabilities need to be remembered.
Let's talk now about asymmetric, or hybrid cryptography. I'll put a trick question: can you use RSA in .NET? Do not rush to respond in the affirmative, as many do - let's first check your knowledge in this area. The following slides will be specifically designed for people already familiar with this topic. But first, let us turn to Wikipedia, and recall what exactly RSA is - in case someone has forgotten or has not used this algorithm for a long time.
Suppose there is a certain Alice, who with the help of a random number generator creates a pair of keys that includes one private and one public. Next, there is some Bob who wants to encrypt a message for Alice: “Hello, Alice!” With her public key, she generates a ciphertext, which she then sends to her. She decrypts this ciphertext with the private part of her key.
Let's try to reproduce this scenario in practice.
As you can see above, we create an instance of RSA and encrypt some text. Immediately note that .NET makes us choose padding mode. There are five of them, all with incomprehensible names. If we try them all in turn, we find out that the last three simply throw an exception and do not work. We use one of the two remaining - OaepSHA1 . Here the key will be 1 kilobits in size, which is too small for RSA, it is almost a cracked key. Therefore, we will have to set the key size manually. From the documentation, we learn that there is a special property .KeySize , which gets or sets the key size.
At first glance, this is exactly what we need, so we write: rsa.KeySize = 3072 . But if, guided by a vague suspicion, we then check what the key size is now equal to, then we find out that it still takes 1 kilobit. It does not matter, we will check this parameter using the WriteLine(rsa.KeySize) method, or rsa.ExportParameters(false).Modulus.Length * 8 - in the latter case, the public RSA key component is exported, this requires the argument “false”. The modulus of this key is an array, which we multiply by 8 and get the size in bits - which again will be 1 kilobit. As you can see, this algorithm is too early to send to production.
Let's not spend time figuring out why this API does not work, instead try another implementation of RSA provided by Microsoft in .NET 4.6, that is, completely new. It is called RSACng , and Cng stands for Cryptography of the next generation (“the next generation of cryptography”). Great, who doesn't want to work with the next generation tools? Surely here we will find the magic solution to all our problems.
We request an instance of RSACng, re-set the key size to 3 kilobits, re-check the key size via WriteLine(rsa.KeySize) - and again find out that the key size is still equal to one kilobit. In addition, if we request the type of the object that generated the key - as we remember, we requested an instance of RSACng - then we find out that it is RSACryptoServiceProvider. I just want to share here my personal feeling of despair, and yell: “For what, Microsoft ?!”.
After long torments and torments, we find out that you really need to use a designer, not a factory.
Here the default key size value is 2048 bits, which is already significantly better. What is even better - here we manage to finally set the key size to 3 kilobits. As they say, achievement unlocked.
Let me remind you that all our efforts so far boiled down to creating RSA, we haven’t even begun to encrypt. There are still questions that we need to answer in advance. For starters, to what degree can you rely on default key sizes? The implementation of the RSA factory can be redefined to machine.config, therefore, it can change without your knowledge (for example, it can be changed by the system administrator). This means that the default key size can also change. Thus, you should never trust the default values, the key size should always be set independently. Further, how good are the default RSA key sizes? In .NET, there are two RSA implementations, one based on one RSACryptoServiceProviderand the other based onRSACng. In the first one, the default size is 1 kilobit, in the second two. Out of interest, let's compare these values ​​with the Bitcoins (BCN) available on the network. I apologize in advance for raising the sore subject, but we will not discuss Bitcoin or cryptocurrency, we will only discuss the network itself. She has a published hashrate, which is growing every month and today is 2 64 hashes per second. This is equivalent to 2 90 hashes per year. Suppose, for simplicity, that the hash is equivalent to the underlying operation — although this is not quite true, it is more complex. If you read cryptography books written by real professionals, not people like me, then you know that 2 70 operations (that is, one minute BCN) are enough to crack the 1-kilobyte RSA key, and 2 90(one year BCN) - to crack the 2-kilobit key. Both values ​​should cause our concern - this is what can be achieved with existing technologies. That is why I strongly recommend that you always set the key size yourself, and make it at least 3 kilobits in size, and if performance allows you, then 4.
In .NET, it is not so easy to figure out how to export public and private keys.
At the top of the slide, you see two instances of the RSA key, the first from RSACryptoServiceProvider, the second fromRSACng, each of 4 kilobits. Below is the code by which the public and private key is extracted from both instances. Here you should pay attention to the fact that both APIs are quite different from each other - different code, different methods, different parameters. Further, if we compare the size of the public keys of the first and second copies, we will see that they are comparable, approximately half a kilobyte each. But the private key of the new RSA implementation is much smaller than the old one. It is necessary to keep in mind and observe uniformity, not to interfere with these two APIs with each other.
Everything that we have done with RSA so far has come down to attempts to get a working copy; Now we will try to encrypt something.
Create an array of bytes that will be our plaintext (data), and then encrypt it using one of those add-on modes that did not throw an exception. But this time we will have an exception. This is the exception of an invalid parameter; but what parameter are we talking about? I have no idea - and Microsoft, most likely, too. If we try to run the same method with other add-on modes, then in each case we get the same exception. So it's not about add-on mode. So the problem is in the source code itself. It’s hard to say what’s wrong with him, so we’ll try to cut it in half, just in case. This time the encryption is successful. We remain at a loss.
Perhaps the whole point is that we used the SHA-1 supplement? SHA-1, as we know, is no longer a cryptographically strong function, so our auditors and the compliance department insist that we get rid of it. Replace OaepSHA1on OaepSHA256, at least it will calm auditors.
But when trying to encrypt, we again get the exception of the wrong parameter. This whole situation is caused by the fact that the limit on the size of the text that can be transferred to a cryptographic function depends not only on the mode of addition, but also on the size of the key.
Let us try to find out exactly how that magic formula looks like, which determines the maximum amount of encrypted data. It must be in the method.int GetMaxDataSizeForEnc(RSAEncryptionPadding pad)which calculates this volume, having received an addition mode on an input. The main disadvantage of this method is that it does not exist, I invented it. I'm trying to convey the idea that even the most basic information needed by the developer for the correct use of RSA is not available to us. Thank you, Microsoft.
I will give reasons why RSA should be avoided, even for a signature. As I hope, I managed to show, the API for RSA in .NET is extremely unsatisfactory. You are forced to make many decisions regarding the mode of addition, the size of the data and the like, which is undesirable. Further, for a 128-bit security level, you will need at least a very cumbersome 4-kilobit key. It will give you a kilobyte private key, a half-kilobyte public key, and a half-kilobyte signature. For many scenarios such values ​​may not be desirable. And if you try to achieve a 256-bit security level, you will need a huge key at all - 15360 bits. In RSA, using such a key is almost impossible. On my laptop one such key is generated one and a half minutes.In addition to this, RSA at the fundamental level, as an algorithm, very slowly implements a signature, regardless of implementation. Why is signature speed important to us? If you use TLS with RSA certificates, then the signature is performed on the server. And we, as developers, are most concerned with exactly what happens on the server, we are responsible for it, its bandwidth is important to us. Summarizing, I want to once again recommend not to use RSA.I want to once again recommend not to use RSA.I want to once again recommend not to use RSA.
In this case, what to replace the RSA? I would like to introduce you to modern elliptical cryptographic primitives. First of all, you should keep in mind the ECDSA (Digital Signature Algorithm) algorithm, which can be used instead of RSA for signatures. In this and the following abbreviations, EC is a common prefix, which stands for Elliptic-Curve (“elliptic”). Under the link securitydriven.net/inferno/#DSA Signatures you can find an example of ECDSA code, which, by the way, is native to .NET. Another important algorithm is ECIES (Integrated Encryption Scheme, "elliptical integrated encryption scheme"). This algorithm can perform hybrid encryption instead of RSA, that is, where you generate a symmetric key, encrypt the data with it, and then encrypt the key itself.Sample code is available at the securitydriven.net/inferno/#ECIES example link. Finally, another very important algorithm is ECDH (Diffie-Hellman key exchange, “Diffie-Hellman key exchange”). It allows you to create keys for symmetric encryption between two parties with known public keys. In some situations and ways of using it, it allows to achieve direct secrecy (forward secrecy ). Sample code is available via the link securitydriven.net/inferno/#DHM Key Exchange.
Let's sum up the conversation about asymmetric encryption. You should always use high-level APIs that do not force you to make decisions for which you are not ready. I would also recommend to stop using RSA. Of course, this is easier said than done, since we are all working with large applications that have already been created, which can be completely refactored. In this case, at least you need to learn how to use RSA correctly. Next, I advise you to get acquainted with modern elliptical cryptographic algorithms (ECDSA, ECDH, ECIES). Finally, it is important that high-level cryptography does not magically solve all the problems, so you need to remember the goals you are pursuing. I will quote from StackOverflow, with which I fully agree: “Cryptography alone does not solve problems.Symmetric encryption only turns the issue of data privacy into a key management problem. ”
I will say a few words about resources that may be useful to you. There is a relatively acceptable high-level securityDriven.Inferno library with good documentation. There is an excellent book “Serious Cryptography” by Jean-Philippe Aumasson (Serious Cryptography). It provides an overview of the current state of cryptography, taking into account the latest innovations. In addition, I have written the already mentioned book Application Security in .NET, Succinctly, which is in the public domain. It has even more information about security traps in .NET. Finally, Slideshare has an excellent presentation by Vladimir Kochetkov, which is somewhat simplistic, but very soundly, sets out the basics of the theory of application security and explains various sources of hazards.
As a conclusion, let's see some additional examples that I have prepared. At the very beginning, I spoke about the fourth stage of crypto-enlightenment, in which comes the realization that the best solution may not need cryptography at all. Let's look at an example of such a solution. Take a look at the classic .NET - CSRF (Cross-Site Request Forgery, “cross-site request forgery”) mechanism designed to protect against a class of attacks, including cross-site request forgery. In this model, we have a user agent - usually a browser. He is trying to establish a connection with the server by sending a GET request. In response, the server sends a CSRF token, which is hidden in the HTML field “hidden”. In addition, the same token is attached to the response as a cookie, as a header.The user processes some form and performs POST, which is returned to the server with both tokens. The server checks, firstly, whether both tokens are sent, and, secondly, whether they are. It is this identity comparison that allows the server to protect itself against an intruder. This is a classic mechanism built into ASP.NET and ASP.NET Core. Mikhail Shcherbakov made an excellent report in which the work of the CSRF was examined in detail.
The problem with this approach is that CSRF token generation uses encryption. The difficulty is that encryption itself is a complex and resource-intensive operation, it loads the processor, requires memory, increases latency. All this is undesirable. In addition, the injection of a token is a cumbersome, confusing and inconvenient process. In many cases — for example, using AJAX, asynchronous calls — its implementation lies with you as the developer. Those who did it, know that this occupation is extremely unpleasant.
The same or comparable crypto can be created without the use of encryption, how - shown on the slide. I understand that the text here is quite complicated, so I am ready to discuss it in more detail in the discussion area. I have it all, thank you very much.