src/crypto/secp256k1/ecdsa.bend
bend-collections@1.0.0.0 · 371 lines · 65 definitions
1import Base
2import bend-collections@1.0.0.0/src/crypto/mac.bend as MAC
3import bend-collections@1.0.0.0/src/crypto/keccak/keccak.bend as K
4import bend-collections@1.0.0.0/src/crypto/secp256k1/limbs.bend as L
5import bend-collections@1.0.0.0/src/crypto/secp256k1/field.bend as F
6import bend-collections@1.0.0.0/src/crypto/secp256k1/scalar.bend as S
7import bend-collections@1.0.0.0/src/crypto/secp256k1/point.bend as P
8import bend-collections@1.0.0.0/src/crypto/secp256k1/bytes.bend as B
9
10
11
12
13
14
15
16
17
18
19type Signature is Data:
20 Signature{r: List<&2, U32>, s: List<&2, U32>, v: U32}
21
22
23
24
25def scalar_ok(+x: List<&2, Nat>) -> Bool:
26 Bool.and(Bool.not(S.is_zero(x)), S.lt_n(x))
27
28
29
30def hash_scalar(+h: List<&2, U32>) -> List<&2, Nat>:
31 S.reduce(B.of_be(h))
32
33
34
35type Drbg is Data:
36 Drbg{k: List<&2, U32>, v: List<&2, U32>}
37
38
39
40def hmac(key: List<&2, U32>, msg: List<&2, U32>) -> List<&2, U32>:
41 match key:
42 case Nil{}: MAC.sign(Nil{}, msg)
43 case k <> t: MAC.sign(k <> t, msg)
44
45def cat(xs: List<&2, U32>, ys: List<&2, U32>) -> List<&2, U32>:
46 List.append(&2, U32, xs, ys)
47
48def fill(n: Nat, +b: U32) -> List<&2, U32>:
49 List.replicate(U32, n, b)
50
51def drbg_v(+k: List<&2, U32>, +v: List<&2, U32>) -> Drbg:
52 Drbg{k, hmac(k, v)}
53
54
55
56def drbg_step(+k: List<&2, U32>, +v: List<&2, U32>, +tag: U32, +seed: List<&2, U32>) -> Drbg:
57 drbg_v(hmac(k, cat(v, tag <> seed)), v)
58
59def drbg_second(d: Drbg, +seed: List<&2, U32>) -> Drbg:
60 match d:
61 case Drbg{k, v}: drbg_step(k, v, 1, seed)
62
63def drbg_init(+seed: List<&2, U32>) -> Drbg:
64 drbg_second(drbg_step(fill(32n, 0), fill(32n, 1), 0, seed), seed)
65
66
67def drbg_next(+k: List<&2, U32>, +v: List<&2, U32>) -> Drbg:
68 drbg_v(hmac(k, cat(v, [0])), v)
69
70
71
72def b2n(b: Bool) -> Nat:
73 L.b2n(b)
74
75type Attempt is Data:
76 ARetry{}
77 ADone{sig: Signature}
78
79def finish_if(bad: Bool, +sig: Signature) -> Attempt:
80 match bad:
81 case True{}: ARetry{}
82 case False{}: ADone{sig}
83
84
85
86
87def finish(+z: List<&2, Nat>, +d: List<&2, Nat>, +k: List<&2, Nat>, +x: List<&2, Nat>, +y: List<&2, Nat>) -> Attempt:
88 +r = S.reduce(x)
89 +s0 = S.mul(S.inv(k), S.add(z, S.mul(r, d)))
90 +high = b2n(S.is_high(s0))
91 +par = F.parity(y)
92 +id = Nat.add(Nat.mul(b2n(Bool.not(S.lt_n(x))), 2n), Nat.sub(Nat.add(par, high), Nat.mul(Nat.mul(par, high), 2n)))
93 finish_if(Bool.or(S.is_zero(r), S.is_zero(s0)), Signature{B.to_be(r), B.to_be(S.select(high, S.neg(s0), s0)), U32.from_nat(id)})
94
95def attempt_aff(+z: List<&2, Nat>, +d: List<&2, Nat>, +k: List<&2, Nat>, a: P.Affine) -> Attempt:
96 match a:
97 case P.Affine{x, y}: finish(z, d, k, x, y)
98
99def attempt_ok(+z: List<&2, Nat>, +d: List<&2, Nat>, +k: List<&2, Nat>, ok: Bool) -> Attempt:
100 match ok:
101 case True{}: attempt_aff(z, d, k, P.to_affine(P.mul(k, P.g())))
102 case False{}: ARetry{}
103
104
105def attempt(+z: List<&2, Nat>, +d: List<&2, Nat>, +v: List<&2, U32>) -> Attempt:
106 +k = B.of_be(v)
107 attempt_ok(z, d, k, scalar_ok(k))
108
109
110
111
112
113
114def sign_loop(fuel: Nat, +z: List<&2, Nat>, +d: List<&2, Nat>, +k: List<&2, U32>, +v: List<&2, U32>, att: Attempt) -> Maybe<&2, Signature>:
115 match fuel att:
116 case 0n ARetry{}: None{}
117 case 0n ADone{sig}: Some{sig}
118 case 1n+f ARetry{}:
119 +k2 = hmac(k, cat(v, [0]))
120 +v3 = hmac(k2, hmac(k2, v))
121 sign_loop(f, z, d, k2, v3, attempt(z, d, v3))
122 case 1n+f ADone{sig}: Some{sig}
123
124def sign_drbg(+z: List<&2, Nat>, +d: List<&2, Nat>, g: Drbg) -> Maybe<&2, Signature>:
125 match g:
126 case Drbg{+k, +v}:
127 +v1 = hmac(k, v)
128 sign_loop(16n, z, d, k, v1, attempt(z, d, v1))
129
130def secret_if(+d: List<&2, Nat>, ok: Bool) -> Maybe<&2, List<&2, Nat>>:
131 match ok:
132 case True{}: Some{d}
133 case False{}: None{}
134
135
136def secret(+sk: List<&2, U32>) -> Maybe<&2, List<&2, Nat>>:
137 +d = B.of_be(sk)
138 secret_if(d, Bool.and(B.has_len(32n, sk), scalar_ok(d)))
139
140
141
142def sign_d(+h: List<&2, U32>, m: Maybe<&2, List<&2, Nat>>) -> Maybe<&2, Signature>:
143 match m:
144 case None{}: None{}
145 case Some{+d}:
146 +z = hash_scalar(h)
147 sign_drbg(z, d, drbg_init(cat(B.to_be(d), B.to_be(z))))
148
149def sign_h(+sk: List<&2, U32>, +h: List<&2, U32>, ok: Bool) -> Maybe<&2, Signature>:
150 match ok:
151 case True{}: sign_d(h, secret(sk))
152 case False{}: None{}
153
154
155def sign(+sk: List<&2, U32>, +h: List<&2, U32>) -> Maybe<&2, Signature>:
156 sign_h(sk, h, B.has_len(32n, h))
157
158
159
160def public_point(+d: List<&2, Nat>) -> P.Point:
161 P.mul(d, P.g())
162
163def pk_of(compressed: Bool, +q: P.Point) -> List<&2, U32>:
164 match compressed:
165 case True{}: P.encode_compressed(q)
166 case False{}: P.encode_uncompressed(q)
167
168def public_key_d(compressed: Bool, m: Maybe<&2, List<&2, Nat>>) -> Maybe<&2, List<&2, U32>>:
169 match m:
170 case None{}: None{}
171 case Some{d}: Some{pk_of(compressed, public_point(d))}
172
173
174def public_key(+sk: List<&2, U32>, compressed: Bool) -> Maybe<&2, List<&2, U32>>:
175 public_key_d(compressed, secret(sk))
176
177
178
179
180
181def verify_rs(+q: P.Point, +z: List<&2, Nat>, +r: List<&2, Nat>, +s: List<&2, Nat>) -> Bool:
182 +w = S.inv(s)
183 +rr = P.add(P.mul(S.mul(z, w), P.g()), P.mul(S.mul(r, w), q))
184 Bool.and(Bool.not(P.is_inf(rr)), S.eq(S.reduce(P.aff_x(P.to_affine(rr))), r))
185
186def verify_ok(+q: P.Point, +z: List<&2, Nat>, +r: List<&2, Nat>, +s: List<&2, Nat>, ok: Bool) -> Bool:
187 match ok:
188 case True{}: verify_rs(q, z, r, s)
189 case False{}: False{}
190
191def verify_q(+h: List<&2, U32>, +sig: List<&2, U32>, strict: Bool, m: Maybe<&2, P.Point>) -> Bool:
192 match m:
193 case None{}: False{}
194 case Some{+q}:
195 +r = B.of_be(B.prefix(32n, sig))
196 +s = B.of_be(B.suffix(32n, sig))
197 verify_ok(q, hash_scalar(h), r, s, Bool.and(Bool.and(scalar_ok(r), scalar_ok(s)), Bool.or(Bool.not(strict), Bool.not(S.is_high(s)))))
198
199def verify_len(+pk: List<&2, U32>, +h: List<&2, U32>, +sig: List<&2, U32>, strict: Bool, ok: Bool) -> Bool:
200 match ok:
201 case True{}: verify_q(h, sig, strict, P.decode(pk))
202 case False{}: False{}
203
204def verify_with(+pk: List<&2, U32>, +h: List<&2, U32>, +sig: List<&2, U32>, strict: Bool) -> Bool:
205 verify_len(pk, h, sig, strict, Bool.and(B.has_len(32n, h), B.has_len(64n, sig)))
206
207
208
209def verify(+pk: List<&2, U32>, +h: List<&2, U32>, +sig: List<&2, U32>) -> Bool:
210 verify_with(pk, h, sig, False{})
211
212
213
214def verify_strict(+pk: List<&2, U32>, +h: List<&2, U32>, +sig: List<&2, U32>) -> Bool:
215 verify_with(pk, h, sig, True{})
216
217
218
219def recover_q(+q: P.Point, inf: Bool) -> Maybe<&2, List<&2, U32>>:
220 match inf:
221 case True{}: None{}
222 case False{}: Some{P.encode_uncompressed(q)}
223
224
225
226def recover_r(+z: List<&2, Nat>, +r: List<&2, Nat>, +s: List<&2, Nat>, m: Maybe<&2, P.Point>) -> Maybe<&2, List<&2, U32>>:
227 match m:
228 case None{}: None{}
229 case Some{+pr}:
230 +ri = S.inv(r)
231 +q = P.add(P.mul(S.mul(S.neg(z), ri), P.g()), P.mul(S.mul(s, ri), pr))
232 recover_q(q, P.is_inf(q))
233
234def recover_x(+z: List<&2, Nat>, +r: List<&2, Nat>, +s: List<&2, Nat>, +id: Nat, +x: List<&2, Nat>, ok: Bool) -> Maybe<&2, List<&2, U32>>:
235 match ok:
236 case True{}: recover_r(z, r, s, P.decompress(x, Nat.mod(id, 2n)))
237 case False{}: None{}
238
239
240def cn_fe() -> List<&2, Nat>:
241 [48831n, 12233n, 41331n, 16429n, 24516n, 20663n, 8985n, 17745n, 1n, 0n, 0n, 0n, 0n, 0n, 0n, 0n]
242
243def cp_fe() -> List<&2, Nat>:
244 [977n, 0n, 1n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n, 0n]
245
246
247def rn_fe(+r: List<&2, Nat>) -> List<&2, Nat>:
248 F.add(F.sub(r, cn_fe()), cp_fe())
249
250
251
252def recover_ok(+z: List<&2, Nat>, +r: List<&2, Nat>, +s: List<&2, Nat>, +id: Nat, ok: Bool) -> Maybe<&2, List<&2, U32>>:
253 match ok:
254 case True{}:
255 +j = Nat.div(id, 2n)
256 +x1 = rn_fe(r)
257 recover_x(z, r, s, id, F.select(j, x1, r), Bool.or(Nat.is_eq(j, 0n), Bool.not(S.lt_n(x1))))
258 case False{}: None{}
259
260def recover_sig(+h: List<&2, U32>, +sig: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
261 +r = B.of_be(B.prefix(32n, sig))
262 +s = B.of_be(B.prefix(32n, B.suffix(32n, sig)))
263 +id = U32.to_nat(B.head(B.suffix(64n, sig)))
264 recover_ok(hash_scalar(h), r, s, id, Bool.and(Bool.and(scalar_ok(r), scalar_ok(s)), Nat.is_lt(id, 4n)))
265
266def recover_len(+h: List<&2, U32>, +sig: List<&2, U32>, ok: Bool) -> Maybe<&2, List<&2, U32>>:
267 match ok:
268 case True{}: recover_sig(h, sig)
269 case False{}: None{}
270
271
272
273
274
275
276def recover(+h: List<&2, U32>, +sig: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
277 recover_len(h, sig, Bool.and(B.has_len(32n, h), B.has_len(65n, sig)))
278
279
280
281
282def word(+b0: U32, +b1: U32, +b2: U32, +b3: U32) -> U32:
283 U32.or(U32.or(b0, U32.shln(b1, 8n)), U32.or(U32.shln(b2, 16n), U32.shln(b3, 24n)))
284
285def words(n: Nat, +bs: List<&2, U32>) -> List<&2, U32>:
286 match n:
287 case 0n: Nil{}
288 case 1n+k: word(B.head(bs), B.head(B.tail(bs)), B.head(B.suffix(2n, bs)), B.head(B.suffix(3n, bs))) <> words(k, B.suffix(4n, bs))
289
290def pack(ws: List<&2, U32>, +i: U32, a: Array<U32>) -> Array<U32>:
291 match ws:
292 case Nil{}: a
293 case w <> t: pack(t, U32.inc(i), Array.set(U32, a, i, w))
294
295def unpack(ws: List<&1, U32>) -> List<&2, U32>:
296 match ws:
297 case Nil{}: Nil{}
298 case +w <> t: U32.and(w, 255) <> U32.and(U32.shrn(w, 8n), 255) <> U32.and(U32.shrn(w, 16n), 255) <> U32.shrn(w, 24n) <> unpack(t)
299
300def digest_bytes(m: Maybe<&1, Array<U32>>) -> List<&2, U32>:
301 match m:
302 case None{}: Nil{}
303 case Some{a}: unpack(Array.to_list(~U32, a))
304
305
306
307type Hash is Data:
308 Keccak256{}
309
310def hash(+h: Hash, a: Array<U32>, length: Nat) -> Maybe<&1, Array<U32>>:
311 match h:
312 case Keccak256{}: K.keccak256(a, length)
313
314
315def keccak64(+h: Hash, +bs: List<&2, U32>) -> List<&2, U32>:
316 digest_bytes(hash(h, pack(words(16n, bs), 0, Array.new(U32, 4n, 0)), 64n))
317
318def eth_if(ok: Bool, +dg: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
319 match ok:
320 case True{}: Some{B.suffix(12n, dg)}
321 case False{}: None{}
322
323def eth_go(+h: Hash, +pk: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
324 eth_if(Bool.and(B.has_len(65n, pk), U32.is_eq(B.head(pk), 4)), keccak64(h, B.tail(pk)))
325
326
327
328def eth_with(+h: Hash, pk: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
329 match pk:
330 case Nil{}: eth_go(h, Nil{})
331 case b <> t: eth_go(h, b <> t)
332
333
334
335def eth_address(+pk: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
336 eth_with(Keccak256{}, pk)
337
338
339
340def zeros_ok(bs: List<&2, U32>) -> Bool:
341 match bs:
342 case Nil{}: True{}
343 case b <> t: Bool.and(U32.is_eq(b, 0), zeros_ok(t))
344
345def eth_word(m: Maybe<&2, List<&2, U32>>) -> Maybe<&2, List<&2, U32>>:
346 match m:
347 case None{}: None{}
348 case Some{a}: Some{cat(fill(12n, 0), a)}
349
350def addr_word(m: Maybe<&2, List<&2, U32>>) -> Maybe<&2, List<&2, U32>>:
351 match m:
352 case None{}: None{}
353 case Some{pk}: eth_word(eth_address(pk))
354
355def ecrecover_v(+h: List<&2, U32>, +rs: List<&2, U32>, +v: U32, ok: Bool) -> Maybe<&2, List<&2, U32>>:
356 match ok:
357 case True{}: addr_word(recover(h, cat(rs, [U32.sub(v, 27)])))
358 case False{}: None{}
359
360def ecrecover_in(+x: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
361 +vw = B.prefix(32n, B.suffix(32n, x))
362 +v = B.head(B.suffix(31n, vw))
363 ecrecover_v(B.prefix(32n, x), B.suffix(64n, x), v, Bool.and(zeros_ok(B.prefix(31n, vw)), Bool.or(U32.is_eq(v, 27), U32.is_eq(v, 28))))
364
365
366
367
368
369
370def ecrecover(input: List<&2, U32>) -> Maybe<&2, List<&2, U32>>:
371 ecrecover_in(B.prefix(128n, cat(input, fill(128n, 0))))