Theorem: if p is prime, then for any x not congruent to zero, there exists y so x*y ≡ 1 (mod p).

Z_{p} is a **field** with
operations +,*

Z_{p} - {0} is a **group** with
operation *

A **field** is an algebraic structure with operator + and
*, with commutative, associative and distributive laws, for which every
nonzero element has a multiplicative inverse.

A **group** is an algebraic structure with an associative
operation in which every element has an inverse. The structure here is Z_{p}
- {0}, of size p-1, and operation here is multiplication. Note p must be
prime; 3 has no inverse mod 6.

Fermat's little theorem a^{p-1} ≡ 1 (mod p), for all a not
congruent to 0

2^{n-1} mod n for n from 2 to 13.

n | 2^{n-1} - 1 |
divisible by n? |

2 | 1 | 2 ≡ 0 mod 2 |

3 | 3 | 3*1 |

4 | 7 | no |

5 | 15 | 5*3 |

6 | 31 | no |

7 | 63 | 7*9 |

8 | 127 | no |

9 | 255 | no |

10 | 511 | no |

11 | 1023 | 11*93 |

12 | 2047 | no |

13 | 4095 | 13*315 |

14 | 8191 | no |

Z_{13} example: 3 has order 3, 5 has order 4 (the **order**
of a, mod p, is the smallest k for which a^{k}≡1 mod p)

3, 9, 27≡1

5, 25≡-1, -5≡8, 40≡1

Binomial-theorem proof

# returns the list a, a*a, a^3, ..., a^n, all mod n

def powerlist(a,n):

lis = [0]*n

lis[0] = a % n

for i in range(1,n):

lis[i] = a*lis[i-1] % n

return lis

a*b ≡ 0 mod 15

a|bc, (a,b) = 1 => a|c

Proof: Note ∃k ka=bc. Also ∃x,y xa+yb=1. At this point c = cxa + cyb = a(cx + ky), so a|c

Group-theoretic proof of Fermat's "little" theorem ∀a≠0 a^{p-1}
≡ 1 (mod p)

The **order** of a ∈ Z_{p} is the smallest k>0
such that a^{k} ≡ 1 (mod p)

There always is such a k. The set {a, a^{2}, a^{3}, ... }
cannot be infinite, so there are i and j, i<j, so a^{i} ≡ a^{j}.
But then a^{j-i} ≡ 1 mod p.

The next step is to prove that order(a) divides p-1. That's it!

Let A = {a, a^{2}, a^{3}, ... }. We show that we can
partition Z_{p} - {0} = {1,...,p-1} into multiple disjoint sets
each the same size as A. That proves |A| divides p-1.

Z_{13} example 3 has order
3, 5 has order 4. A = A_{5} = {5, 12, 8, 1}.

2A = {10, 11, 3, 2}

3A = {2, 10, 11, 3}

4A = {7, 9, 6, 4}

5A = {12, 8, 1, 5}

6A = {4, 7, 9, 6}

7A = {9, 7, 4, 7}

How can Alice and Bob exchange *public* data and end up with a
number that each knows, but which is not something an eavesdropper can
find?

Let p=10007 and let g=5. I picked g because it has order p-1 modulo p.

Alice picks a, and Bob picks b, at random, say a=2418 and b = 5071.

Alice calculates A = g^{a} and Bob calculates B= g^{b},
both mod p. This can be done quickly with repeated squaring.

def power(x,e,n): # computes x^e mod n pow = 1 while e>0: if e%2 == 1: pow = pow*x % n x = x*x % n e = e//2 # // denotes integer division return pow

Alice gets A=1244 and Bob gets B=1994. They then exchange these values. Anyone can eavesdrop to get A and B, and g and p are public. But Alice also knows a, and Bob knows b, and nobody else knows either of these.

Then Alice calculates B^{a} = 2479 and Bob calculates A^{b}
= 2479.

Why are these equal? Both are equal to g^{a*b}.

