Commit | Line | Data |
---|---|---|
ee39a683 MW |
1 | #! /usr/local/bin/sage |
2 | ### -*- mode: python; coding: utf-8 -*- | |
3 | ||
4 | ###-------------------------------------------------------------------------- | |
fc2d44af MW |
5 | ### Some general utilities. |
6 | ||
7 | def ld(v): | |
8 | return 0 + sum(ord(v[i]) << 8*i for i in xrange(len(v))) | |
9 | ||
10 | def st(x, n): | |
11 | return ''.join(chr((x >> 8*i)&0xff) for i in xrange(n)) | |
12 | ||
25f67362 MW |
13 | def piece_widths_offsets(wd, n): |
14 | o = [ceil(wd*i/n) for i in xrange(n + 1)] | |
15 | w = [o[i + 1] - o[i] for i in xrange(n)] | |
16 | return w, o | |
17 | ||
18 | def pieces(x, wd, n, bias = 0): | |
19 | ||
20 | ## Figure out widths and offsets. | |
21 | w, o = piece_widths_offsets(wd, n) | |
22 | ||
23 | ## First, normalize |n| < bias/2. | |
24 | if bias and n >= bias/2: n -= bias | |
25 | ||
26 | ## First, collect the bits. | |
27 | nn = [] | |
28 | for i in xrange(n - 1): | |
29 | m = (1 << w[i]) - 1 | |
30 | nn.append(x&m) | |
31 | x >>= w[i] | |
32 | nn.append(x) | |
33 | ||
34 | ## Now normalize them to the appropriate interval. | |
35 | c = 0 | |
36 | for i in xrange(n - 1): | |
37 | b = 1 << (w[i] - 1) | |
38 | if nn[i] >= b: | |
39 | nn[i] -= 2*b | |
40 | nn[i + 1] += 1 | |
41 | ||
42 | ## And we're done. | |
43 | return nn | |
44 | ||
45 | def combine(v, wd, n): | |
46 | w, o = piece_widths_offsets(wd, n) | |
47 | return sum(v[i] << o[i] for i in xrange(n)) | |
48 | ||
fc2d44af MW |
49 | ###-------------------------------------------------------------------------- |
50 | ### Define the curve. | |
ee39a683 MW |
51 | |
52 | p = 2^255 - 19; k = GF(p) | |
fc2d44af MW |
53 | A = k(486662); A0 = (A - 2)/4 |
54 | E = EllipticCurve(k, [0, A, 0, 1, 0]); P = E.lift_x(9) | |
55 | l = 2^252 + 27742317777372353535851937790883648493 | |
56 | ||
57 | assert is_prime(l) | |
58 | assert (l*P).is_zero() | |
59 | assert (p + 1 - 8*l)^2 <= 4*p | |
60 | ||
61 | ###-------------------------------------------------------------------------- | |
62 | ### Example points from `Cryptography in NaCl'. | |
63 | ||
64 | x = ld(map(chr, [0x70,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d | |
65 | ,0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45 | |
66 | ,0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a | |
67 | ,0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x6a])) | |
68 | y = ld(map(chr, [0x58,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b | |
69 | ,0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6 | |
70 | ,0x6f,0x3b,0xb1,0x29,0x26,0x18,0xb6,0xfd | |
71 | ,0x1c,0x2f,0x8b,0x27,0xff,0x88,0xe0,0x6b])) | |
72 | X = x*P | |
73 | Y = y*P | |
74 | Z = x*Y | |
75 | assert Z == y*X | |
ee39a683 MW |
76 | |
77 | ###-------------------------------------------------------------------------- | |
78 | ### Arithmetic implementation. | |
79 | ||
80 | def sqrn(x, n): | |
81 | for i in xrange(n): x = x*x | |
82 | return x | |
83 | ||
25f67362 MW |
84 | sqrtm1 = sqrt(k(-1)) |
85 | ||
ee39a683 MW |
86 | def inv(x): |
87 | t2 = sqrn(x, 1) # 1 | 2 | |
88 | u = sqrn(t2, 2) # 3 | 8 | |
89 | t = u*x # 4 | 9 | |
90 | t11 = t*t2 # 5 | 11 | |
91 | u = sqrn(t11, 1) # 6 | 22 | |
92 | t = u*t # 7 | 2^5 - 1 = 31 | |
93 | u = sqrn(t, 5) # 12 | 2^10 - 2^5 | |
94 | t2p10m1 = u*t # 13 | 2^10 - 1 | |
95 | u = sqrn(t2p10m1, 10) # 23 | 2^20 - 2^10 | |
96 | t = u*t2p10m1 # 24 | 2^20 - 1 | |
97 | u = sqrn(t, 20) # 44 | 2^40 - 2^20 | |
98 | t = u*t # 45 | 2^40 - 1 | |
99 | u = sqrn(t, 10) # 55 | 2^50 - 2^10 | |
100 | t2p50m1 = u*t2p10m1 # 56 | 2^50 - 1 | |
101 | u = sqrn(t2p50m1, 50) # 106 | 2^100 - 2^50 | |
102 | t = u*t2p50m1 # 107 | 2^100 - 1 | |
103 | u = sqrn(t, 100) # 207 | 2^200 - 2^100 | |
104 | t = u*t # 208 | 2^200 - 1 | |
105 | u = sqrn(t, 50) # 258 | 2^250 - 2^50 | |
106 | t = u*t2p50m1 # 259 | 2^250 - 1 | |
107 | u = sqrn(t, 5) # 264 | 2^255 - 2^5 | |
108 | t = u*t11 # 265 | 2^255 - 21 | |
109 | return t | |
110 | ||
25f67362 MW |
111 | def quosqrt(x, y): |
112 | ||
113 | ## First, some preliminary values. | |
114 | y2 = sqrn(y, 1) # 1 | 0, 2 | |
115 | y3 = y2*y # 2 | 0, 3 | |
116 | xy3 = x*y3 # 3 | 1, 3 | |
117 | y4 = sqrn(y2, 1) # 4 | 0, 4 | |
118 | w = xy3*y4 # 5 | 1, 7 | |
119 | ||
120 | ## Now calculate w^(p - 5)/8. Notice that (p - 5)/8 = | |
121 | ## (2^255 - 24)/8 = 2^252 - 3. | |
122 | u = sqrn(w, 1) # 6 | 2 | |
123 | t = u*w # 7 | 3 | |
124 | u = sqrn(t, 1) # 8 | 6 | |
125 | t = u*w # 9 | 7 | |
126 | u = sqrn(t, 3) # 12 | 56 | |
127 | t = u*t # 13 | 63 = 2^6 - 1 | |
128 | u = sqrn(t, 6) # 19 | 2^12 - 2^6 | |
129 | t = u*t # 20 | 2^12 - 1 | |
130 | u = sqrn(t, 12) # 32 | 2^24 - 2^12 | |
131 | t = u*t # 33 | 2^24 - 1 | |
132 | u = sqrn(t, 1) # 34 | 2^25 - 2 | |
133 | t = u*w # 35 | 2^25 - 1 | |
134 | u = sqrn(t, 25) # 60 | 2^50 - 2^25 | |
135 | t2p50m1 = u*t # 61 | 2^50 - 1 | |
136 | u = sqrn(t2p50m1, 50) # 111 | 2^100 - 2^50 | |
137 | t = u*t2p50m1 # 112 | 2^100 - 1 | |
138 | u = sqrn(t, 100) # 212 | 2^200 - 2^100 | |
139 | t = u*t # 213 | 2^200 - 1 | |
140 | u = sqrn(t, 50) # 263 | 2^250 - 2^50 | |
141 | t = u*t2p50m1 # 264 | 2^250 - 1 | |
142 | u = sqrn(t, 2) # 266 | 2^252 - 4 | |
143 | t = u*w # 267 | 2^252 - 3 | |
144 | beta = t*xy3 # 268 | | |
145 | ||
146 | ## Now we have beta = (x y^3) (x y^7)^((p - 5)/8) = | |
147 | ## x^((p + 3)/8) y^((7 p - 11)/8) = (x/y)^((p + 3)/8). | |
148 | ## Suppose alpha^2 = x/y. Then beta^4 = (x/y)^((p + 3)/2) = | |
149 | ## alpha^(p + 3) = alpha^4 = (x/y)^2, so beta^2 = ±x/y. If | |
150 | ## y beta^2 = x then alpha = beta and we're done; if | |
151 | ## y beta^2 = -x, then alpha = beta sqrt(-1); otherwise x/y | |
152 | ## wasn't actually a square after all. | |
153 | t = y*beta^2 | |
154 | if t == x: return beta | |
155 | elif t == -x: return beta*sqrtm1 | |
156 | else: raise ValueError, 'not a square' | |
157 | ||
ee39a683 | 158 | assert inv(k(9))*9 == 1 |
25f67362 | 159 | assert 5*quosqrt(k(4), k(5))^2 == 4 |
ee39a683 | 160 | |
fc2d44af MW |
161 | ###-------------------------------------------------------------------------- |
162 | ### The Montgomery ladder. | |
163 | ||
164 | A0 = (A - 2)/4 | |
165 | ||
166 | def x25519(n, x1): | |
167 | ||
168 | ## Let Q = (x_1 : y_1 : 1) be an input point. We calculate | |
169 | ## n Q = (x_n : y_n : z_n), returning x_n/z_n (unless z_n = 0, | |
170 | ## in which case we return zero). | |
171 | ## | |
172 | ## We're given that n = 2^254 + n'_254, where 0 <= n'_254 < 2^254. | |
173 | bb = n.bits() | |
174 | x, z = 1, 0 | |
175 | u, w = x1, 1 | |
176 | ||
177 | ## Initially, let i = 255. | |
178 | for i in xrange(len(bb) - 1, -1, -1): | |
179 | ||
180 | ## Split n = n_i 2^i + n'_i, where 0 <= n'_i < 2^i, so n_0 = n. | |
181 | ## We have x, z = x_{n_{i+1}}, z_{n_{i+1}}, and | |
182 | ## u, w = x_{n_{i+1}+1}, z_{n_{i+1}+1}. | |
183 | ## Now either n_i = 2 n_{i+1} or n_i = 2 n_{i+1} + 1, depending | |
184 | ## on bit i of n. | |
185 | ||
186 | ## Swap (x : z) and (u : w) if bit i of n is set. | |
187 | if bb[i]: x, z, u, w = u, w, x, z | |
188 | ||
189 | ## Do the ladder step. | |
190 | xmz, xpz = x - z, x + z | |
191 | umw, upw = u - w, u + w | |
192 | xmz2, xpz2 = xmz^2, xpz^2 | |
193 | xpz2mxmz2 = xpz2 - xmz2 | |
194 | xmzupw, xpzumw = xmz*upw, xpz*umw | |
195 | x, z = xmz2*xpz2, xpz2mxmz2*(xpz2 + A0*xpz2mxmz2) | |
196 | u, w = (xmzupw + xpzumw)^2, x1*(xmzupw - xpzumw)^2 | |
197 | ||
198 | ## Finally, unswap. | |
199 | if bb[i]: x, z, u, w = u, w, x, z | |
200 | ||
201 | ## Almost done. | |
202 | return x*inv(z) | |
203 | ||
204 | assert x25519(y, k(9)) == Y[0] | |
205 | assert x25519(x, Y[0]) == x25519(y, X[0]) == Z[0] | |
206 | ||
ee39a683 | 207 | ###----- That's all, folks -------------------------------------------------- |