Public-Key Encryption (Optional)

City University of Hong Kong

CS1302 Introduction to Computer Programming


This notebook will introduce the idea of public-key (asymmetric key) cryptography called the RSA algorithm.

Motivation

What is public key encryption?

Suppose there is one receiver trying to receive private messsages from multiple senders.

Solution to Exercise 1
  • Easy to share keys: Encryption key can be shared to all senders publicly.
  • Easy to store keys: Only the receiver needs to know the private key.

Is public key encryption possible?

Unfortunately, public key encryption is an invertible function given a public key:

YOUR ANSWER HERE

How to make public key encryption secure?

We can make it computationally infeasible to invert E(,ke)E(\cdot, k_e) unless with the private key kdk_d is available. Such a function is called the trapdoor function. An example of a trapdoor function is:

As the size of pp and qq increases, the time required to factor nn increases dramatically as illustrated here.

RSA Algorithm

How to encrypt/decrypt?

The encryption and decryption use modulo exponentiation instead of addition:

E(x,ke):=xemodnwhere ke:=(e,n)D(c,kd):=cdmodnwhere kd:=(d,n),\begin{align} E(x, k_e) &:= x^e \bmod n && \text{where }k_e:=(e,n)\\ D(c, k_d) &:= c^d \bmod n && \text{where }k_d:=(d,n), \end{align}

and ee, dd, and nn are positive integers.

Computing exponentiation can be fast using repeated squaring. The built-in function pow already has an efficient implementation:

%%timeit
x, e, n = 3, 2 ** 1000000, 4
pow(x, e, n)
%%timeit
def modulo_power(x, e, n):
    # YOUR CODE HERE
    raise NotImplementedError()


x, e, n = 3, 2 ** 1000000, 4
pow(x, e, n)

How to ensure decryptability?

For x=D(E(x,ke),kd)=xedmodnx = D(E(x, k_e), k_d) = x^{ed} \bmod n, we need 0x<n0\leq x < n and

xedxmodn.x^{ed} \equiv x \mod n.
Solution to Exercise 4
x=D(E(x,ke),kd)=(xemodn)dmodn=xedmodn.\begin{align} x &= D(E(x, k_e), k_d) \\ &= (x^{e} \bmod n)^{d} \bmod n \\ &= x^{ed} \bmod n. \end{align}

RSA makes use of the following result to choose (e,d,n)(e, d, n):

There are elegant and elementary combinatorial proofs.

Since (7) implies xp=xmodpx^p = x \bmod p, can we choose

  • n=pn=p and
  • ed=ped=p

to satisfies (5)?

No because the private key can then be easily computed from the public key: d=n/ed = n/e.

Alternatively, by raising (7) to the power of any integer mm,

xm(p1)1modp.x^{m(p-1)} \equiv 1 \mod p.

Can we have n=pn=p and ed1modp1ed \equiv 1 \bmod p-1?

No because dd is the modular multiplicative inverse of ee, which is easy to compute, e.g., using pow with an exponent of -1. In particular, for prime modulus here, the inverse is d=ep2modpd=e^{p-2}\bmod p:

e, n = 3, 7
d = pow(e, -1, n)
d, e * d % n == 1 and d == e ** (n - 2) % n

How to make it difficult to compute dd?

RSA makes use of the hardness of factoring a product of large primes to create the desired trapdoor function.

In particular, with m(p1)m(p-1) in (8) being the least common multiple lcm(p1,q1)\operatorname{lcm}(p-1,q-1) for another prime number qq, we have

xlcm(p1,q1)1modpandxlcm(p1,q1)1modqby symmetry.\begin{align} x^{\operatorname{lcm}(p-1, q-1)} &\equiv 1 \mod p && \text{and}\\ x^{\operatorname{lcm}(p-1, q-1)} &\equiv 1 \mod q && \text{by symmetry.} \end{align}

This implies xlcm(p1,q1)1x^{\operatorname{lcm}(p-1, q-1)} - 1 is divisible by both pp and qq, and so

xlcm(p1,q1)1modpq.x^{\operatorname{lcm}(p-1, q-1)} \equiv 1 \mod p q.

Raising both sides to the power of any positive integer mm give:

Although dd is still the modulo multiplicative inverse of ee, it is with respect to lcm(p1,q1)\operatorname{lcm}(p-1, q-1), which is not easy to compute without knowing the factors of nn, namely pp and qq. It can be shown that computing dd is as hard as computing lcm(p1,q1)\operatorname{lcm}(p-1, q-1) or factoring nn.

How to generate the public key and private key?

By (12), we can compute dd as the modulo multiplicative inverse of ee. How to choose ee then?

We can choose any e{1,,lcm(p1,q1)}e \in \{1, \dots, \operatorname{lcm}(p-1, q-1)\} such that ee does not divide lcm(p1,q1)\operatorname{lcm}(p-1, q-1).

YOUR ANSWER HERE

The following function randomly generate the e, d, n for some given prime numbers p and q:

from math import gcd
from random import randint


def get_rsa_keys(p, q):
    n = p * q
    lcm = (p - 1) * (q - 1) // gcd(p - 1, q - 1)
    while True:
        e = randint(1, lcm - 1)
        if gcd(e, lcm) == 1:
            break
    d = pow(e, -1, lcm)
    return e, d, n, lcm

Note that

lcm(p1,q1)=(p1)(q1)gcd(p1,q1).\operatorname{lcm}(p-1, q-1) = \frac{(p-1)(q-1)}{\operatorname{gcd}(p-1, q-1)}.

As an example, if we choose two prime numbers p=17094589121p=17094589121 and q=1062873761q=1062873761:

e, d, n, lcm = get_rsa_keys(17094589121, 1062873761)
e, d, n, e * d % lcm == 1

The integer 13021302 can be encrypted as follows:

x = 1302
c = pow(x, e, n)
print(f'The plain text "{x}" has been encrypted into "{c}".')

With the private key kdk_d, the ciphertext can be decrypted easily as

output = pow(c, d, n)
print(f'The cipher "{c}" has been decrypted into "{output}".')