Now suppose Mal wants to figure out this number. ("Mal" is for "bad";
sometimes "Eve" for "eavesdropper" is also used.) Mal knows g^{a}.
However, to retrieve a from this is pretty much trial and error; there is
no efficient way to calculate "logarithms" mod p. There are 10006 values
to try. Mal could do that. But what if p is one of the 1024-bit primes
below:

p1=114902778697775519243807582051986688206847636430271634702090657275218782716669464081032413884549344387394094087259661654561022881327078127526384809970858979402103006488401086094663281066582049708990541731967260736593494404957317697229498732453262923700475346089068944768390560557693732452373340611891958308767

p2=129651718124678942765016021538496745064647313888372133585127299150859811165629622930013191157953033177630211720672149680439864625436496084772520694105903693008684888736409307947154035655552124537557394420237669184331457034861315538355045456127862243908432618239395413896768931129135699998148774027350339433807

p3=151411492943903525609159107145531406770633646696963185633236924633911832081036003031702063446653228002679225218550123734827047095217950004631562077061399454195110745548303886078631576580775359988946688323293943535764774586171656801705899544663796306356213080590904250975733938158955953730614248028384783770231

p4=161544667656054348363670806479142750827145269232707609926486480974617939756921910563751432024988427071665740937104815499857383413860057098052108342878109563493606496842708022250662702555047793114379471958451772222496672257164937505127026521511592892670705383853224735942627931798116107444487204168945425126021

At this point trial-and-error is impossible. But Alice and Bob can still
calculate g^{a} and g^{b }very quickly!

But how can somebody quickly determine that the values above are in fact primes? It turns out there are fast primality checks. But nobody has discovered a fast logarithm calculation.

What can we do modulo 35? For multiplications to have inverses, we need numbers a that are relatively prime to 35. (For primes, that's all the numbers from 1 to p-1 automatically). For 35, we have to skip multiples of 5 and of 7: {0,5,7,10,14,15,20,21,25,28,30}. The number of values remaining is (5-1)*(7-1) = 24.

Homework 6:

induction: make Inductive Hypothesis explicit!

x^{2} - 6x + 5: I factored this wrong earlier!

Pick primes p and q that are quite large. Let N=pq. The primes p and q
must be secret, unlike with DHM. Also pick an exponent e, *eg* e=3
or e=65537 which is relatively prime to p-1 and q-1. Next, use the
Euclidean algorithm to find a d so e*d ≡ 1 mod (p-1)(q-1). The e is the **public
key** (along with N); d is the **private key**.

It turns out that, for any integer m, m^{(p-1)(q-1)} ≡ 1 mod N.
(Fermat's theorem says m^{(p-1)} ≡ 1 mod p and m^{(q-1)}
≡ 1 mod q; the result follows with a little algebra.) This means m^{ed}
= (m^{e})^{d} ≡ 1 mod p (why?).

To encrypt a message m, let c ≡ m^{e}. Someone with d can decrypt
this via c^{d} = m^{ed} ≡ m mod N.

Anyone who knows p and q can easily find d. And p and q are in principle
straightforward to find by factoring N. **But factoring N is
extremely hard**.

All the operations here are O(log p), that is proportional to the number
of digits in p. **But factoring is not**. The obvious way to
factor is O(p^{1/2}). A faster method is the "general
number field sieve"; for n ~ 2^{1000} the performance
there works out to around 25 million, and for n ~ 2^{2000} it
works out to ~ 2 billion.

The idea is to start with a cubic "curve" mod p: x^{3} = y^{2}
+ ay + b, where p, a and b are picked in advance. There are ~p^{2}
many points (x,y), and typically around p solutions. For any two
solutions, we can find a third on the straight line through the first two;
this is the basis for a **group** product. Now we use that
group for DHM and encryption.

Curve25519 uses
the prime p = 2^{255} - 19, and the curve y^{2} = x^{3}
+ 486662x^{2} + x

See pld.cs.luc.edu/courses/163/spr22/mnotes/probability.html.