|| Date: 18-12-25 || Back to index ||

|| Tag: book-summary ||

|| Author: Jean-Philippe Aumasson ||

Serious Cryptography, by Jean-Philippe Aumasson, runs through the common encryption and authentication protocols that a modern web-surfer would interact with on a daily basis. Our author has not only written a great primer for the uninitiated, but a valuable resource for practiced developer. The book runs through the basic terms in cryptography from block/stream ciphers, to advanced concepts such as RSA, TLS, and the sort of attacks that could be applied to which.

It is worthy to note that the title of the book, namely the words “Practical Introduction” betrays the content of the book. Although each chapter and new concept includes a Python snippet which can be executed and is very much relevant to the text, I found it to be a bit irrelevent for the usages of a software developer or security professional. To give an example, I learned much more about EAPOL and wifi handshakes by looking at Wireshark packets than by studying the cryptography behind it. Furthermore, I found the exercises in Sam Bowne’s online class ^{1} much, much more helpful than the Python snippets in the book.

I would suggest the reader to very much give this book a try, but feel free to skip the sections that seem a bit daunting, especially the ones on Quantum Cryptography. Afterwards, take the sections you are interested in and find a practical application/example/project to interact with them in the wild. For example, one can understand RSA by actually using both their properties: signing and encryption, with a tool like `gpg`

. Here’s a useful cheat sheet to start with. The projects in the CNIT course I linked should have enough examples to better understand the material.

*Informational Security* means that an algorithm is secure regardless of duration *Computational Security* means that an algorithm is secure since a lot of smart crypto folk could not decrypt it in sufficient time

So the level of complexity for an algorithm can be understood as `e = (t/x)-secure`

where t is the number of tries over the total number of necessary tries to break it. So a 128-bit key would require 2^128 number of possible tries to get it once. So that algorithm is (t/t^128)-secure.

The difference between an 80-bit key and a 128-bit key is like the difference between a mission to Mars and a mission to Alpha Centauri. – Quote by NIST Cryptographer John Kelsey.

The above quote should highlight that there will be a point where a practical brute-force attack is not feasible.

Ways to generate keys: - Randomly - Through a password - Through an exchange protocol

Randomly can only work if the keys are symmetric. For asymmetric keys, the public key has to be generated from the private key.

Another possible solution is generation of keys from a password through a ‘key derivation algorithm’.

There’s also method of ‘on the fly’ key generation from a password where the password is entered and a key is generated immediately. This is a bit immature since one key make due with an encryption algorithm that simply uses a password instead of a key.

For HTTPS, a key exchange protocol occurs. First the website sends it’s Public key and a temporary session key is established.

A **Block Cipher** is a symmetric encryption that works by encrypting plaintext block P with key K to produce ciphertext C. So, `C = E(K, P)`

, where E is the encryption algorithm. It also works backwards in the same fashion by having `P = D(K, C)`

, where D is the decryption algorithm.

The most famous block ciphers are AES and it’s predecessor DES, which was the standard for the US government from the 70’s well into 2005.

AES comes in 128 and 256-bit flavours but the differences between them is negligible since it is still hard to beat a 128-bit version.

A stable attack was the ‘codebook attack’ which basically allows the attacker to make a lookup table of every possible ciphertext and it’s plaintext counterpart. For a 16-bit cipher, this is pretty easy to break since you only need `2^16 * 16 = 128`

kilobytes of size. Increased size would yield increased memory footprint and then it won’t be viable.

A **Key** is something that must always be secured. If it was exposed or easily derived, it would defeat the purpose of the encryption.

An **IV**, or Initialization Vector or Initial Value is the bits that would start the process of encryption. For example, a hashing function must have a fixed IV for it to be started. This IV is something that should be shared between both recipient and sender.

A **Nonce** is short for Number Only Used Once. The only thing needed here is that a nonce is to be used only once since using it more than one time would first of all defeat its purpose and would make the ciphertext not as random ref ^{2}.

A Block Cipher takes a key and mashes it with a plaintext to create a ciphertext. The reverse is possible for decryption.

A **Stream Cipher** takes a key and extracts pseudorandom bits from it which is XORed with the plaintext to create the ciphertext. Those pseudorandom bits are called ‘keystream’ and they are generated from a nonce N and a key K.

So a Stream Cipher SC would be:

`KS = SC(K, N) C = P (XOR) KS P = C (XOR) KS`

The idea here is **Trapdoor Permutation**. Basically a mathematical equation relating to multiplication of large integers that would prove relatively cheap to do one-way, but very expensive to do without a ‘secret exponent’ the other way.

There’re two modes for RSA: Signing and Encryption

Basically have the private key ‘sign’ a block of bytes. Now given a public-key, that block of bytes is sure to be authentic that it came from that public key.

Have a block of bytes encrypted with a public key, where only the private key can decrypt the data

The idea works by utilizing the natural complexity of **Factorization**. An example of factorization is given *n* where `n = pq`

and *p* and *q* are prime numbers, find out the prime numbers *p* and *q*. For example if n = 15, we would know easily that p and q are 3 and 5, but given a larger number, it becomes efficitvely very computationally heavy.

The same plaintext would result in the same ciphertext, so a padding algorithm like OAEP should be utilized.

This involves MAC (Message authentication codes) and PRF (pseudorandom Functions).

The idea here is to have a hashing function be used in concurrence with a key so as to produce a MAC. For example:

`MAC = Hash(K || M)`

Above is what is called a ‘secret-prefix MAC’ which basically means that the key K has the bits of message M appended to its end.

There’s also secret-suffix MAC, which is the opposite.

The idea here is to send a MAC alongside a message to authenticate it’s validity. If only the receiver and sender know the key K, the message is authenticated.

The difference between an MAC and a PRF is that the main requirement for a MAC is that, during a session, no two MACs should be similar.

PRF, however, has a stricter requirement which is that each tag should be completely non distinguishable from a random blob.

A PRF is always a MAC, but a MAC is not necessarily a PRF. one can produce a MAC from an existing MAC by simply appending one 0 bit at the end.