RSA

RSA, named after its creators Ron Rivest, Adi Shamir, and Leonard Adleman, is an asymmetric cryptosystem that functions through the use of public-private key pairs. In this system a user wishing to receive messages generates both a public key and a private key, which are used for encryption and decryption respectively. The public key consists of the numbers e and n, and the private key consists of numbers d and n.

Here, n is the modulus, e is the public exponent, and d is the private exponent. The algorithms for encrypting and decrypting a message m and corresponding ciphertext c use a technique known as modular exponentiation as shown:

E is the encryption algorithm, and D is the decryption algorithm

Because RSA is a public-key cryptosystem, a message encrypted with a public key can only be decrypted using the corresponding private key. In order to receive encrypted messages an individual can widely share and distribute their public key, allowing for anyone to encrypt a message to send to them. However, these messages can only be decrypted by them.

Meeting the definition of a cipher, RSA follows the consistency equation being expressed as:

This just means that after encrypting a message m with a public key (e, n), the message can then only be decrypted with the corresponding private key (d, n) to recover the message. The magic of how this works comes from the very careful generation of the numbers n, e and d, which I will be covering shortly.

Additionally, to verify the authenticity of the source of a digital message, we can include a digital signature along with the message. As a message encrypted with a public key can only decrypted with the corresponding private key, it also holds that a message encrypted with the private key can only be decrypted using the corresponding public key. Therefore, if you can decrypt a message with someone’s public key, you can guarantee that it was created by the person who possessed the corresponding private key.

The typical way to do this is to calculate a message digest using a cryptographic hash function such as SHA-512, encrypt it with their private key, and concatenate it to the ciphertext being sent.

For example, if Alice wants to send a digitally signed message to Bob, Alice can encrypt the message with Bob’s public key, then encrypt a hash of the message with her own private key.

Here, || denotes concatenation.

To verify this digital signature, Bob can decrypt the message with his private key, create a hash of the message, decrypt the signature with Alice’s public key and compare the two to ensure that Alice was the one that wrote the message.

Ignoring signatures for now, here’s a simple example with some pre-calculated values. Say we want to encrypt the number 42 with a public key, where e=17 and n=3233. (Note that these numbers are very small for demonstration purposes, and are much much larger in practice).

The resulting ciphertext of this operation is the number 2557. We can now decrypt this message using the corresponding private key, where d=2753 and n=3233.

As demonstrated, this makes RSA fairly easy to understand for most use cases. However, the magic of how RSA works comes from how the public and private keys are generated, which tends to get more complicated.

RSA Key Generation

One of the primary ideas behind RSA’s security are actions that are easy to compute but impractical to do in reverse such as the technique of modulo exponentiation, which is near impossible to reverse in optimal implementations. Because of this, modulo exponentiation can be considered a Trapdoor function.

As covered earlier, the algorithm for decryption can be rewritten using index laws as follows:

As e is a component of the public key, and d is a component of the private key, we can think of e as the exponent for encryption, and d as the exponent for decryption. In order to make this system work, we must find a pair of numbers e and d that satisfy two very important conditions.

Raising a ciphertext (c or mᵉ) to the power of d must undo the original operation applied to the message, resulting in the original cleartext message (m). It must be (practically) impossible to calculate d just by knowing e and n.

In order to satisfy these conditions, we can turn to another trapdoor function known as prime factorisation.

As a trapdoor function, there is no known fast and efficient way to find the prime factors of a large number. However, multiplying two prime numbers together and finding the result is trivial to do. For example, the following question is very difficult to calculate by hand:

14078417 is the product of two prime numbers. What are those numbers?

However, rephrasing the question slightly makes it far easier to work it out manually:

What is 1801×7817?

Note that in this case, ‘14078417’ is a 24 bit number, and so can be factorised by a computer trivially. However finding the prime factors of the following 2048 bit number:

28054858894067023484001444540318453571018106101097278546878654383462804693324724266179068736999021606591696140306368101007527587969081983087482330183244549608668390742295364641677132049586501132135729395873453995217546629615237960162783190031657288708106646763170912197351177871907428727339739967246045009611849617088464032097342684035032810437813967876189592255029228354836446888654251720287338691938771818905976064989199133637615430931722685466870972608988899507683650966189062385231180105749751696410490948186037908771602234032431258650407531488547072384375563523617112604767003493676401338233014749354971625136557

would take — for all intents and purposes — an infinite amount of time with our current methods. With the current state of technology, I am likely the only person who will ever know the prime factors of the above number.

Owing this, we can use prime factorisation as a trapdoor function in this algorithm by finding a function that depends upon knowing the prime factors of a large number. We can do this by using Euler totient function.

Euler’s totient function, denoted φ(n), counts the amount of positive integers that are relatively prime to n. I.e, The number of integers in the range 1 ≤ k ≤ n where the greatest common divisor gcd(n, k) = 1. For example:

Here, φ(8) is 4, as 8 is relatively prime to 1, 3, 5, and 7. Additionally, φ(9) is 6, as 9 is relatively prime to 1, 2, 4, 5, 7, and 9.

In order to make this function useful, there are two very important properties we can look at:

If n is prime, φ(n) = n-1, as prime numbers have no factors greater than one. For example, φ(7) = 6, as 7 is coprime with 1, 2, 3, 4, 5 and 6. This holds true for any prime number.

2. Euler’s totient function is multiplicative, and as such:

Owing to this, we can combine these properties to derive a function that is only possible to calculate given a number’s prime factors. Therefore, if we have a significantly large number n which is equal to the product of the two prime numbers p and q, then:

As such, we now have a trapdoor function for φ(n) that is only computable if you know the prime factors of n. Now, we need to find a way to link the totient function to modular exponentiation. For this we can use Euler’s theorem, which states that for two numbers m and n, if n and m are coprime, then:

We can now rearrange this equation using simple index laws. As 1 to the power of any exponent is itself 1, we can rewrite the equation as:

Additionally, as any number m, multiplied by 1 is m, we can again rewrite this equation as:

Now, compare this to the original equation we are looking for,

Rearranging, we can now solve for d with the following equation:

In this equation, k can be substituted for a value such that d is a natural number. Additionally, e must be chosen such that it is odd, and does not share a factor with φ(n). What is so great about this equation is that to find d, the only variables used are n and e. However, for large numbers it is impossible to calculate without knowing the prime factors of n.

Using what we’ve gathered, we can generate an RSA public-private key pair (e, n) and (d, n) quite simply.