-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathotp.lean
More file actions
138 lines (102 loc) · 2.7 KB
/
otp.lean
File metadata and controls
138 lines (102 loc) · 2.7 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
/-
One-Time Pad correctness
-/
import data.zmod.basic
import data.bitvec.basic
namespace otp
/-
There are 2 ways to do it:
1- By modular arithmetic.
2- by bitwise arithmetic.
Difference is performance, bitwise should be faster to implement.
-/
namespace modulo
-- the mod we use is irrelevant, 26 is common but we keep it random.
variable mod : ℕ
-- secret message and key of the same size
variables p k : zmod mod
def encrypt : zmod mod := p - k
variable c : zmod mod
def decrypt : zmod mod := c + k
-- correctness
lemma dec_undoes_enc : p = decrypt mod (encrypt mod p k) k :=
begin
unfold encrypt,
unfold decrypt,
finish
end
-- lets have 2 random keys
variables k1 k2 : zmod mod
-- assuming the keys are always different,
-- then the ciphertext will be always different too.
lemma always_different (h: k1 ≠ k2) :
encrypt mod p k1 ≠ encrypt mod p k2 :=
begin
unfold encrypt,
finish,
end
-- Example: otp is inmune to letter frequency
section exampl
-- lets use mod 26 to encrypt and decrypt the word `happy`.
-- https://www.youtube.com/watch?v=2_w9l9visH8
def example_mod : ℕ := 26
def H : zmod example_mod := 8
def A : zmod example_mod := 1
def P : zmod example_mod := 16
def Y : zmod example_mod := 25
def K1 : zmod example_mod := 8
def K2 : zmod example_mod := 19
def K3 : zmod example_mod := 13
def K4 : zmod example_mod := 4
def K5 : zmod example_mod := 23
-- encrypt
#eval (H + K1)
#eval (A + K2)
-- as we use different keys, same letter gets encrypted different.
-- this makes otp inmune to letter frequency
#eval (P + K3)
#eval (P + K4)
#eval (Y + K5)
-- decrypt
#eval ((H + K1) - K1)
#eval ((A + K2) - K2)
#eval ((P + K3) - K3)
#eval ((P + K4) - K4)
#eval ((Y + K5) - K5)
end exampl
end modulo
namespace xor
-- size of the bitvectors
variable len : ℕ
-- plaintext and keys of the same size
variables p k : bitvec len
-- encrypt is the same as decrypt
def encrypt : bitvec len := p.xor k
def decrypt : bitvec len := p.xor k
-- TODO: xor properties that need to be proved
def xor_self_inverse (A : bitvec len) : Prop :=
A.xor A = bitvec.zero len
def xor_assoc (A : bitvec len) (B : bitvec len) (C : bitvec len) :
Prop := (A.xor B).xor C = (A.xor C).xor B
-- Appling xor to a bitvector A with B and then appling xor
-- to the result with B gives A.
lemma xor_twice (A: bitvec len) (B: bitvec len) :
(A.xor B).xor B = A :=
begin
sorry,
end
-- Example: A vector A xored twice with the same key K
-- returns the vector A.
section exampl
def A : bitvec 3 := 6
def K : bitvec 3 := 3
#eval ((A.xor K).xor K).to_nat
end exampl
lemma dec_undoes_enc : p = decrypt len (encrypt len p k) k :=
begin
unfold encrypt,
unfold decrypt,
rw xor_twice,
end
end xor
end otp