Imagine you lean over and look at a cryptographer’s notebook. You see some notes in the margin:
1
4 + 9 = 1
2
5 - 7 = 10
3
2 + 3 = 5
At first you might think they’ve gone mad. Maybe this is why there are so many data leaks nowadays you’d think, but this is nothing more than modular arithmetic modulo 12 (albeit with some sloppy notation).
You may not have been calling it modular arithmetic, but you’ve been doing these kinds of calculations since you learnt to tell the time (look again at those equations and think about adding hours).
Formally, “calculating time” is described by the theory of congruences. We say that two integers are congruent modulo m if a ≡ b mod m.
Another way of saying this, is that when we divide the integer a by m, the remainder is b. This tells you that if m divides a (this can be written as m∣a) then a ≡ 0 mod m.
Calculate the following integers:
1
11 ≡ x mod 6
2
8146798528947 ≡ y mod 17
The solution is the smaller of the two integers, (x,y), you obtained after reducing by the modulus.
We’ll pick up from the last challenge and imagine we’ve picked a modulus p, and we will restrict ourselves to the case when p is prime.
NOTE
If the modulus is not prime, the set of integers modulo n define a ring.
A finite field Fₚ is the set of integers 0, 1, ..., p − 1, and under both addition and multiplication, there are inverse elements b₊ and bₓfor every element a in the set, such that: a + b₊ = 0 and a · bₓ = 1
NOTE
Note that the identity element for addition and multiplication is different! This is because the identity when acted with the operator should do nothing: a + 0 = a and a ⋅ 1 = a.
Let’s say we pick p = 17.
Calculate 3¹⁷ mod 17.
Now do the same but with 5¹⁷ mod 17.
What would you expect to get for 7¹⁶ mod 17
Try calculating that.
This interesting fact is known as Fermat's Little Theorem.
We’ll be needing this (and its generalizations) when we look at RSA cryptography.
Now take the prime p = 65537.
Calculate 27324678765⁴⁶⁵⁵³⁶ mod 65537.
Did you need a calculator
Solution :
1
# Fermat's Little Theorem demonstrations
2
p1 =17
3
4
# 3^17 mod 17
5
res1 =pow(3, 17, p1)
6
print("3^17 mod 17 =", res1)
7
8
# 5^17 mod 17
9
res2 =pow(5, 17, p1)
10
print("5^17 mod 17 =", res2)
11
12
# 7^16 mod 17
13
res3 =pow(7, 16, p1)
14
print("7^16 mod 17 =", res3)
15
16
# Now using the large prime p = 65537
17
p2 =65537
18
base =pow(27324678765, 4, p2)
19
res4 =pow(base, 65536, p2)
20
print("27324678765^4^65536 mod 65537 =", res4)
21
22
# This script shows that for a prime p and integer a not divisible by p:
p =101524035174539890485408575671085261788758965189060164484385690801466167356667036677932998889725476582421738788500738738503134356158197247473850273565349249573867251280253564698939768700489401960767007716413932851838937641880157263936985954881657889497583485535527613578457628399173971810541670838543309159139
Find a square root of a mod p using the Tonelli-Shanks algorithm.
4
p must be an odd prime.
5
"""
6
7
# Step 1: Find Q and S such that p-1 = Q * 2^S
8
S =0# Initialize S to 0
9
Q = p -1# Start with Q = p-1
10
while Q %2==0:
11
Q //=2# Keep dividing Q by 2 while it's even
12
S +=1# Count how many times 2 divides p-1
13
14
# Step 2: Find a quadratic non-residue z
15
# A quadratic non-residue z is a number such that z^(p-1)/2 ≡ -1 (mod p)
16
z =2# Start from z = 2 and check if it's a quadratic non-residue
17
whilepow(z, (p -1) //2, p) != p -1: # z^(p-1)/2 mod p should be -1 (p-1 mod p)
18
z +=1# Increment z until a non-residue is found
19
20
# Step 3: Initialize variables for the Tonelli-Shanks algorithm
21
M = S # Set M to S, which is the exponent of 2 in the factorization of p-1
22
c =pow(z, Q, p) # Compute c = z^Q mod p
23
t =pow(a, Q, p) # Compute t = a^Q mod p (this is used to track progress)
24
R =pow(a, (Q +1) //2, p) # Compute R = a^((Q+1)//2) mod p as the initial guess for the root
25
26
# Step 4: Loop until t == 1, which means we have found a square root
27
while t !=1:
28
# Find the smallest i such that t^(2^i) ≡ 1 mod p
29
i =0# Start the exponent count
30
temp = t # Use a temporary variable to check powers of t
31
while temp !=1:
32
temp =pow(temp, 2, p) # Compute t^(2^i) mod p by squaring t
33
i +=1# Increment i (the exponent of 2)
34
if i == M: # If we have tried all possible exponents, no root exists
35
returnNone# No square root exists, so return None
36
37
# Step 5: Update variables to improve the guess for the root
38
b =pow(c, 2** (M - i -1), p) # Compute b = c^(2^(M-i-1)) mod p
39
M = i # Update M to the current value of i
40
c =pow(b, 2, p) # Update c = b^2 mod p (this helps in the next iteration)
41
t = (t * c) % p # Update t = t * c mod p (this moves us closer to 1)
42
R = (R * b) % p # Update R to the new value, refining the root approximation
43
44
# When t becomes 1, R is the square root of a mod p
45
return R # Return the square root
46
47
# Given values for a and p
48
a =8479994658316772151941616510097127087554541274812435112009425778595495359700244470400642403747058566807127814165396640215844192327900454116257979487432016769329970767046735091249898678088061634796559556704959846424131820416048436501387617211770124292793308079214153179977624440438616958575058361193975686620046439877308339989295604537867493683872778843921771307305602776398786978353866231661453376056771972069776398999013769588936194859344941268223184197231368887060609212875507518936172060702209557124430477137421847130682601666968691651447236917018634902407704797328509461854842432015009878011354022108661461024768
49
p =30531851861994333252675935111487950694414332763909083514133769861350960895076504687261369815735742549428789138300843082086550059082835141454526618160634109969195486322015775943030060449557090064811940139431735209185996454739163555910726493597222646855506445602953689527405362207926990442391705014604777038685880527537489845359101552442292804398472642356609304810680731556542002301547846635101455995732584071355903010856718680732337369128498655255277003643669031694516851390505923416710601212618443109844041514942401969629158975457079026906304328749039997262960301209158175920051890620947063936347307238412281568760161
50
51
# Solve for the square root
52
sqrt_a = tonelli_shanks(a, p)
53
54
# Output the result
55
if sqrt_a isnotNone:
56
# If a square root exists, print the smaller root (between sqrt_a and p - sqrt_a)
57
smaller_root =min(sqrt_a, p - sqrt_a)
58
print(f"Square root found! The smaller root is:\n{smaller_root}")