-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathrsa.lean
More file actions
226 lines (214 loc) · 7.44 KB
/
rsa.lean
File metadata and controls
226 lines (214 loc) · 7.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
/-
-----------------------------------------------------------
Correctness of RSA public-key encryption scheme
RSA encryption undoes decryption as originally
described in the [RSA paper](https://people.csail.mit.edu/rivest/Rsapaper.pdf).
Original file: https://github.com/aronerben/lean-rsa/blob/main/src/rsa.lean
-----------------------------------------------------------
-/
import data.nat.basic
import data.nat.modeq
import data.nat.prime
import data.nat.totient
import field_theory.finite.basic
variables (a b c d n : ℕ)
-- Helpers for proof
lemma mod_pow_mod : ((a % n) ^ b) % n = (a ^ b) % n :=
begin
-- Use either change here or write MOD form directly as goal
change (a % n) ^ b ≡ (a ^ b) [MOD n],
apply nat.modeq.pow,
exact nat.mod_modeq _ _,
end
lemma mul_mod_right_distrib : (a * b) % n = ((a % n) * (b % n)) % n :=
begin
change (a * b) ≡ a % n * (b % n) [MOD n],
apply nat.modeq.mul,
repeat {apply nat.modeq.symm, exact nat.mod_modeq _ _},
end
variables (msg pub_e p q priv : ℕ)
-- Encryption
def enc : ℕ := (msg ^ pub_e) % (p * q)
-- Decryption
def dec (enc : ℕ) : ℕ := (enc ^ priv) % (p * q)
lemma mul_coprime_or_coprime_and
(prime_p : p.prime)
(prime_q : q.prime)
(diff_pq : p ≠ q)
: (p.coprime msg ∧ q.coprime msg) ∨ (¬p.coprime msg ∨ ¬q.coprime msg) :=
begin
by_cases h : (p * q).coprime msg,
{ left,
exact ⟨nat.coprime.coprime_mul_right h, nat.coprime.coprime_mul_left h⟩,
},
{
right,
rw nat.prime.not_coprime_iff_dvd at h,
cases h with k ncp,
cases ncp with prime_k ncp,
cases ncp with k_dvd_pq k_dvd_msg,
rw nat.prime.dvd_mul prime_k at k_dvd_pq,
cases k_dvd_pq with k_dvd_p k_dvd_q,
-- TODO use meta tactics to combine these two similar parts
{ rw nat.dvd_prime prime_p at k_dvd_p,
cases k_dvd_p with k_one k_p,
{ have k_ne_one : k ≠ 1 := nat.prime.ne_one prime_k,
exact absurd k_one k_ne_one,
},
{ rw k_p at k_dvd_msg,
left,
rw ←nat.prime.dvd_iff_not_coprime prime_p,
assumption,
},
},
{ rw nat.dvd_prime prime_q at k_dvd_q,
cases k_dvd_q with k_one k_q,
{ have k_ne_one : k ≠ 1 := nat.prime.ne_one prime_k,
exact absurd k_one k_ne_one,
},
{ rw k_q at k_dvd_msg,
right,
rw ←nat.prime.dvd_iff_not_coprime prime_q,
assumption,
},
},
},
end
theorem dec_undoes_enc
-- These are the picking requirements
(prime_p : p.prime)
(prime_q : q.prime)
(diff_pq : p ≠ q)
(one_lt_pub_e : 1 < pub_e)
(msg_lt_pq : msg < (p * q))
(pub_e_lt_totient : pub_e < (p * q).totient)
(pub_e_coprime_totient : pub_e * priv ≡ 1 [MOD (p * q).totient])
(h : (p * q).coprime msg ∨ ¬(p * q).coprime msg)
: msg = dec p q priv (enc msg pub_e p q) :=
begin
have msg_coprime_or_not := mul_coprime_or_coprime_and msg _ _ prime_p prime_q diff_pq,
rw [enc, dec, mod_pow_mod, ←pow_mul],
have msg_zero_or_gt : 0 = msg ∨ 0 < msg
:= nat.eq_or_lt_of_le (nat.zero_le msg),
rw nat.modeq.comm at pub_e_coprime_totient,
have one_le_mul_pub_e_priv : 1 ≤ pub_e * priv
:= begin
apply nat.modeq.le_of_lt_add pub_e_coprime_totient _,
apply nat.lt_add_left,
exact nat.lt_trans one_lt_pub_e pub_e_lt_totient,
end,
cases msg_zero_or_gt with eq_zero gt_zero,
{ rw [←eq_zero, zero_pow one_le_mul_pub_e_priv],
simp only [nat.zero_mod]},
{ conv
begin
to_lhs,
rw ←nat.mod_eq_of_lt msg_lt_pq,
end,
have p_coprime_q : p.coprime q
:= (nat.coprime_primes prime_p prime_q).mpr diff_pq,
rw [←nat.modeq, ←nat.modeq_and_modeq_iff_modeq_mul p_coprime_q],
split,
all_goals { rw nat.modeq_iff_dvd' one_le_mul_pub_e_priv at pub_e_coprime_totient,
cases pub_e_coprime_totient with k h₁,
have h₂ : (pub_e * priv - 1).succ = ((p * q).totient * k).succ
:= congr_arg nat.succ h₁,
rw [nat.sub_one,
nat.succ_pred_eq_of_pos (nat.lt_of_succ_le one_le_mul_pub_e_priv)]
at h₂,
rw h₂,
rw [pow_succ,
pow_mul,
nat.modeq, mul_mod_right_distrib,
←mod_pow_mod],
},
show msg % q = msg % q * ((msg ^ (p * q).totient % q) ^ k % q) % q,
rw mul_comm p,
show msg % p = msg % p * ((msg ^ (p * q).totient % p) ^ k % p) % p,
all_goals {
cases msg_coprime_or_not with coprime ncoprime,
{ rw [nat.totient_mul p_coprime_q,
pow_mul,
←mod_pow_mod _ q.totient]
<|> rw [nat.totient_mul (nat.coprime.symm p_coprime_q),
pow_mul,
←mod_pow_mod _ p.totient],
have h₃ := nat.modeq.pow_totient,
rw nat.modeq at h₃,
rw h₃ (nat.coprime.symm coprime.1)
<|> rw h₃ (nat.coprime.symm coprime.2),
repeat {rw nat.mod_eq_of_lt (nat.prime.one_lt prime_p)
<|> rw nat.mod_eq_of_lt (nat.prime.one_lt prime_q)
<|> rw one_pow},
simp only [mul_one, nat.mod_mod],
},
},
-- TODO use meta tactics to combine these two similar parts
{ cases ncoprime with np nq,
{ rw [nat.totient_mul p_coprime_q,
pow_mul,
←mod_pow_mod _ q.totient],
rw ←nat.prime.dvd_iff_not_coprime prime_p at np,
rw [←nat.modeq_zero_iff_dvd, nat.modeq] at np,
rw [mul_mod_right_distrib, np],
simp only [nat.zero_mod, zero_mul],
},
{ have msg_coprime_p : msg.coprime p := begin
rw [nat.coprime_comm, nat.prime.coprime_iff_not_dvd prime_p],
rw ←nat.prime.dvd_iff_not_coprime prime_q at nq,
exact begin
intro p_dvd_msg,
have pq_dvd_msg
:= nat.prime.dvd_mul_of_dvd_ne
diff_pq prime_p prime_q p_dvd_msg nq,
have pq_not_dvd_msg
:= nat.not_dvd_of_pos_of_lt gt_zero msg_lt_pq,
exact absurd pq_dvd_msg pq_not_dvd_msg,
end,
end,
rw [nat.totient_mul p_coprime_q,
pow_mul,
←mod_pow_mod _ q.totient],
have h₃ := nat.modeq.pow_totient,
rw nat.modeq at h₃,
rw h₃ msg_coprime_p,
repeat {rw nat.mod_eq_of_lt (nat.prime.one_lt prime_p)
<|> rw one_pow},
simp only [mul_one, nat.mod_mod],
},
},
{ cases ncoprime with np nq,
{ have msg_coprime_q : msg.coprime q := begin
rw [nat.coprime_comm, nat.prime.coprime_iff_not_dvd prime_q],
rw ←nat.prime.dvd_iff_not_coprime prime_p at np,
exact begin
intro q_dvd_msg,
have pq_dvd_msg
:= nat.prime.dvd_mul_of_dvd_ne
diff_pq prime_p prime_q np q_dvd_msg,
have pq_not_dvd_msg
:= nat.not_dvd_of_pos_of_lt gt_zero msg_lt_pq,
exact absurd pq_dvd_msg pq_not_dvd_msg,
end,
end,
rw [nat.totient_mul (nat.coprime.symm p_coprime_q),
pow_mul,
←mod_pow_mod _ p.totient],
have h₃ := nat.modeq.pow_totient,
rw nat.modeq at h₃,
rw h₃ msg_coprime_q,
repeat {rw nat.mod_eq_of_lt (nat.prime.one_lt prime_q)
<|> rw one_pow},
simp only [mul_one, nat.mod_mod],
},
{ rw [nat.totient_mul (nat.coprime.symm p_coprime_q),
pow_mul,
←mod_pow_mod _ p.totient],
rw ←nat.prime.dvd_iff_not_coprime prime_q at nq,
rw [←nat.modeq_zero_iff_dvd, nat.modeq] at nq,
rw [mul_mod_right_distrib, nq],
simp only [nat.zero_mod, zero_mul],
},
},
},
end