# RSA

What is interesting about an RSA key:

The modulus is `N` and it is `p * q = N` through factorization. `p` and `q` are primes.

Coprime $\phi$ is calculated either by [Euler Totient](https://en.wikipedia.org/wiki/Euler's_totient_function) or [greatest common divisor](https://en.wikipedia.org/wiki/Greatest_common_divisor) via [euclidean algorithm](https://crypto.stanford.edu/pbc/notes/numbertheory/euclid.html).

There is:
$$
\phi(N) = (p-1)(q-1)
$$

and further

$$
1\ <\ \phi < N
$$

The public key is `(N, e)`. If you create a real key e.g. through OpenSSH, the default for `e` (encryption) is `65537` or `0x10001` in hex.

The private key is `(N, d)` and `d` (decryption) is the modular multiplicative inverse of `e` and $\phi$.

$$
Cipher = msg^{d}\ mod\ \phi
$$

$$
Cleartext = cipher^{e}\ mod\ \phi
$$


Further properties:

public key `e` is a prime between 2 and phi

$$
2 < e < \phi
$$

Private key `d` is the multiplicative inverse modulo $\phi(N)$

$$
1\ \equiv de\ mod\ \phi(N)
$$

$$
de\ \equiv 1\ mod\ \phi(N)
$$

This means `d` can be calculated by

$$
d \equiv\ e^{-1}\ mod\ \phi(N)
$$

---

`e` and `d` may be found through the following Python snippets

```python
possible_e = []
for i in range (2, phi):
    if gcd(n, i) == 1 and gcd(phi, i) == 1:
        possible_e.append()
```

```python
possible_d = []
for i in range (phi + 1, phi + foo):
    if i * e mod phi == 1 :
       possible_d.append()
```

## Euklid

Just a short excourse:
A greatest common divisior out of an example a = 32 and b = 14 would be the
groups of the following divisors

```sh
a = 32, b = 24
a = {1, 2, 4, 8, 16}
b = {1, 2, 3, 8, 12}
gcd(a,b) = 8
```

### Greatest Common Divisor (GCD)

Two values are prime and have themselves and only `1` as a divisor are called coprime.
To check if a and b have a greatest common divisor do the euclidean algorithm.

```python
def gcd(a, b):
    if b == 0:
        return a
    return gcd(b, a % b)
```

### Extended GCD

\#TODO

## Fermat`s Little Theorem

If modulus $p$ is a prime and and modulus $n$ is not a prime, p defines a
finite field (ring).
$$
n \in F_{p} \{0,1,...,p-1\}
$$

The field consists of elements $n$ which have an inverse $m$ resulting in $n +
m = 0$ and $n * m = 1$.

So , $n^p - n$ is a multiple of p then $n^p \equiv n\ mod\ p$ and therefore $ n
= n^p\ mod\ p$. An example
$$
4 = 4^{31}\ mod\ 31
$$

Further, $p$ while still a prime results in $1 = n^{p-1} mod\ p$. An example
$$
1 = 5^{11-1}\ mod\ 11
$$

### Modular Inverse

Coming back to the modular inverse $n$, it can be found in the following way
$n^{p-1} \equiv 1\ mod\ p$
$n^{p-1} * n^{-1} \equiv n^{-1}\ mod\ p$
$n^{p-2} * n * n^-1 \equiv n^{-1}\ mod\ p$
$n^{p-2} * 1 \equiv n^{-1}\ mod\ p$
$n^{p-2} \equiv n^{-1}\ mod\ p$

## Quadratic Residue

$m$ is a quadratic residue when $\pm n^2 = m\ mod\ p$ with two solutions.
Otherwise it is a quadratic non residue.

So a porperty of quad res are, if Quadratic Residue $QR = 1$ and Quadratic
NonResidue $QN = -1$

$$
QR * QR = QR\\
QR * QN = QN\\
QN * QN = QR\\
$$

## Legendre

$$
\frac{a}{p} =
\begin{cases}
1, & if\ a\ quadratic\ residue\ mod\ p\ and\ not\ a\ \equiv\ 0\ (mod\ p),\\
-1, & if\ a\ is\ a\ non\ residue\ mod\ p,\\
0, & if\ a\ \equiv 0\ (mod\ p)\\
\end{cases}
$$

$$
\frac{a}{p} \equiv a^{p-1/2}\ (mod\ p)\ and\ \frac{a}{p} \in \{-1,0,1\}
$$

Legendre Symbol test via Python with

```python
pow(a,(p-1)/2,p)
```

[Finding the square root of integer a which is quadratic residue](http://mathcenter.oxford.emory.edu/site/math125/findingSquareRoots/)

Given $p \equiv 3\ mod\ 4$ the square root is calculated through

```python
pow(a,((p+1)//4),p)
```

## Tonelli-Shanks - Modular Square Root

* Find elliptic curve co-ordinates
* Precondition: modulus is not a prime
* TBD

## RSA PublicKey Extraction

### Extract n and e from RSA public key

```python
from Crypto.PublicKey import RSA

with open("./id_rsa.pub", 'r') as _f:
    pub_k = RSA.importKey(_f.read())

print(f"n:\n{pub_k.n}\n")
print(f"\ne:\n{pub_k.e}\n")
```

### Extract p and q from PublicKey

Modified from [d4rkvaibhav](https://github.com/murtaza-u/zet/tree/main/20220808171808/README.md)

```python
from Crypto.PublicKey import RSA

with open("./id_rsa.pub", 'r') as _f:
    pub_k = RSA.importKey(_f.read())

def isqrt(n):
    x=n
    y=(x+n//x)//2
    while(y<x):
        x=y
        y=(x+n//x)//2
    return x

def fermat(n):
    t0=isqrt(n)+1
    counter=0
    t=t0+counter
    temp=isqrt((t*t)-n)
    while((temp*temp)!=((t*t)-n)):
        counter+=1
        t=t0+counter
        temp=isqrt((t*t)-n)
    s=temp
    p=t+s
    q=t-s
    return p,q

p,q = fermat(pub_k.n)
print(f"\np: {p}\n")
print(f"\nq: {q}\n")
print(f"\np-q: {p-q}\n")
```

### Generate PrivateKey

```python
from Crypto.PublicKey import RSA

with open("./id_rsa.pub", 'r') as _f:
    pub_k = RSA.importKey(_f.read())

def isqrt(n):
    x=n
    y=(x+n//x)//2
    while(y<x):
        x=y
        y=(x+n//x)//2
    return x

def fermat(n):
    t0=isqrt(n)+1
    counter=0
    t=t0+counter
    temp=isqrt((t*t)-n)
    while((temp*temp)!=((t*t)-n)):
        counter+=1
        t=t0+counter
        temp=isqrt((t*t)-n)
    s=temp
    p=t+s
    q=t-s
    return p,q

def extended_euclid(a, b):
    if a == 0:
        return b, 0, 1
    else:
        g, y, x = extended_euclid(b % a, a)
        return g, x - (b // a) * y, y

def modular_inverse(e, phi):
    g, x, y = extended_euclid(e, phi)

    if g != 1 :
        raise Exception("No modular inverse")
    else:
        return x % phi

p,q = fermat(pub_k.n)
phi = (p-1) * (q-1)
d = modular_inverse(pub_k.e, phi)


print(f"\np: {p}\n")
print(f"\nq: {q}\n")
print(f"\np-q: {p-q}\n")
print(f"\nd: {d}\n")

priv_k = RSA.construct((pub_k.n, pub_k.e, d))
with open ("./priv_id_rsa", "wb") as _f:
    _f.write(priv_k.export_key('PEM'))
```

## Links

* [Encryption+Decryption](https://www.cs.drexel.edu/~jpopyack/Courses/CSP/Fa17/notes/10.1_Cryptography/RSA_Express_EncryptDecrypt_v2.html)
* [Extended GCD](http://www-math.ucdenver.edu/~wcherowi/courses/m5410/exeucalg.html)
