| 1 | #! /usr/local/bin/sage |
| 2 | ### -*- mode: python; coding: utf-8 -*- |
| 3 | |
| 4 | import hashlib as H |
| 5 | |
| 6 | ###-------------------------------------------------------------------------- |
| 7 | ### Some general utilities. |
| 8 | |
| 9 | def hash(*m): |
| 10 | h = H.sha512() |
| 11 | for i in m: h.update(i) |
| 12 | return h.digest() |
| 13 | |
| 14 | def ld(v): |
| 15 | return 0 + sum(ord(v[i]) << 8*i for i in xrange(len(v))) |
| 16 | |
| 17 | def st(x, n): |
| 18 | return ''.join(chr((x >> 8*i)&0xff) for i in xrange(n)) |
| 19 | |
| 20 | def piece_widths_offsets(wd, n): |
| 21 | o = [ceil(wd*i/n) for i in xrange(n + 1)] |
| 22 | w = [o[i + 1] - o[i] for i in xrange(n)] |
| 23 | return w, o |
| 24 | |
| 25 | def pieces(x, wd, n, bias = 0): |
| 26 | |
| 27 | ## Figure out widths and offsets. |
| 28 | w, o = piece_widths_offsets(wd, n) |
| 29 | |
| 30 | ## First, normalize |n| < bias/2. |
| 31 | if bias and n >= bias/2: n -= bias |
| 32 | |
| 33 | ## First, collect the bits. |
| 34 | nn = [] |
| 35 | for i in xrange(n - 1): |
| 36 | m = (1 << w[i]) - 1 |
| 37 | nn.append(x&m) |
| 38 | x >>= w[i] |
| 39 | nn.append(x) |
| 40 | |
| 41 | ## Now normalize them to the appropriate interval. |
| 42 | c = 0 |
| 43 | for i in xrange(n - 1): |
| 44 | b = 1 << (w[i] - 1) |
| 45 | if nn[i] >= b: |
| 46 | nn[i] -= 2*b |
| 47 | nn[i + 1] += 1 |
| 48 | |
| 49 | ## And we're done. |
| 50 | return nn |
| 51 | |
| 52 | def combine(v, wd, n): |
| 53 | w, o = piece_widths_offsets(wd, n) |
| 54 | return sum(v[i] << o[i] for i in xrange(n)) |
| 55 | |
| 56 | ###-------------------------------------------------------------------------- |
| 57 | ### Define the curve. |
| 58 | |
| 59 | p = 2^255 - 19; k = GF(p) |
| 60 | A = k(486662); A0 = (A - 2)/4 |
| 61 | E = EllipticCurve(k, [0, A, 0, 1, 0]); P = E.lift_x(9) |
| 62 | l = 2^252 + 27742317777372353535851937790883648493 |
| 63 | |
| 64 | assert is_prime(l) |
| 65 | assert (l*P).is_zero() |
| 66 | assert (p + 1 - 8*l)^2 <= 4*p |
| 67 | |
| 68 | ###-------------------------------------------------------------------------- |
| 69 | ### Example points from `Cryptography in NaCl'. |
| 70 | |
| 71 | x = ld(map(chr, [0x70,0x07,0x6d,0x0a,0x73,0x18,0xa5,0x7d |
| 72 | ,0x3c,0x16,0xc1,0x72,0x51,0xb2,0x66,0x45 |
| 73 | ,0xdf,0x4c,0x2f,0x87,0xeb,0xc0,0x99,0x2a |
| 74 | ,0xb1,0x77,0xfb,0xa5,0x1d,0xb9,0x2c,0x6a])) |
| 75 | y = ld(map(chr, [0x58,0xab,0x08,0x7e,0x62,0x4a,0x8a,0x4b |
| 76 | ,0x79,0xe1,0x7f,0x8b,0x83,0x80,0x0e,0xe6 |
| 77 | ,0x6f,0x3b,0xb1,0x29,0x26,0x18,0xb6,0xfd |
| 78 | ,0x1c,0x2f,0x8b,0x27,0xff,0x88,0xe0,0x6b])) |
| 79 | X = x*P |
| 80 | Y = y*P |
| 81 | Z = x*Y |
| 82 | assert Z == y*X |
| 83 | |
| 84 | ###-------------------------------------------------------------------------- |
| 85 | ### Arithmetic implementation. |
| 86 | |
| 87 | def sqrn(x, n): |
| 88 | for i in xrange(n): x = x*x |
| 89 | return x |
| 90 | |
| 91 | sqrtm1 = sqrt(k(-1)) |
| 92 | |
| 93 | def inv(x): |
| 94 | t2 = sqrn(x, 1) # 1 | 2 |
| 95 | u = sqrn(t2, 2) # 3 | 8 |
| 96 | t = u*x # 4 | 9 |
| 97 | t11 = t*t2 # 5 | 11 |
| 98 | u = sqrn(t11, 1) # 6 | 22 |
| 99 | t = u*t # 7 | 2^5 - 1 = 31 |
| 100 | u = sqrn(t, 5) # 12 | 2^10 - 2^5 |
| 101 | t2p10m1 = u*t # 13 | 2^10 - 1 |
| 102 | u = sqrn(t2p10m1, 10) # 23 | 2^20 - 2^10 |
| 103 | t = u*t2p10m1 # 24 | 2^20 - 1 |
| 104 | u = sqrn(t, 20) # 44 | 2^40 - 2^20 |
| 105 | t = u*t # 45 | 2^40 - 1 |
| 106 | u = sqrn(t, 10) # 55 | 2^50 - 2^10 |
| 107 | t2p50m1 = u*t2p10m1 # 56 | 2^50 - 1 |
| 108 | u = sqrn(t2p50m1, 50) # 106 | 2^100 - 2^50 |
| 109 | t = u*t2p50m1 # 107 | 2^100 - 1 |
| 110 | u = sqrn(t, 100) # 207 | 2^200 - 2^100 |
| 111 | t = u*t # 208 | 2^200 - 1 |
| 112 | u = sqrn(t, 50) # 258 | 2^250 - 2^50 |
| 113 | t = u*t2p50m1 # 259 | 2^250 - 1 |
| 114 | u = sqrn(t, 5) # 264 | 2^255 - 2^5 |
| 115 | t = u*t11 # 265 | 2^255 - 21 |
| 116 | return t |
| 117 | |
| 118 | def quosqrt_djb(x, y): |
| 119 | |
| 120 | ## First, some preliminary values. |
| 121 | y2 = sqrn(y, 1) # 1 | 0, 2 |
| 122 | y3 = y2*y # 2 | 0, 3 |
| 123 | xy3 = x*y3 # 3 | 1, 3 |
| 124 | y4 = sqrn(y2, 1) # 4 | 0, 4 |
| 125 | w = xy3*y4 # 5 | 1, 7 |
| 126 | |
| 127 | ## Now calculate w^(p - 5)/8. Notice that (p - 5)/8 = |
| 128 | ## (2^255 - 24)/8 = 2^252 - 3. |
| 129 | u = sqrn(w, 1) # 6 | 2 |
| 130 | t = u*w # 7 | 3 |
| 131 | u = sqrn(t, 1) # 8 | 6 |
| 132 | t = u*w # 9 | 7 |
| 133 | u = sqrn(t, 3) # 12 | 56 |
| 134 | t = u*t # 13 | 63 = 2^6 - 1 |
| 135 | u = sqrn(t, 6) # 19 | 2^12 - 2^6 |
| 136 | t = u*t # 20 | 2^12 - 1 |
| 137 | u = sqrn(t, 12) # 32 | 2^24 - 2^12 |
| 138 | t = u*t # 33 | 2^24 - 1 |
| 139 | u = sqrn(t, 1) # 34 | 2^25 - 2 |
| 140 | t = u*w # 35 | 2^25 - 1 |
| 141 | u = sqrn(t, 25) # 60 | 2^50 - 2^25 |
| 142 | t2p50m1 = u*t # 61 | 2^50 - 1 |
| 143 | u = sqrn(t2p50m1, 50) # 111 | 2^100 - 2^50 |
| 144 | t = u*t2p50m1 # 112 | 2^100 - 1 |
| 145 | u = sqrn(t, 100) # 212 | 2^200 - 2^100 |
| 146 | t = u*t # 213 | 2^200 - 1 |
| 147 | u = sqrn(t, 50) # 263 | 2^250 - 2^50 |
| 148 | t = u*t2p50m1 # 264 | 2^250 - 1 |
| 149 | u = sqrn(t, 2) # 266 | 2^252 - 4 |
| 150 | t = u*w # 267 | 2^252 - 3 |
| 151 | beta = t*xy3 # 268 | |
| 152 | |
| 153 | ## Now we have beta = (x y^3) (x y^7)^((p - 5)/8) = |
| 154 | ## x^((p + 3)/8) y^((7 p - 11)/8) = (x/y)^((p + 3)/8). |
| 155 | ## Suppose alpha^2 = x/y. Then beta^4 = (x/y)^((p + 3)/2) = |
| 156 | ## alpha^(p + 3) = alpha^4 = (x/y)^2, so beta^2 = ±x/y. If |
| 157 | ## y beta^2 = x then alpha = beta and we're done; if |
| 158 | ## y beta^2 = -x, then alpha = beta sqrt(-1); otherwise x/y |
| 159 | ## wasn't actually a square after all. |
| 160 | t = y*beta^2 |
| 161 | if t == x: return beta |
| 162 | elif t == -x: return beta*sqrtm1 |
| 163 | else: raise ValueError, 'not a square' |
| 164 | |
| 165 | def quosqrt_mdw(x, y): |
| 166 | v = x*y |
| 167 | |
| 168 | ## Now we calculate w = v^{3*2^252 - 8}. This will be explained later. |
| 169 | u = sqrn(v, 1) # 1 | 2 |
| 170 | t = u*v # 2 | 3 |
| 171 | u = sqrn(t, 2) # 4 | 12 |
| 172 | t15 = u*t # 5 | 15 |
| 173 | u = sqrn(t15, 1) # 6 | 30 |
| 174 | t = u*v # 7 | 31 = 2^5 - 1 |
| 175 | u = sqrn(t, 5) # 12 | 2^10 - 2^5 |
| 176 | t = u*t # 13 | 2^10 - 1 |
| 177 | u = sqrn(t, 10) # 23 | 2^20 - 2^10 |
| 178 | u = u*t # 24 | 2^20 - 1 |
| 179 | u = sqrn(u, 10) # 34 | 2^30 - 2^10 |
| 180 | t = u*t # 35 | 2^30 - 1 |
| 181 | u = sqrn(t, 1) # 36 | 2^31 - 2 |
| 182 | t = u*v # 37 | 2^31 - 1 |
| 183 | u = sqrn(t, 31) # 68 | 2^62 - 2^31 |
| 184 | t = u*t # 69 | 2^62 - 1 |
| 185 | u = sqrn(t, 62) # 131 | 2^124 - 2^62 |
| 186 | t = u*t # 132 | 2^124 - 1 |
| 187 | u = sqrn(t, 124) # 256 | 2^248 - 2^124 |
| 188 | t = u*t # 257 | 2^248 - 1 |
| 189 | u = sqrn(t, 1) # 258 | 2^249 - 2 |
| 190 | t = u*v # 259 | 2^249 - 1 |
| 191 | t = sqrn(t, 3) # 262 | 2^252 - 8 |
| 192 | u = sqrn(t, 1) # 263 | 2^253 - 16 |
| 193 | t = u*t # 264 | 3*2^252 - 24 |
| 194 | t = t*t15 # 265 | 3*2^252 - 9 |
| 195 | w = t*v # 266 | 3*2^252 - 8 |
| 196 | |
| 197 | ## Awesome. Now let me explain. Let v be a square in GF(p), and let w = |
| 198 | ## v^(3*2^252 - 8). In particular, let's consider |
| 199 | ## |
| 200 | ## v^2 w^4 = v^2 v^{3*2^254 - 32} = (v^{2^254 - 10})^3 |
| 201 | ## |
| 202 | ## But 2^254 - 10 = ((2^255 - 19) - 1)/2 = (p - 1)/2. Since v is a square, |
| 203 | ## it has order dividing (p - 1)/2, and therefore v^2 w^4 = 1 and |
| 204 | ## |
| 205 | ## w^4 = 1/v^2 |
| 206 | ## |
| 207 | ## That in turn implies that w^2 = ±1/v. Now, recall that v = x y, and let |
| 208 | ## w' = w x. Then w'^2 = ±x^2/v = ±x/y. If y w'^2 = x then we set |
| 209 | ## z = w', since we have z^2 = x/y; otherwise let z = i w', where i^2 = -1, |
| 210 | ## so z^2 = -w^2 = x/y, and we're done. |
| 211 | t = w*x |
| 212 | u = y*t^2 |
| 213 | if u == x: return t |
| 214 | elif u == -x: return t*sqrtm1 |
| 215 | else: raise ValueError, 'not a square' |
| 216 | |
| 217 | quosqrt = quosqrt_mdw |
| 218 | |
| 219 | assert inv(k(9))*9 == 1 |
| 220 | assert 5*quosqrt(k(4), k(5))^2 == 4 |
| 221 | |
| 222 | ###-------------------------------------------------------------------------- |
| 223 | ### The Montgomery ladder. |
| 224 | |
| 225 | def x25519(n, x1): |
| 226 | |
| 227 | ## Let Q = (x_1 : y_1 : 1) be an input point. We calculate |
| 228 | ## n Q = (x_n : y_n : z_n), returning x_n/z_n (unless z_n = 0, |
| 229 | ## in which case we return zero). |
| 230 | ## |
| 231 | ## We're given that n = 2^254 + n'_254, where 0 <= n'_254 < 2^254. |
| 232 | bb = n.bits() |
| 233 | x, z = 1, 0 |
| 234 | u, w = x1, 1 |
| 235 | |
| 236 | ## Initially, let i = 255. |
| 237 | for i in xrange(len(bb) - 1, -1, -1): |
| 238 | |
| 239 | ## Split n = n_i 2^i + n'_i, where 0 <= n'_i < 2^i, so n_0 = n. |
| 240 | ## We have x, z = x_{n_{i+1}}, z_{n_{i+1}}, and |
| 241 | ## u, w = x_{n_{i+1}+1}, z_{n_{i+1}+1}. |
| 242 | ## Now either n_i = 2 n_{i+1} or n_i = 2 n_{i+1} + 1, depending |
| 243 | ## on bit i of n. |
| 244 | |
| 245 | ## Swap (x : z) and (u : w) if bit i of n is set. |
| 246 | if bb[i]: x, z, u, w = u, w, x, z |
| 247 | |
| 248 | ## Do the ladder step. |
| 249 | xmz, xpz = x - z, x + z |
| 250 | umw, upw = u - w, u + w |
| 251 | xmz2, xpz2 = xmz^2, xpz^2 |
| 252 | xpz2mxmz2 = xpz2 - xmz2 |
| 253 | xmzupw, xpzumw = xmz*upw, xpz*umw |
| 254 | x, z = xmz2*xpz2, xpz2mxmz2*(xpz2 + A0*xpz2mxmz2) |
| 255 | u, w = (xmzupw + xpzumw)^2, x1*(xmzupw - xpzumw)^2 |
| 256 | |
| 257 | ## Finally, unswap. |
| 258 | if bb[i]: x, z, u, w = u, w, x, z |
| 259 | |
| 260 | ## Almost done. |
| 261 | return x*inv(z) |
| 262 | |
| 263 | assert x25519(y, k(9)) == Y[0] |
| 264 | assert x25519(x, Y[0]) == x25519(y, X[0]) == Z[0] |
| 265 | |
| 266 | ###-------------------------------------------------------------------------- |
| 267 | ### Edwards curve parameters and conversion. |
| 268 | |
| 269 | a = k(-1) |
| 270 | d = -A0/(A0 + 1) |
| 271 | |
| 272 | def mont_to_ed(u, v): |
| 273 | return sqrt(-A - 2)*u/v, (u - 1)/(u + 1) |
| 274 | |
| 275 | def ed_to_mont(x, y): |
| 276 | u = (1 + y)/(1 - y) |
| 277 | v = sqrt(-A - 2)*u/x |
| 278 | return u, v |
| 279 | |
| 280 | Bx, By = mont_to_ed(P[0], P[1]) |
| 281 | if Bx.lift()%2: Bx = -Bx |
| 282 | B = (Bx, By, 1) |
| 283 | u, v = ed_to_mont(Bx, By) |
| 284 | |
| 285 | assert By == k(4/5) |
| 286 | assert -Bx^2 + By^2 == 1 + d*Bx^2*By^2 |
| 287 | assert u == k(9) |
| 288 | assert v == P[1] or v == -P[1] |
| 289 | |
| 290 | ###-------------------------------------------------------------------------- |
| 291 | ### Edwards point addition and doubling. |
| 292 | |
| 293 | def ed_add((X1, Y1, Z1), (X2, Y2, Z2)): |
| 294 | A = Z1*Z2 |
| 295 | B = A^2 |
| 296 | C = X1*X2 |
| 297 | D = Y1*Y2 |
| 298 | E = d*C*D |
| 299 | F = B - E |
| 300 | G = B + E |
| 301 | X3 = A*F*((X1 + Y1)*(X2 + Y2) - C - D) |
| 302 | Y3 = A*G*(D - a*C) |
| 303 | Z3 = F*G |
| 304 | return X3, Y3, Z3 |
| 305 | |
| 306 | def ed_dbl((X1, Y1, Z1)): |
| 307 | B = (X1 + Y1)^2 |
| 308 | C = X1^2 |
| 309 | D = Y1^2 |
| 310 | E = a*C |
| 311 | F = E + D |
| 312 | H = Z1^2 |
| 313 | J = F - 2*H |
| 314 | X3 = (B - C - D)*J |
| 315 | Y3 = F*(E - D) |
| 316 | Z3 = F*J |
| 317 | return X3, Y3, Z3 |
| 318 | |
| 319 | Q = E.random_point() |
| 320 | R = E.random_point() |
| 321 | n = ZZ(randint(0, 2^255 - 1)) |
| 322 | m = ZZ(randint(0, 2^255 - 1)) |
| 323 | Qx, Qy = mont_to_ed(Q[0], Q[1]) |
| 324 | Rx, Ry = mont_to_ed(R[0], R[1]) |
| 325 | |
| 326 | S = Q + R; T = 2*Q |
| 327 | Sx, Sy, Sz = ed_add((Qx, Qy, 1), (Rx, Ry, 1)) |
| 328 | Tx, Ty, Tz = ed_dbl((Qx, Qy, 1)) |
| 329 | assert (Sx/Sz, Sy/Sz) == mont_to_ed(S[0], S[1]) |
| 330 | assert (Tx/Tz, Ty/Tz) == mont_to_ed(T[0], T[1]) |
| 331 | |
| 332 | ###-------------------------------------------------------------------------- |
| 333 | ### Scalar multiplication. |
| 334 | |
| 335 | def ed_mul(n, Q): |
| 336 | winwd = 4 |
| 337 | winlim = 1 << winwd |
| 338 | winmask = winlim - 1 |
| 339 | tabsz = winlim/2 + 1 |
| 340 | |
| 341 | ## Recode the scalar to roughly-balanced form. |
| 342 | nn = [(n >> i)&winmask for i in xrange(0, n.nbits() + winwd, winwd)] |
| 343 | for i in xrange(len(nn) - 2, -1, -1): |
| 344 | if nn[i] >= winlim/2: |
| 345 | nn[i] -= winlim |
| 346 | nn[i + 1] += 1 |
| 347 | |
| 348 | ## Build the table of small multiples. |
| 349 | V = tabsz*[None] |
| 350 | V[0] = (0, 1, 1) |
| 351 | V[1] = Q |
| 352 | V[2] = ed_dbl(V[1]) |
| 353 | for i in xrange(3, tabsz, 2): |
| 354 | V[i] = ed_add(V[i - 1], Q) |
| 355 | V[i + 1] = ed_dbl(V[(i + 1)/2]) |
| 356 | |
| 357 | ## Do the multiplication. |
| 358 | T = V[0] |
| 359 | for i in xrange(len(nn) - 1, -1, -1): |
| 360 | w = nn[i] |
| 361 | for j in xrange(winwd): T = ed_dbl(T) |
| 362 | if w >= 0: T = ed_add(T, V[w]) |
| 363 | else: x, y, z = V[-w]; T = ed_add(T, (-x, y, z)) |
| 364 | |
| 365 | ## Done. |
| 366 | return T |
| 367 | |
| 368 | def ed_simmul(n0, Q0, n1, Q1): |
| 369 | winwd = 2 |
| 370 | winlim = 1 << winwd |
| 371 | winmask = winlim - 1 |
| 372 | tabsz = 1 << 2*winwd |
| 373 | |
| 374 | ## Extract the scalar pieces. |
| 375 | nn = [(n0 >> i)&winmask | (((n1 >> i)&winmask) << winwd) |
| 376 | for i in xrange(0, max(n0.nbits(), n1.nbits()), winwd)] |
| 377 | |
| 378 | ## Build the table of small linear combinations. |
| 379 | V = tabsz*[None] |
| 380 | V[0] = (0, 1, 1) |
| 381 | V[1] = Q0; V[winlim] = Q1 |
| 382 | i = 2 |
| 383 | while i < winlim: |
| 384 | V[i] = ed_dbl(V[i/2]) |
| 385 | V[i*winlim] = ed_dbl(V[i*winlim/2]) |
| 386 | i <<= 1 |
| 387 | i = 2 |
| 388 | while i < tabsz: |
| 389 | for j in xrange(1, i): |
| 390 | V[i + j] = ed_add(V[i], V[j]) |
| 391 | i <<= 1 |
| 392 | |
| 393 | ## Do the multiplication. |
| 394 | T = V[0] |
| 395 | for i in xrange(len(nn) - 1, -1, -1): |
| 396 | w = nn[i] |
| 397 | for j in xrange(winwd): T = ed_dbl(T) |
| 398 | T = ed_add(T, V[w]) |
| 399 | |
| 400 | ## Done. |
| 401 | return T |
| 402 | |
| 403 | U = n*Q; V = n*Q + m*R |
| 404 | Ux, Uy, Uz = ed_mul(n, (Qx, Qy, 1)) |
| 405 | Vx, Vy, Vz = ed_simmul(n, (Qx, Qy, 1), m, (Rx, Ry, 1)) |
| 406 | assert (Ux/Uz, Uy/Uz) == mont_to_ed(U[0], U[1]) |
| 407 | assert (Vx/Vz, Vy/Vz) == mont_to_ed(V[0], V[1]) |
| 408 | |
| 409 | ###-------------------------------------------------------------------------- |
| 410 | ### Point encoding. |
| 411 | |
| 412 | def ed_encode((X, Y, Z)): |
| 413 | x, y = X/Z, Y/Z |
| 414 | xx, yy = x.lift(), y.lift() |
| 415 | if xx%2: yy += 1 << 255 |
| 416 | return st(yy, 32) |
| 417 | |
| 418 | def ed_decode(s): |
| 419 | n = ld(s) |
| 420 | bit = (n >> 255)&1 |
| 421 | y = n&((1 << 255) - 1) |
| 422 | y2 = y^2 |
| 423 | x = quosqrt(y2 - 1, d*y2 + 1) |
| 424 | if x.lift()%2 != bit: x = -x |
| 425 | return (x, y, 1) |
| 426 | |
| 427 | ###-------------------------------------------------------------------------- |
| 428 | ### EdDSA implementation. |
| 429 | |
| 430 | def eddsa_splitkey(k): |
| 431 | h = hash(k) |
| 432 | a = 2^254 + (ld(h[0:32])&((1 << 254) - 8)) |
| 433 | h1 = h[32:64] |
| 434 | return a, h1 |
| 435 | |
| 436 | def eddsa_pubkey(k): |
| 437 | a, h1 = eddsa_splitkey(k) |
| 438 | A = ed_mul(a, B) |
| 439 | return ed_encode(A) |
| 440 | |
| 441 | def eddsa_sign(k, m): |
| 442 | K = eddsa_pubkey(k) |
| 443 | a, h1 = eddsa_splitkey(k) |
| 444 | r = ld(hash(h1, m))%l |
| 445 | A = ed_decode(K) |
| 446 | R = ed_mul(r, B) |
| 447 | RR = ed_encode(R) |
| 448 | S = (r + a*ld(hash(RR, K, m)))%l |
| 449 | return RR + st(S, 32) |
| 450 | |
| 451 | def eddsa_verify(K, m, sig): |
| 452 | A = ed_decode(K) |
| 453 | R, S = sig[0:32], ld(sig[32:64]) |
| 454 | h = ld(hash(R, K, m))%l |
| 455 | V = ed_simmul(S, B, h, (-A[0], A[1], A[2])) |
| 456 | return ed_encode(V) == R |
| 457 | |
| 458 | priv = '1acdbb793b0384934627470d795c3d1dd4d79cea59ef983f295b9b59179cbb28'.decode('hex') |
| 459 | msg = '7cf34f75c3dac9a804d0fcd09eba9b29c9484e8a018fa9e073042df88e3c56'.decode('hex') |
| 460 | pub = '3f60c7541afa76c019cf5aa82dcdb088ed9e4ed9780514aefb379dabc844f31a'.decode('hex') |
| 461 | sig = 'be71ef4806cb041d885effd9e6b0fbb73d65d7cdec47a89c8a994892f4e55a568c4cc78d61f901e80dbb628b86a23ccd594e712b57fa94c2d67ec26634878507'.decode('hex') |
| 462 | assert pub == eddsa_pubkey(priv) |
| 463 | assert sig == eddsa_sign(priv, msg) |
| 464 | assert eddsa_verify(pub, msg, sig) |
| 465 | |
| 466 | ###----- That's all, folks -------------------------------------------------- |