Merge pull request #6239 from Yawning/feature/ecdsa

core:crypto/ecdsa: Add ECDSA support
This commit is contained in:
Jeroen van Rijn
2026-02-19 21:15:17 +01:00
committed by GitHub
20 changed files with 2550 additions and 343 deletions

View File

@@ -135,7 +135,7 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) ->
tmp: Montgomery_Domain_Field_Element = ---
fe_sub(&tmp, arg1, arg2)
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
is_eq := subtle.eq(fe_non_zero(&tmp), 0)
fe_clear(&tmp)
@@ -208,3 +208,271 @@ fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Eleme
fe_clear(&tmp1)
}
fe_pow2k :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^Montgomery_Domain_Field_Element,
arg2: uint,
) {
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
if arg2 == 0 {
fe_one(out1)
return
}
fe_square(out1, arg1)
for _ in 1 ..< arg2 {
fe_square(out1, out1)
}
}
fe_inv :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
// Inversion computation is derived from the addition chain:
//
// _10 = 2*1
// _100 = 2*_10
// _101 = 1 + _100
// _110 = 1 + _101
// _1001 = _100 + _101
// _1111 = _110 + _1001
// _10010 = 2*_1001
// _10101 = _110 + _1111
// _11000 = _110 + _10010
// _11010 = _10 + _11000
// _101111 = _10101 + _11010
// _111000 = _1001 + _101111
// _111101 = _101 + _111000
// _111111 = _10 + _111101
// _1001111 = _10010 + _111101
// _1100001 = _10010 + _1001111
// _1100011 = _10 + _1100001
// _1110011 = _10010 + _1100001
// _1110111 = _100 + _1110011
// _1111101 = _110 + _1110111
// _10010101 = _11000 + _1111101
// _10100111 = _10010 + _10010101
// _10101101 = _110 + _10100111
// _11100101 = _111000 + _10101101
// _11111111 = _11010 + _11100101
// x16 = _11111111 << 8 + _11111111
// x32 = x16 << 16 + x16
// i133 = ((x32 << 48 + x16) << 16 + x16) << 16
// i158 = ((x16 + i133) << 16 + x16) << 6 + _101111
// i186 = ((i158 << 9 + _1110011) << 8 + _1111101) << 9
// i206 = ((_10101101 + i186) << 8 + _10100111) << 9 + _101111
// i236 = ((i206 << 8 + _111101) << 11 + _1001111) << 9
// i257 = ((_1110111 + i236) << 10 + _11100101) << 8 + _1100001
// i286 = ((i257 << 7 + _111111) << 10 + _1100011) << 10
// return (_10010101 + i286) << 6 + _1111
//
// Operations: 251 squares 43 multiplies
//
// Generated by github.com/mmcloughlin/addchain v0.4.0.
// Note: Need to stash `arg1` (`xx`) in the case that `out1`/`arg1` alias,
// as `arg1` is used after `out1` has been altered.
t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, xx: Montgomery_Domain_Field_Element = ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, arg1^
// Step 1: t1 = x^0x2
fe_square(&t1, arg1)
// Step 2: t5 = x^0x4
fe_square(&t5, &t1)
// Step 3: t2 = x^0x5
fe_mul(&t2, arg1, &t5)
// Step 4: t10 = x^0x6
fe_mul(&t10, arg1, &t2)
// Step 5: t3 = x^0x9
fe_mul(&t3, &t5, &t2)
// Step 6: z = x^0xf
fe_mul(out1, &t10, &t3)
// Step 7: t9 = x^0x12
fe_square(&t9, &t3)
// Step 8: t4 = x^0x15
fe_mul(&t4, &t10, out1)
// Step 9: t0 = x^0x18
fe_mul(&t0, &t10, &t9)
// Step 10: t13 = x^0x1a
fe_mul(&t13, &t1, &t0)
// Step 11: t8 = x^0x2f
fe_mul(&t8, &t4, &t13)
// Step 12: t4 = x^0x38
fe_mul(&t4, &t3, &t8)
// Step 13: t7 = x^0x3d
fe_mul(&t7, &t2, &t4)
// Step 14: t2 = x^0x3f
fe_mul(&t2, &t1, &t7)
// Step 15: t6 = x^0x4f
fe_mul(&t6, &t9, &t7)
// Step 16: t3 = x^0x61
fe_mul(&t3, &t9, &t6)
// Step 17: t1 = x^0x63
fe_mul(&t1, &t1, &t3)
// Step 18: t12 = x^0x73
fe_mul(&t12, &t9, &t3)
// Step 19: t5 = x^0x77
fe_mul(&t5, &t5, &t12)
// Step 20: t11 = x^0x7d
fe_mul(&t11, &t10, &t5)
// Step 21: t0 = x^0x95
fe_mul(&t0, &t0, &t11)
// Step 22: t9 = x^0xa7
fe_mul(&t9, &t9, &t0)
// Step 23: t10 = x^0xad
fe_mul(&t10, &t10, &t9)
// Step 24: t4 = x^0xe5
fe_mul(&t4, &t4, &t10)
// Step 25: t13 = x^0xff
fe_mul(&t13, &t13, &t4)
// Step 33: t14 = x^0xff00
fe_pow2k(&t14, &t13, 8)
// Step 34: t13 = x^0xffff
fe_mul(&t13, &t13, &t14)
// Step 50: t14 = x^0xffff0000
fe_pow2k(&t14, &t13, 16)
// Step 51: t14 = x^0xffffffff
fe_mul(&t14, &t13, &t14)
// Step 99: t14 = x^0xffffffff000000000000
fe_pow2k(&t14, &t14, 48)
// Step 100: t14 = x^0xffffffff00000000ffff
fe_mul(&t14, &t13, &t14)
// Step 116: t14 = x^0xffffffff00000000ffff0000
fe_pow2k(&t14, &t14, 16)
// Step 117: t14 = x^0xffffffff00000000ffffffff
fe_mul(&t14, &t13, &t14)
// Step 133: t14 = x^0xffffffff00000000ffffffff0000
fe_pow2k(&t14, &t14, 16)
// Step 134: t14 = x^0xffffffff00000000ffffffffffff
fe_mul(&t14, &t13, &t14)
// Step 150: t14 = x^0xffffffff00000000ffffffffffff0000
fe_pow2k(&t14, &t14, 16)
// Step 151: t13 = x^0xffffffff00000000ffffffffffffffff
fe_mul(&t13, &t13, &t14)
// Step 157: t13 = x^0x3fffffffc00000003fffffffffffffffc0
fe_pow2k(&t13, &t13, 6)
// Step 158: t13 = x^0x3fffffffc00000003fffffffffffffffef
fe_mul(&t13, &t8, &t13)
// Step 167: t13 = x^0x7fffffff800000007fffffffffffffffde00
fe_pow2k(&t13, &t13, 9)
// Step 168: t12 = x^0x7fffffff800000007fffffffffffffffde73
fe_mul(&t12, &t12, &t13)
// Step 176: t12 = x^0x7fffffff800000007fffffffffffffffde7300
fe_pow2k(&t12, &t12, 8)
// Step 177: t11 = x^0x7fffffff800000007fffffffffffffffde737d
fe_mul(&t11, &t11, &t12)
// Step 186: t11 = x^0xffffffff00000000ffffffffffffffffbce6fa00
fe_pow2k(&t11, &t11, 9)
// Step 187: t10 = x^0xffffffff00000000ffffffffffffffffbce6faad
fe_mul(&t10, &t10, &t11)
// Step 195: t10 = x^0xffffffff00000000ffffffffffffffffbce6faad00
fe_pow2k(&t10, &t10, 8)
// Step 196: t9 = x^0xffffffff00000000ffffffffffffffffbce6faada7
fe_mul(&t9, &t9, &t10)
// Step 205: t9 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e00
fe_pow2k(&t9, &t9, 9)
// Step 206: t8 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f
fe_mul(&t8, &t8, &t9)
// Step 214: t8 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f00
fe_pow2k(&t8, &t8, 8)
// Step 215: t7 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f3d
fe_mul(&t7, &t7, &t8)
// Step 226: t7 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e800
fe_pow2k(&t7, &t7, 11)
// Step 227: t6 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f
fe_mul(&t6, &t6, &t7)
// Step 236: t6 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f3d09e00
fe_pow2k(&t6, &t6, 9)
// Step 237: t5 = x^0x1fffffffe00000001ffffffffffffffff79cdf55b4e2f3d09e77
fe_mul(&t5, &t5, &t6)
// Step 247: t5 = x^0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dc00
fe_pow2k(&t5, &t5, 10)
// Step 248: t4 = x^0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dce5
fe_mul(&t4, &t4, &t5)
// Step 256: t4 = x^0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dce500
fe_pow2k(&t4, &t4, 8)
// Step 257: t3 = x^0x7fffffff800000007fffffffffffffffde737d56d38bcf4279dce561
fe_mul(&t3, &t3, &t4)
// Step 264: t3 = x^0x3fffffffc00000003fffffffffffffffef39beab69c5e7a13cee72b080
fe_pow2k(&t3, &t3, 7)
// Step 265: t2 = x^0x3fffffffc00000003fffffffffffffffef39beab69c5e7a13cee72b0bf
fe_mul(&t2, &t2, &t3)
// Step 275: t2 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc00
fe_pow2k(&t2, &t2, 10)
// Step 276: t1 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63
fe_mul(&t1, &t1, &t2)
// Step 286: t1 = x^0x3fffffffc00000003fffffffffffffffef39beab69c5e7a13cee72b0bf18c00
fe_pow2k(&t1, &t1, 10)
// Step 287: t0 = x^0x3fffffffc00000003fffffffffffffffef39beab69c5e7a13cee72b0bf18c95
fe_mul(&t0, &t0, &t1)
// Step 293: t0 = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632540
fe_pow2k(&t0, &t0, 6)
// Step 294: z = x^0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc63254f
fe_mul(out1, out1, &t0)
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9, &t10, &t11, &t12, &t13, &t14, &xx})
}

View File

@@ -148,7 +148,7 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) ->
tmp: Montgomery_Domain_Field_Element = ---
fe_sub(&tmp, arg1, arg2)
is_eq := subtle.u64_is_zero(fe_non_zero(&tmp))
is_eq := subtle.eq(fe_non_zero(&tmp), 0)
fe_clear(&tmp)
@@ -237,3 +237,371 @@ fe_cond_negate :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Eleme
fe_clear(&tmp1)
}
fe_pow2k :: proc "contextless" (
out1: ^Montgomery_Domain_Field_Element,
arg1: ^Montgomery_Domain_Field_Element,
arg2: uint,
) {
// Special case: `arg1^(2 * 0) = 1`, though this should never happen.
if arg2 == 0 {
fe_one(out1)
return
}
fe_square(out1, arg1)
for _ in 1 ..< arg2 {
fe_square(out1, out1)
}
}
fe_inv :: proc "contextless" (out1, arg1: ^Montgomery_Domain_Field_Element) {
// Inversion computation is derived from the addition chain:
//
// _10 = 2*1
// _11 = 1 + _10
// _101 = _10 + _11
// _111 = _10 + _101
// _1001 = _10 + _111
// _1011 = _10 + _1001
// _1101 = _10 + _1011
// _1111 = _10 + _1101
// _11110 = 2*_1111
// _11111 = 1 + _11110
// _1111100 = _11111 << 2
// i14 = _1111100 << 2
// i26 = (i14 << 3 + _1111100) << 7 + i14
// i42 = i26 << 15 + i26
// x64 = i42 << 30 + i42 + _1111
// x128 = x64 << 64 + x64
// x192 = x128 << 64 + x64
// x194 = x192 << 2 + _11
// i225 = ((x194 << 6 + _111) << 3 + _11) << 7
// i235 = 2*((_1101 + i225) << 6 + _1101) + 1
// i258 = ((i235 << 11 + _11111) << 2 + 1) << 8
// i269 = ((_1101 + i258) << 2 + _11) << 6 + _1011
// i286 = ((i269 << 4 + _111) << 6 + _11111) << 5
// i308 = ((_1011 + i286) << 10 + _1101) << 9 + _1101
// i323 = ((i308 << 4 + _1011) << 6 + _1001) << 3
// i340 = ((1 + i323) << 7 + _1011) << 7 + _101
// i357 = ((i340 << 5 + _111) << 5 + _1111) << 5
// i369 = ((_1011 + i357) << 4 + _1011) << 5 + _111
// i387 = ((i369 << 3 + _11) << 7 + _11) << 6
// i397 = ((_1011 + i387) << 4 + _101) << 3 + _11
// i413 = ((i397 << 4 + _11) << 4 + _11) << 6
// i427 = ((_101 + i413) << 5 + _101) << 6 + _1011
// return (2*i427 + 1) << 4 + 1
//
// Operations: 381 squares 53 multiplies
//
// Generated by github.com/mmcloughlin/addchain v0.4.0.
// Note: Need to stash `arg1` (`xx`) in the case that `out1`/`arg1` alias,
// as `arg1` is used after `out1` has been altered.
t0, t1, t2, t3, t4, t5, t6, t7, t8, t9, xx: Montgomery_Domain_Field_Element = ---, ---, ---, ---, ---, ---, ---, ---, ---, ---, arg1^
// Step 1: t3 = x^0x2
fe_square(&t3, arg1)
// Step 2: t1 = x^0x3
fe_mul(&t1, arg1, &t3)
// Step 3: t0 = x^0x5
fe_mul(&t0, &t3, &t1)
// Step 4: t2 = x^0x7
fe_mul(&t2, &t3, &t0)
// Step 5: t4 = x^0x9
fe_mul(&t4, &t3, &t2)
// Step 6: z = x^0xb
fe_mul(out1, &t3, &t4)
// Step 7: t5 = x^0xd
fe_mul(&t5, &t3, out1)
// Step 8: t3 = x^0xf
fe_mul(&t3, &t3, &t5)
// Step 9: t6 = x^0x1e
fe_square(&t6, &t3)
// Step 10: t6 = x^0x1f
fe_mul(&t6, &xx, &t6)
// Step 12: t8 = x^0x7c
fe_pow2k(&t8, &t6, 2)
// Step 14: t7 = x^0x1f0
fe_pow2k(&t7, &t8, 2)
// Step 17: t9 = x^0xf80
fe_pow2k(&t9, &t7, 3)
// Step 18: t8 = x^0xffc
fe_mul(&t8, &t8, &t9)
// Step 25: t8 = x^0x7fe00
fe_pow2k(&t8, &t8, 7)
// Step 26: t7 = x^0x7fff0
fe_mul(&t7, &t7, &t8)
// Step 41: t8 = x^0x3fff80000
fe_pow2k(&t8, &t7, 15)
// Step 42: t7 = x^0x3fffffff0
fe_mul(&t7, &t7, &t8)
// Step 72: t8 = x^0xfffffffc00000000
fe_pow2k(&t8, &t7, 30)
// Step 73: t7 = x^0xfffffffffffffff0
fe_mul(&t7, &t7, &t8)
// Step 74: t7 = x^0xffffffffffffffff
fe_mul(&t7, &t3, &t7)
// Step 138: t8 = x^0xffffffffffffffff0000000000000000
fe_pow2k(&t8, &t7, 64)
// Step 139: t8 = x^0xffffffffffffffffffffffffffffffff
fe_mul(&t8, &t7, &t8)
// Step 203: t8 = x^0xffffffffffffffffffffffffffffffff0000000000000000
fe_pow2k(&t8, &t8, 64)
// Step 204: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffff
fe_mul(&t7, &t7, &t8)
// Step 206: t7 = x^0x3fffffffffffffffffffffffffffffffffffffffffffffffc
fe_pow2k(&t7, &t7, 2)
// Step 207: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff
fe_mul(&t7, &t1, &t7)
// Step 213: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc0
fe_pow2k(&t7, &t7, 6)
// Step 214: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7
fe_mul(&t7, &t2, &t7)
// Step 217: t7 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe38
fe_pow2k(&t7, &t7, 3)
// Step 218: t7 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b
fe_mul(&t7, &t1, &t7)
// Step 225: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d80
fe_pow2k(&t7, &t7, 7)
// Step 226: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d
fe_mul(&t7, &t5, &t7)
// Step 232: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc76340
fe_pow2k(&t7, &t7, 6)
// Step 233: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d
fe_mul(&t7, &t5, &t7)
// Step 234: t7 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69a
fe_square(&t7, &t7)
// Step 235: t7 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b
fe_mul(&t7, &xx, &t7)
// Step 246: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d800
fe_pow2k(&t7, &t7, 11)
// Step 247: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f
fe_mul(&t7, &t6, &t7)
// Step 249: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607c
fe_pow2k(&t7, &t7, 2)
// Step 250: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d
fe_mul(&t7, &xx, &t7)
// Step 258: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d00
fe_pow2k(&t7, &t7, 8)
// Step 259: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0d
fe_mul(&t7, &t5, &t7)
// Step 261: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f434
fe_pow2k(&t7, &t7, 2)
// Step 262: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f437
fe_mul(&t7, &t1, &t7)
// Step 268: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dc0
fe_pow2k(&t7, &t7, 6)
// Step 269: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb
fe_mul(&t7, out1, &t7)
// Step 273: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb0
fe_pow2k(&t7, &t7, 4)
// Step 274: t7 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb7
fe_mul(&t7, &t2, &t7)
// Step 280: t7 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372dc0
fe_pow2k(&t7, &t7, 6)
// Step 281: t6 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf
fe_mul(&t6, &t6, &t7)
// Step 286: t6 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbe0
fe_pow2k(&t6, &t6, 5)
// Step 287: t6 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb
fe_mul(&t6, out1, &t6)
// Step 297: t6 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac00
fe_pow2k(&t6, &t6, 10)
// Step 298: t6 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d
fe_mul(&t6, &t5, &t6)
// Step 307: t6 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a00
fe_pow2k(&t6, &t6, 9)
// Step 308: t5 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0d
fe_mul(&t5, &t5, &t6)
// Step 312: t5 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0d0
fe_pow2k(&t5, &t5, 4)
// Step 313: t5 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db
fe_mul(&t5, out1, &t5)
// Step 319: t5 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c0
fe_pow2k(&t5, &t5, 6)
// Step 320: t4 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c9
fe_mul(&t4, &t4, &t5)
// Step 323: t4 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b648
fe_pow2k(&t4, &t4, 3)
// Step 324: t4 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b649
fe_mul(&t4, &xx, &t4)
// Step 331: t4 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db2480
fe_pow2k(&t4, &t4, 7)
// Step 332: t4 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b
fe_mul(&t4, out1, &t4)
// Step 339: t4 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d924580
fe_pow2k(&t4, &t4, 7)
// Step 340: t4 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d924585
fe_mul(&t4, &t0, &t4)
// Step 345: t4 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a0
fe_pow2k(&t4, &t4, 5)
// Step 346: t4 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a7
fe_mul(&t4, &t2, &t4)
// Step 351: t4 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614e0
fe_pow2k(&t4, &t4, 5)
// Step 352: t3 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef
fe_mul(&t3, &t3, &t4)
// Step 357: t3 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29de0
fe_pow2k(&t3, &t3, 5)
// Step 358: t3 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29deb
fe_mul(&t3, out1, &t3)
// Step 362: t3 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29deb0
fe_pow2k(&t3, &t3, 4)
// Step 363: t3 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb
fe_mul(&t3, out1, &t3)
// Step 368: t3 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd760
fe_pow2k(&t3, &t3, 5)
// Step 369: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd767
fe_mul(&t2, &t2, &t3)
// Step 372: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb38
fe_pow2k(&t2, &t2, 3)
// Step 373: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b
fe_mul(&t2, &t1, &t2)
// Step 380: t2 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef5d9d80
fe_pow2k(&t2, &t2, 7)
// Step 381: t2 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef5d9d83
fe_mul(&t2, &t1, &t2)
// Step 387: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760c0
fe_pow2k(&t2, &t2, 6)
// Step 388: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb
fe_mul(&t2, out1, &t2)
// Step 392: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb0
fe_pow2k(&t2, &t2, 4)
// Step 393: t2 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb5
fe_mul(&t2, &t0, &t2)
// Step 396: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065a8
fe_pow2k(&t2, &t2, 3)
// Step 397: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab
fe_mul(&t2, &t1, &t2)
// Step 401: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab0
fe_pow2k(&t2, &t2, 4)
// Step 402: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab3
fe_mul(&t2, &t1, &t2)
// Step 406: t2 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab30
fe_pow2k(&t2, &t2, 4)
// Step 407: t1 = x^0x3ffffffffffffffffffffffffffffffffffffffffffffffff1d8d3607d0dcb77d606836c922c29debb3b065ab33
fe_mul(&t1, &t1, &t2)
// Step 413: t1 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc0
fe_pow2k(&t1, &t1, 6)
// Step 414: t1 = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5
fe_mul(&t1, &t0, &t1)
// Step 419: t1 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef5d9d832d5998a0
fe_pow2k(&t1, &t1, 5)
// Step 420: t0 = x^0x1ffffffffffffffffffffffffffffffffffffffffffffffff8ec69b03e86e5bbeb0341b6491614ef5d9d832d5998a5
fe_mul(&t0, &t0, &t1)
// Step 426: t0 = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb56662940
fe_pow2k(&t0, &t0, 6)
// Step 427: z = x^0x7fffffffffffffffffffffffffffffffffffffffffffffffe3b1a6c0fa1b96efac0d06d9245853bd76760cb5666294b
fe_mul(out1, out1, &t0)
// Step 428: z = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5296
fe_square(out1, out1)
// Step 429: z = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc5297
fe_mul(out1, &xx, out1)
// Step 433: z = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52970
fe_pow2k(out1, out1, 4)
// Step 434: z = x^0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52971
fe_mul(out1, &xx, out1)
fe_clear_vec([]^Montgomery_Domain_Field_Element{&t0, &t1, &t2, &t3, &t4, &t5, &t6, &t7, &t8, &t9, &xx})
}

View File

@@ -1,5 +1,6 @@
package _weierstrass
@(require) import "core:crypto"
import p256r1 "core:crypto/_fiat/field_scalarp256r1"
import p384r1 "core:crypto/_fiat/field_scalarp384r1"
import subtle "core:crypto/_subtle"
@@ -9,6 +10,41 @@ Scalar_p384r1 :: p384r1.Montgomery_Domain_Field_Element
SC_SIZE_P256R1 :: 32
SC_SIZE_P384R1 :: 48
SC_SIZE_MAX :: SC_SIZE_P384R1
sc_size :: proc "contextless" (sc: ^$T) -> int where T == Scalar_p256r1 || T == Scalar_p384r1 {
when T == Scalar_p256r1 {
return SC_SIZE_P256R1
} else when T == Scalar_p384r1 {
return SC_SIZE_P384R1
}
}
sc_set_random :: proc(sc: ^$T) where T == Scalar_p256r1 || T == Scalar_p384r1 {
ensure(crypto.HAS_RAND_BYTES, "weierstrass: entropy source required")
b: [48]byte = ---
defer crypto.zero_explicit(&b, size_of(b))
when T == Scalar_p256r1 {
// 384-bits reduced makes the modulo bias insignificant
for {
crypto.rand_bytes(b[:])
_ = sc_set_bytes(sc, b[:])
if sc_is_zero(sc) == 0 { // Likely
break
}
}
} else when T == Scalar_p384r1 {
for {
crypto.rand_bytes(b[:])
did_reduce := sc_set_bytes(sc, b[:])
if !did_reduce && sc_is_zero(sc) == 0 { // Likely
break
}
}
}
}
sc_clear :: proc {
p256r1.fe_clear,
@@ -40,7 +76,7 @@ sc_zero :: proc {
p384r1.fe_zero,
}
sc_one_p256r1 :: proc {
sc_one :: proc {
p256r1.fe_one,
p384r1.fe_one,
}
@@ -70,6 +106,11 @@ sc_square :: proc {
p384r1.fe_square,
}
sc_inv :: proc {
p256r1.fe_inv,
p384r1.fe_inv,
}
sc_cond_assign :: proc {
p256r1.fe_cond_assign,
p384r1.fe_cond_assign,

View File

@@ -73,6 +73,81 @@ pt_scalar_mul_bytes :: proc "contextless" (
}
}
pt_double_scalar_mul_generator_vartime :: proc "contextless" (
p, q: ^$T,
sc_g, sc_q: ^$Q,
) {
// Strauss-Shamir, commonly referred to as the "Shamir trick",
// saves half the doublings, relative to doing this the naive way.
//
// Note: In the unlikely event where we support curves with an
// efficent endomorphism (secp256k1), scalarmul + GLV is faster.
when T == Point_p256r1 && Q == Scalar_p256r1 {
q_tbl: Multiply_Table_p256r1 = ---
SC_SZ :: SC_SIZE_P256R1
} else when T == Point_p384r1 && Q == Scalar_p384r1 {
q_tbl: Multiply_Table_p384r1 = ---
SC_SZ :: SC_SIZE_P384R1
} else {
#panic("weierstrass: invalid curve")
}
sc_q_bytes, sc_g_bytes: [SC_SZ]byte = ---, ---
sc_bytes(sc_q_bytes[:], sc_q)
sc_bytes(sc_g_bytes[:], sc_g)
r, tmp: T = ---, ---
when crypto.COMPACT_IMPLS == true {
pt_generator(&r)
when T == Point_p256r1 {
g_tbl: Multiply_Table_p256r1 = ---
} else when T == Point_p384r1 {
g_tbl: Multiply_Table_p384r1 = ---
}
mul_tbl_set(&g_tbl, &r, true)
} else {
when T == Point_p256r1 {
g_tbl := &Gen_Multiply_Table_p256r1_lo[31]
} else when T == Point_p384r1 {
g_tbl := &Gen_Multiply_Table_p384r1_lo[47]
}
}
mul_tbl_set(&q_tbl, q, true)
pt_identity(&r)
for i in 0..<SC_SZ {
limb_byte_q, limb_byte_g := sc_q_bytes[i], sc_g_bytes[i]
hi_q, lo_q := (limb_byte_q >> 4) & 0x0f, limb_byte_q & 0x0f
hi_g, lo_g := (limb_byte_g >> 4) & 0x0f, limb_byte_g & 0x0f
if i != 0 {
pt_double(&r, &r)
pt_double(&r, &r)
pt_double(&r, &r)
pt_double(&r, &r)
}
mul_tbl_lookup_add(&r, &tmp, &q_tbl, u64(hi_q), true)
when crypto.COMPACT_IMPLS == true {
mul_tbl_lookup_add(&r, &tmp, &g_tbl, u64(hi_g), true)
} else {
mul_affine_tbl_lookup_add(&r, &tmp, g_tbl, u64(hi_g), true)
}
pt_double(&r, &r)
pt_double(&r, &r)
pt_double(&r, &r)
pt_double(&r, &r)
mul_tbl_lookup_add(&r, &tmp, &q_tbl, u64(lo_q), true)
when crypto.COMPACT_IMPLS == true {
mul_tbl_lookup_add(&r, &tmp, &g_tbl, u64(lo_g), true)
} else {
mul_affine_tbl_lookup_add(&r, &tmp, g_tbl, u64(lo_g), true)
}
}
pt_set(p, &r)
}
when crypto.COMPACT_IMPLS == true {
pt_scalar_mul_generator :: proc "contextless" (
p: ^$T,

View File

@@ -121,29 +121,10 @@ private_key_generate :: proc(priv_key: ^Private_Key, curve: Curve) -> bool {
#partial switch curve {
case .SECP256R1:
sc := &priv_key._impl.(secec.Scalar_p256r1)
// 384-bits reduced makes the modulo bias insignificant
b: [48]byte = ---
defer (crypto.zero_explicit(&b, size_of(b)))
for {
crypto.rand_bytes(b[:])
_ = secec.sc_set_bytes(sc, b[:])
if secec.sc_is_zero(sc) == 0 { // Likely
break
}
}
secec.sc_set_random(sc)
case .SECP384R1:
sc := &priv_key._impl.(secec.Scalar_p384r1)
b: [48]byte = ---
defer (crypto.zero_explicit(&b, size_of(b)))
for {
crypto.rand_bytes(b[:])
did_reduce := secec.sc_set_bytes(sc, b[:])
if !did_reduce && secec.sc_is_zero(sc) == 0 { // Likely
break
}
}
secec.sc_set_random(sc)
case .X25519:
sc := &priv_key._impl.(X25519_Buf)
crypto.rand_bytes(sc[:])

View File

@@ -0,0 +1,7 @@
/*
Elliptic Curve Digital Signature Algorithm per SEC 2.0 section 4.1.
See:
- [[ https://secg.org/sec1-v2.pdf ]]
*/
package ecdsa

View File

@@ -0,0 +1,321 @@
package ecdsa
import "core:crypto"
import secec "core:crypto/_weierstrass"
import "core:mem"
import "core:reflect"
// Curve the curve identifier associated with a given Private_Key
// or Public_Key
Curve :: enum {
Invalid,
SECP256R1,
SECP384R1,
}
// CURVE_NAMES is the Curve to curve name string.
CURVE_NAMES := [Curve]string {
.Invalid = "Invalid",
.SECP256R1 = "secp256r1",
.SECP384R1 = "secp384r1",
}
// PRIVATE_KEY_SIZES is the Curve to private key size in bytes.
PRIVATE_KEY_SIZES := [Curve]int {
.Invalid = 0,
.SECP256R1 = secec.SC_SIZE_P256R1,
.SECP384R1 = secec.SC_SIZE_P384R1,
}
// PUBLIC_KEY_SIZES is the Curve to public key size in bytes.
PUBLIC_KEY_SIZES := [Curve]int {
.Invalid = 0,
.SECP256R1 = 1 + 2 * secec.FE_SIZE_P256R1,
.SECP384R1 = 1 + 2 * secec.FE_SIZE_P384R1,
}
// RAW_SIGNATURE_SIZES is the Curve to "raw" signature size in bytes.
RAW_SIGNATURE_SIZES := [Curve]int {
.Invalid = 0,
.SECP256R1 = 2 * secec.SC_SIZE_P256R1,
.SECP384R1 = 2 * secec.SC_SIZE_P384R1,
}
@(private="file")
_PRIV_IMPL_IDS := [Curve]typeid {
.Invalid = nil,
.SECP256R1 = typeid_of(secec.Scalar_p256r1),
.SECP384R1 = typeid_of(secec.Scalar_p384r1),
}
@(private="file")
_PUB_IMPL_IDS := [Curve]typeid {
.Invalid = nil,
.SECP256R1 = typeid_of(secec.Point_p256r1),
.SECP384R1 = typeid_of(secec.Point_p384r1),
}
// Private_Key is an ECDSA private key.
Private_Key :: struct {
// WARNING: All of the members are to be treated as internal (ie:
// the Private_Key structure is intended to be opaque).
_curve: Curve,
_impl: union {
secec.Scalar_p256r1,
secec.Scalar_p384r1,
},
_pub_key: Public_Key,
}
// Public_Key is an ECDSA public key.
Public_Key :: struct {
// WARNING: All of the members are to be treated as internal (ie:
// the Public_Key structure is intended to be opaque).
_curve: Curve,
_impl: union {
secec.Point_p256r1,
secec.Point_p384r1,
},
}
// private_key_generate uses the system entropy source to generate a new
// Private_Key. This will only fail iff the system entropy source is
// missing or broken.
private_key_generate :: proc(priv_key: ^Private_Key, curve: Curve) -> bool {
private_key_clear(priv_key)
if !crypto.HAS_RAND_BYTES {
return false
}
reflect.set_union_variant_typeid(
priv_key._impl,
_PRIV_IMPL_IDS[curve],
)
#partial switch curve {
case .SECP256R1:
sc := &priv_key._impl.(secec.Scalar_p256r1)
secec.sc_set_random(sc)
case .SECP384R1:
sc := &priv_key._impl.(secec.Scalar_p384r1)
secec.sc_set_random(sc)
case:
panic("crypto/ecdsa: invalid curve")
}
priv_key._curve = curve
private_key_generate_public(priv_key)
return true
}
// private_key_set_bytes decodes a byte-encoded private key, and returns
// true iff the operation was successful.
private_key_set_bytes :: proc(priv_key: ^Private_Key, curve: Curve, b: []byte) -> bool {
private_key_clear(priv_key)
if len(b) != PRIVATE_KEY_SIZES[curve] {
return false
}
reflect.set_union_variant_typeid(
priv_key._impl,
_PRIV_IMPL_IDS[curve],
)
#partial switch curve {
case .SECP256R1:
sc := &priv_key._impl.(secec.Scalar_p256r1)
did_reduce := secec.sc_set_bytes(sc, b)
is_zero := secec.sc_is_zero(sc) == 1
// Reject `0` and scalars that are not less than the
// curve order.
if did_reduce || is_zero {
private_key_clear(priv_key)
return false
}
case .SECP384R1:
sc := &priv_key._impl.(secec.Scalar_p384r1)
did_reduce := secec.sc_set_bytes(sc, b)
is_zero := secec.sc_is_zero(sc) == 1
// Reject `0` and scalars that are not less than the
// curve order.
if did_reduce || is_zero {
private_key_clear(priv_key)
return false
}
case:
panic("crypto/esa: invalid curve")
}
priv_key._curve = curve
private_key_generate_public(priv_key)
return true
}
@(private="file")
private_key_generate_public :: proc(priv_key: ^Private_Key) {
switch &sc in priv_key._impl {
case secec.Scalar_p256r1:
pub_key: secec.Point_p256r1 = ---
secec.pt_scalar_mul_generator(&pub_key, &sc)
secec.pt_rescale(&pub_key, &pub_key)
priv_key._pub_key._impl = pub_key
case secec.Scalar_p384r1:
pub_key: secec.Point_p384r1 = ---
secec.pt_scalar_mul_generator(&pub_key, &sc)
secec.pt_rescale(&pub_key, &pub_key)
priv_key._pub_key._impl = pub_key
case:
panic("crypto/ecdsa: invalid curve")
}
priv_key._pub_key._curve = priv_key._curve
}
// private_key_bytes sets dst to byte-encoding of priv_key.
private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) {
ensure(priv_key._curve != .Invalid, "crypto/ecdsa: uninitialized private key")
ensure(len(dst) == PRIVATE_KEY_SIZES[priv_key._curve], "crypto/ecdsa: invalid destination size")
#partial switch priv_key._curve {
case .SECP256R1:
sc := &priv_key._impl.(secec.Scalar_p256r1)
secec.sc_bytes(dst, sc)
case .SECP384R1:
sc := &priv_key._impl.(secec.Scalar_p384r1)
secec.sc_bytes(dst, sc)
case:
panic("crypto/ecdsa: invalid curve")
}
}
// private_key_equal returns true iff the private keys are equal,
// in constant time.
private_key_equal :: proc(p, q: ^Private_Key) -> bool {
if p._curve != q._curve {
return false
}
#partial switch p._curve {
case .SECP256R1:
sc_p, sc_q := &p._impl.(secec.Scalar_p256r1), &q._impl.(secec.Scalar_p256r1)
return secec.sc_equal(sc_p, sc_q) == 1
case .SECP384R1:
sc_p, sc_q := &p._impl.(secec.Scalar_p384r1), &q._impl.(secec.Scalar_p384r1)
return secec.sc_equal(sc_p, sc_q) == 1
case:
return false
}
}
// private_key_clear clears priv_key to the uninitialized state.
private_key_clear :: proc "contextless" (priv_key: ^Private_Key) {
mem.zero_explicit(priv_key, size_of(Private_Key))
}
// public_key_set_bytes decodes a byte-encoded public key, and returns
// true iff the operation was successful.
public_key_set_bytes :: proc(pub_key: ^Public_Key, curve: Curve, b: []byte) -> bool {
public_key_clear(pub_key)
if len(b) != PUBLIC_KEY_SIZES[curve] {
return false
}
reflect.set_union_variant_typeid(
pub_key._impl,
_PUB_IMPL_IDS[curve],
)
#partial switch curve {
case .SECP256R1:
if b[0] != secec.SEC_PREFIX_UNCOMPRESSED {
return false
}
pt := &pub_key._impl.(secec.Point_p256r1)
ok := secec.pt_set_sec_bytes(pt, b)
if !ok || secec.pt_is_identity(pt) == 1 {
return false
}
case .SECP384R1:
if b[0] != secec.SEC_PREFIX_UNCOMPRESSED {
return false
}
pt := &pub_key._impl.(secec.Point_p384r1)
ok := secec.pt_set_sec_bytes(pt, b)
if !ok || secec.pt_is_identity(pt) == 1 {
return false
}
case:
panic("crypto/ecdsa: invalid curve")
}
pub_key._curve = curve
return true
}
// public_key_set_priv sets pub_key to the public component of priv_key.
public_key_set_priv :: proc(pub_key: ^Public_Key, priv_key: ^Private_Key) {
ensure(priv_key._curve != .Invalid, "crypto/ecdsa: uninitialized private key")
public_key_clear(pub_key)
pub_key^ = priv_key._pub_key
}
// public_key_bytes sets dst to byte-encoding of pub_key.
public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) {
ensure(pub_key._curve != .Invalid, "crypto/ecdsa: uninitialized public key")
ensure(len(dst) == PUBLIC_KEY_SIZES[pub_key._curve], "crypto/ecdsa: invalid destination size")
#partial switch pub_key._curve {
case .SECP256R1:
// Invariant: Unless the caller is manually building pub_key
// `Z = 1`, so we can skip the rescale.
pt := &pub_key._impl.(secec.Point_p256r1)
dst[0] = secec.SEC_PREFIX_UNCOMPRESSED
secec.fe_bytes(dst[1:1+secec.FE_SIZE_P256R1], &pt.x)
secec.fe_bytes(dst[1+secec.FE_SIZE_P256R1:], &pt.y)
case .SECP384R1:
// Invariant: Unless the caller is manually building pub_key
// `Z = 1`, so we can skip the rescale.
pt := &pub_key._impl.(secec.Point_p384r1)
dst[0] = secec.SEC_PREFIX_UNCOMPRESSED
secec.fe_bytes(dst[1:1+secec.FE_SIZE_P384R1], &pt.x)
secec.fe_bytes(dst[1+secec.FE_SIZE_P384R1:], &pt.y)
case:
panic("crypto/ecdsa: invalid curve")
}
}
// public_key_equal returns true iff the public keys are equal,
// in constant time.
public_key_equal :: proc(p, q: ^Public_Key) -> bool {
if p._curve != q._curve {
return false
}
#partial switch p._curve {
case .SECP256R1:
pt_p, pt_q := &p._impl.(secec.Point_p256r1), &q._impl.(secec.Point_p256r1)
return secec.pt_equal(pt_p, pt_q) == 1
case .SECP384R1:
pt_p, pt_q := &p._impl.(secec.Point_p384r1), &q._impl.(secec.Point_p384r1)
return secec.pt_equal(pt_p, pt_q) == 1
case:
panic("crypto/ecdsa: invalid curve")
}
}
// public_key_clear clears pub_key to the uninitialized state.
public_key_clear :: proc "contextless" (pub_key: ^Public_Key) {
mem.zero_explicit(pub_key, size_of(Public_Key))
}

View File

@@ -0,0 +1,183 @@
package ecdsa
import secec "core:crypto/_weierstrass"
// ASN.1 format ECDSA signatures are`SEQUENCE { r INTEGER, s INTEGER }`
// this implements enough to generate/parse signatures. Eventually when
// we have a full ASN.1 DER library, these routines will be removed.
@(private="file")
TAG_SEQUENCE :: 0x30
@(private="file")
TAG_INTEGER :: 0x02
@(private,require_results)
generate_asn1_sig :: proc(r, s: ^$T, allocator := context.allocator) -> []byte {
when T == secec.Scalar_p256r1 {
SC_SZ :: secec.SC_SIZE_P256R1
} else when T == secec.Scalar_p384r1 {
SC_SZ :: secec.SC_SIZE_P384R1
} else {
#panic("crypto/ecdsa: invalid curve")
}
INT_TLP :: 3 // tag, tength, (optional) leading zero-byte
encode_uint :: proc(b: []byte) -> []byte {
b := b
// DER requires minimal encoding.
off := INT_TLP
for v in b[off:] {
if v != 0 {
break
}
off += 1
}
// If the sign big is set, add a leading zero.
if b[off] & 0x80 == 0x80 {
off -= 1
b[off] = 0
}
// Encode the length (up to 127 octets, adequate for ECDSA).
l := len(b[off:])
off -= 1
b[off] = byte(l)
// Encode the tag
off -= 1
b[off] = TAG_INTEGER
return b[off:]
}
r_buf, s_buf: [INT_TLP+SC_SZ]byte = ---, ---
secec.sc_bytes(r_buf[INT_TLP:], r)
secec.sc_bytes(s_buf[INT_TLP:], s)
r_bytes, s_bytes := encode_uint(r_buf[:]), encode_uint(s_buf[:])
seq_len := len(r_bytes) + len(s_bytes)
// WARNING: If secp521r1 support is added, this needs to support
// long-form length encoding.
ensure(seq_len <= 127, "BUG: crypto/ecdsa: signature length too large")
b := make([]byte, seq_len + 2, allocator)
b[0] = TAG_SEQUENCE
b[1] = byte(seq_len)
copy(b[2:], r_bytes)
copy(b[2+len(r_bytes):], s_bytes)
return b
}
@(private,require_results)
parse_asn1_sig :: proc(sig: []byte) -> (r, s: []byte, ok: bool) {
read_seq :: proc(b: []byte) -> (v: []byte, rest: []byte, ok: bool) {
b_len := len(b)
if b_len < 3 {
return nil, nil, false
}
if b[0] != TAG_SEQUENCE {
return nil, nil, false
}
seq_len, off: int
if b[1] & 0x80 == 0x80 {
if b[1] != 0x81 || b_len < 4 { // 2-length octets is sufficient for ecdsa.
return nil, nil, false
}
if b[2] & 0x80 == 0x80 || b[3] & 0x80 == 80 {
return nil, nil, false
}
seq_len = int(b[2]) * 127 + int(b[3])
off = 4
} else {
seq_len = int(b[1])
off = 2
}
if b_len - off < seq_len {
return nil, nil, false
}
return b[off:off+seq_len], b[off+seq_len:], true
}
read_int :: proc(b: []byte) -> (v: []byte, rest: []byte, ok: bool) {
b_len := len(b)
if b_len < 3 {
return nil, nil, false
}
if b[0] != TAG_INTEGER {
return nil, nil, false
}
v_len := int(b[1])
if v_len > 0x80 || b_len - 2 < v_len { // 127-bytes max.
return nil, nil, false
}
return b[2:2+v_len], b[2+v_len:], true
}
// SEQUENCE
seq_bytes, rest: []byte
seq_bytes, rest, ok = read_seq(sig)
if !ok {
return nil, nil, false
}
if len(rest) != 0 {
return nil, nil, false
}
// INTEGER (r)
r, rest, ok = read_int(seq_bytes)
if !ok {
return nil, nil, false
}
// INTEGER (s)
s, rest, ok = read_int(rest)
if !ok {
return nil, nil, false
}
if len(rest) != 0 {
return nil, nil, false
}
// DER requires a leading 0 iff the sign bit of the leading byte
// is set to distinguish between positive and negative integers,
// and the minimal length representation. `r` and `s` are always
// going to be unsigned, so we validate malformed DER and strip
// the leading 0 as needed.
fixup_der_uint :: proc(b: []byte) -> ([]byte, bool) {
switch len(b) {
case 0:
// 0 length is invalid
return nil, false
case 1:
// Missing leading zero
if b[0] & 0x80 == 0x80 {
return nil, false
}
case:
if b[0] == 0 {
// Sign bit not set
if b[1] & 0x80 != 0x80 {
return nil, false
}
return b[1:], true
} else if b[0] & 0x80 == 0x80 {
// Missing leading zero
return nil, false
}
}
return b, true
}
if r, ok = fixup_der_uint(r); !ok {
return nil, nil, false
}
if s, ok = fixup_der_uint(s); !ok {
return nil, nil, false
}
return r, s, true
}

View File

@@ -0,0 +1,98 @@
package ecdsa
import "core:crypto"
import "core:crypto/hash"
import "core:crypto/hmac"
import secec "core:crypto/_weierstrass"
@(private)
Drbg_RFC6979 :: struct {
v: [hash.MAX_DIGEST_SIZE]byte,
k: [hash.MAX_DIGEST_SIZE]byte,
algorithm: hash.Algorithm,
sz: int,
need_update: bool,
}
@(private)
init_drbg_rfc6979 :: proc(rng: ^Drbg_RFC6979, algorithm: hash.Algorithm, x_bytes, e_bytes: []byte, deterministic: bool) {
rng.algorithm = algorithm
rng.sz = hash.DIGEST_SIZES[algorithm]
rng.need_update = false
for i in 0..<rng.sz {
rng.v[i] = 0x01
rng.k[i] = 0x00
}
if !deterministic {
additional_input: [64]byte
crypto.rand_bytes(additional_input[:])
defer crypto.zero_explicit(&additional_input, size_of(additional_input))
drbg_init_update_k(rng, x_bytes, e_bytes, 0x00, additional_input[:])
drbg_update_v(rng)
drbg_init_update_k(rng, x_bytes, e_bytes, 0x01, nil)
drbg_update_v(rng)
} else {
drbg_init_update_k(rng, x_bytes, e_bytes, 0x00, nil)
drbg_update_v(rng)
drbg_init_update_k(rng, x_bytes, e_bytes, 0x01, nil)
drbg_update_v(rng)
}
}
@(private)
drbg_read_rfc6979 :: proc(rng: ^Drbg_RFC6979, sc: ^$T) -> bool {
// Extremely unlikely, so avoid unless the rejection
// sampling triggers.
if rng.need_update {
drbg_update_k(rng)
drbg_update_v(rng)
}
drbg_update_v(rng)
rng.need_update = true
return secec.sc_set_bytes(sc, rng.v[:secec.sc_size(sc)])
}
@(private)
drbg_clear_rfc6979 :: proc(rng: ^Drbg_RFC6979) {
crypto.zero_explicit(rng, size_of(Drbg_RFC6979))
}
@(private="file")
drbg_update_v :: proc(rng: ^Drbg_RFC6979) {
// V = HMAC_K(V)
k, v := rng.k[:rng.sz], rng.v[:rng.sz]
hmac.sum(rng.algorithm, v, v, k)
}
@(private="file")
drbg_update_k :: proc(rng: ^Drbg_RFC6979) {
// K = HMAC_K(V || 0x00)
k, v := rng.k[:rng.sz], rng.v[:rng.sz]
ctx: hmac.Context
hmac.init(&ctx, rng.algorithm, k)
hmac.update(&ctx, v)
hmac.update(&ctx, []byte{0x00})
hmac.final(&ctx, k)
}
@(private="file")
drbg_init_update_k :: proc(rng: ^Drbg_RFC6979, i2o_b, b2o_h1: []byte, internal_octet: byte, additional_input: []byte) {
k, v := rng.k[:rng.sz], rng.v[:rng.sz]
ctx: hmac.Context
hmac.init(&ctx, rng.algorithm, k)
hmac.update(&ctx, v)
hmac.update(&ctx, []byte{internal_octet})
hmac.update(&ctx, i2o_b)
hmac.update(&ctx, b2o_h1)
if additional_input != nil {
hmac.update(&ctx, additional_input)
}
hmac.final(&ctx, k)
}

View File

@@ -0,0 +1,201 @@
package ecdsa
import "base:runtime"
import "core:crypto"
import "core:crypto/hash"
import secec "core:crypto/_weierstrass"
// sign_asn1 returns the signature by priv_key over msg hased using hash_algo
// using the signing procedure as specified in SEC 1, Version 2.0, Section
// 4.1.3. ASN.1 DER requires minimal encoding, and the format is clunky
// and variable-length so for simplicity we allocate the signature.
//
// The signature format is ASN1. `SEQUECE `{ r INTEGER, s INTEGER }`.
@(require_results)
sign_asn1 :: proc(priv_key: ^Private_Key, hash_algo: hash.Algorithm, msg: []byte, allocator: runtime.Allocator, deterministic := !crypto.HAS_RAND_BYTES) -> ([]byte, bool) {
ensure(hash_algo != .Invalid, "crypto/edsa: invalid hash algorithm")
ensure(priv_key._curve != .Invalid, "crypto/edsa: invalid curve")
if !deterministic && !crypto.HAS_RAND_BYTES {
return nil, false
}
#partial switch priv_key._curve {
case .SECP256R1:
SC_SZ :: secec.SC_SIZE_P256R1
RAW_SIG_SZ :: 2 * SC_SZ
r, s: secec.Scalar_p256r1 = ---, ---
sk := &priv_key._impl.(secec.Scalar_p256r1)
sign_internal(sk, &r, &s, hash_algo, msg, deterministic)
return generate_asn1_sig(&r, &s, allocator), true
case .SECP384R1:
SC_SZ :: secec.SC_SIZE_P384R1
RAW_SIG_SZ :: 2 * SC_SZ
r, s: secec.Scalar_p384r1 = ---, ---
sk := &priv_key._impl.(secec.Scalar_p384r1)
sign_internal(sk, &r, &s, hash_algo, msg, deterministic)
return generate_asn1_sig(&r, &s, allocator), true
}
return nil, false
}
// sign_raw writes the signature by priv_key over msg hased using hash_algo
// to sig, using the signing procedure as specified in SEC 1, Version 2.0,
// Section 4.1.3.
//
// The signature format is `r | s`.
@(require_results)
sign_raw :: proc(priv_key: ^Private_Key, hash_algo: hash.Algorithm, msg, sig: []byte, deterministic := !crypto.HAS_RAND_BYTES) -> bool {
ensure(hash_algo != .Invalid, "crypto/edsa: invalid hash algorithm")
ensure(priv_key._curve != .Invalid, "crypto/edsa: invalid curve")
ensure(len(sig) == RAW_SIGNATURE_SIZES[priv_key._curve], "crypto/ecdsa: invalid destination size")
if !deterministic && !crypto.HAS_RAND_BYTES {
return false
}
#partial switch priv_key._curve {
case .SECP256R1:
SC_SZ :: secec.SC_SIZE_P256R1
r, s: secec.Scalar_p256r1 = ---, ---
sk := &priv_key._impl.(secec.Scalar_p256r1)
sign_internal(sk, &r, &s, hash_algo, msg, deterministic)
r_bytes, s_bytes := sig[:SC_SZ], sig[SC_SZ:]
secec.sc_bytes(r_bytes, &r)
secec.sc_bytes(s_bytes, &s)
case .SECP384R1:
SC_SZ :: secec.SC_SIZE_P384R1
r, s: secec.Scalar_p384r1 = ---, ---
sk := &priv_key._impl.(secec.Scalar_p384r1)
sign_internal(sk, &r, &s, hash_algo, msg, deterministic)
r_bytes, s_bytes := sig[:SC_SZ], sig[SC_SZ:]
secec.sc_bytes(r_bytes, &r)
secec.sc_bytes(s_bytes, &s)
case:
panic("crypto/ecdsa: invalid curve")
}
return true
}
@(private)
sign_internal :: proc(priv_key, sig_r, sig_s: ^$T, hash_algo: hash.Algorithm, msg: []byte, deterministic: bool) {
when T == secec.Scalar_p256r1 {
SC_SZ :: secec.SC_SIZE_P256R1
FE_SZ :: secec.FE_SIZE_P256R1
MIN_DRBG_HASH :: hash.Algorithm.SHA256
r_pt: secec.Point_p256r1 = ---
} else when T == secec.Scalar_p384r1 {
SC_SZ :: secec.SC_SIZE_P384R1
FE_SZ :: secec.FE_SIZE_P384R1
MIN_DRBG_HASH :: hash.Algorithm.SHA384
r_pt: secec.Point_p384r1 = ---
} else {
#panic("crypto/ecdsa: invalid curve")
}
// Note: `e` (derived from `hash`) in steps 4 and 5, is
// unchanged throughout the process even if a different `k`
// needs to be selected, thus, the value is derived first
// before the rejection sampling loop.
// 4. Use the hash function selected during the setup procedure
// to compute the hash value:
// H = Hash(M)
// of length hashlen octets as specified in Section 3.5. If the
// hash function outputs “invalid”, output “invalid” and stop.
// 5. Derive an integer e from H as follows:
// 5.1. Convert the octet string H to a bit string H using the
// conversion routine specified in Section 2.3.2.
// 5.2. Set E = H if ceil(log2(n)) >= 8(hashlen), and set E equal
// to the leftmost ceil(log2(n)) bits of H if ceil(log2(n)) <
// 8(hashlen).
// 5.3. Convert the bit string E to an octet string E using the
// conversion routine specified in Section 2.3.1.
// 5.4. Convert the octet string E to an integer e using the
// conversion routine specified in Section 2.3.8.
e: T = ---
h_bytes: [hash.MAX_DIGEST_SIZE]byte = ---
e_bytes := hash.hash_bytes_to_buffer(hash_algo, msg, h_bytes[:])
if len(e_bytes) > SC_SZ {
e_bytes = e_bytes[:SC_SZ]
}
if did_reduce := secec.sc_set_bytes(&e, e_bytes); did_reduce {
// RFC 6979 wants the reduced value
secec.sc_bytes(e_bytes, &e)
}
x_bytes: [SC_SZ]byte = ---
defer crypto.zero_explicit(&x_bytes, size_of(x_bytes))
secec.sc_bytes(x_bytes[:], priv_key)
// While I normally will be content to let idiots compromise
// their signing keys, past precident (eg: Sony Computer
// Entertainment America, Inc v. Hotz) shows that "idiots"
// are also litigatious asshats. By default we use the hedged
// ("with additional input") variant of RFC 6979, with the option
// to disable the added entropy for RFC 6979 compatible deterministic
// signatures.
//
// For implementation simplicity, we will use a hash function that
// has a digest size that is greater than or equal to that of the
// curve order. Using a separate hash is allowed per section 3.6
// of the RFC, and we only ever use a more secure hash.
rng: Drbg_RFC6979 = ---
drbg_hash := hash_algo
if hash.DIGEST_SIZES[hash_algo] < SC_SZ {
drbg_hash = MIN_DRBG_HASH
}
init_drbg_rfc6979(&rng, drbg_hash, x_bytes[:], e_bytes, deterministic)
k: T = ---
defer secec.sc_clear(&k)
for {
// 1. Select an ephemeral elliptic curve key pair (k, R) with
// R = (xR, yR) associated with the elliptic curve domain parameters
// T established during the setup procedure using the key pair
// generation primitive specified in Section 3.2.1.
if did_reduce := drbg_read_rfc6979(&rng, &k); did_reduce || secec.sc_is_zero(&k) == 1 {
continue
}
secec.pt_scalar_mul_generator(&r_pt, &k)
// 2. Convert the field element xR to an integer xR using the
// conversion routine specified in Section 2.3.9.
rx_bytes: [FE_SZ]byte = ---
_ = secec.pt_bytes(rx_bytes[:], nil, &r_pt)
// 3. Set r = xR mod n. If r = 0, or optionally r fails to meet
// other publicly verifiable criteria (see below), return to Step 1.
_ = secec.sc_set_bytes(sig_r, rx_bytes[:])
if secec.sc_is_zero(sig_r) == 1 {
// This is essentially totally untestable since the odds
// of generating `r = 0` is astronomically unlikely.
continue
}
// (Steps 4/5 done prior to loop.)
// 6. Compute: s = k^1 (e + r * dU) mod n.
// If s = 0, return to Step 1.
secec.sc_inv(&k, &k)
secec.sc_mul(sig_s, sig_r, priv_key)
secec.sc_add(sig_s, sig_s, &e)
secec.sc_mul(sig_s, sig_s, &k)
if secec.sc_is_zero(sig_s) == 0 {
return
}
}
}

View File

@@ -0,0 +1,148 @@
package ecdsa
import "core:crypto/hash"
import secec "core:crypto/_weierstrass"
// verify_raw returns true iff sig is a valid signature by pub_key over
// msg, hased using hash_algo, per the verification procedure specifed
// in SEC 1, Version 2.0, Section 4.1.4.
//
// The signature format is `r | s`.
@(require_results)
verify_raw :: proc(pub_key: ^Public_Key, hash_algo: hash.Algorithm, msg, sig: []byte) -> bool {
ensure(hash_algo != .Invalid, "crypto/edsa: invalid hash algorithm")
ensure(pub_key._curve != .Invalid, "crypto/edsa: invalid curve")
if len(sig) != RAW_SIGNATURE_SIZES[pub_key._curve] {
return false
}
#partial switch pub_key._curve {
case .SECP256R1:
SC_SZ :: secec.SC_SIZE_P256R1
r_bytes, s_bytes := sig[:SC_SZ], sig[SC_SZ:]
pk := &pub_key._impl.(secec.Point_p256r1)
return verify_internal(pk, hash_algo, r_bytes, s_bytes, msg)
case .SECP384R1:
SC_SZ :: secec.SC_SIZE_P384R1
r_bytes, s_bytes := sig[:SC_SZ], sig[SC_SZ:]
pk := &pub_key._impl.(secec.Point_p384r1)
return verify_internal(pk, hash_algo, r_bytes, s_bytes, msg)
}
panic("crypto/ecdsa: invalid curve")
}
// verify_asn1 returns true iff sig is a valid signature by pub_key over
// msg, hased using hash_algo, per the verification procedure specifed
// in SEC 1, Version 2.0, Section 4.1.4.
//
// The signature format is ASN.1 `SEQUENCE { r INTEGER, s INTEGER }`.
@(require_results)
verify_asn1 :: proc(pub_key: ^Public_Key, hash_algo: hash.Algorithm, msg, sig: []byte) -> bool {
ensure(hash_algo != .Invalid, "crypto/edsa: invalid hash algorithm")
ensure(pub_key._curve != .Invalid, "crypto/edsa: invalid curve")
r_bytes, s_bytes, ok := parse_asn1_sig(sig)
if !ok {
return false
}
#partial switch pub_key._curve {
case .SECP256R1:
pk := &pub_key._impl.(secec.Point_p256r1)
return verify_internal(pk, hash_algo, r_bytes, s_bytes, msg)
case .SECP384R1:
pk := &pub_key._impl.(secec.Point_p384r1)
return verify_internal(pk, hash_algo, r_bytes, s_bytes, msg)
}
panic("crypto/ecdsa: invalid curve")
}
@(private,require_results)
verify_internal :: proc(pub_key: ^$T, hash_algo: hash.Algorithm, sig_r, sig_s, msg: []byte) -> bool {
when T == secec.Point_p256r1 {
r, s, e, v: secec.Scalar_p256r1 = ---, ---, ---, ---
u1, u2, s_inv: secec.Scalar_p256r1 = ---, ---, ---
SC_SZ :: secec.SC_SIZE_P256R1
} else when T == secec.Point_p384r1 {
r, s, e, v: secec.Scalar_p384r1 = ---, ---, ---, ---
u1, u2, s_inv: secec.Scalar_p384r1 = ---, ---, ---
SC_SZ :: secec.SC_SIZE_P384R1
} else {
#panic("crypto/ecdsa: invalid curve")
}
if len(sig_r) > SC_SZ || len(sig_s) > SC_SZ {
return false
}
// 1. If r and s are not both integers in the interval [1, n 1],
// output “invalid” and stop.
if did_reduce := secec.sc_set_bytes(&r, sig_r); did_reduce {
return false
}
if did_reduce := secec.sc_set_bytes(&s, sig_s); did_reduce {
return false
}
if secec.sc_is_zero(&r) == 1 || secec.sc_is_zero(&s) == 1 {
return false
}
// 2. Use the hash function established during the setup procedure
// to compute the hash value:
// H = Hash(M)
// of length hashlen octets as specified in Section 3.5. If the
// hash function outputs “invalid”, output “invalid” and stop.
// 3. Derive an integer e from H as follows:
// 3.1. Convert the octet string H to a bit string H using the
// conversion routine specified in Section 2.3.2.
// 3.2. Set E = H if ceil(log2(n)) >= 8(hashlen), and set E equal
// to the leftmost ceil(log2(n)) bits of H if ceil(log2(n)) <
// 8(hashlen).
// 3.3. Convert the bit string E to an octet string E using the
// conversion routine specified in Section 2.3.1.
// 3.4. Convert the octet string E to an integer e using the
// conversion routine specified in Section 2.3.8.
h_bytes: [hash.MAX_DIGEST_SIZE]byte = ---
e_bytes := hash.hash_bytes_to_buffer(hash_algo, msg, h_bytes[:])
if len(e_bytes) > SC_SZ {
e_bytes = e_bytes[:SC_SZ]
}
_ = secec.sc_set_bytes(&e, e_bytes)
// 4. Compute: u1 = e(s^1) mod n and u2 = r(s^-1) mod n.
secec.sc_inv(&s_inv, &s)
secec.sc_mul(&u1, &e, &s_inv)
secec.sc_mul(&u2, &r, &s_inv)
// 5. Compute: R = (xR, yR) = u1 * G + u2 * QU.
r_pt: T = ---
secec.pt_double_scalar_mul_generator_vartime(&r_pt, pub_key, &u1, &u2)
// If R = O, output “invalid” and stop.
if secec.pt_is_identity(&r_pt) == 1 {
return false
}
// 6. Convert the field element xR to an integer xR using the
// conversion routine specified in Section 2.3.9.
//
// 7. Set v = xR mod n.
r_x: [SC_SZ]byte = ---
_ = secec.pt_bytes(r_x[:], nil, &r_pt)
_ = secec.sc_set_bytes(&v, r_x[:])
// 8. Compare v and r — if v = r, output “valid”, and if
// v != r, output “invalid”.
return secec.sc_equal(&r, &v) == 1
}

View File

@@ -47,6 +47,25 @@ Public_Key :: struct {
_is_initialized: bool,
}
// private_key_generate uses the system entropy source to generate a new
// Private_Key. This will only fail iff the system entropy source is
// missing or broken.
private_key_generate :: proc(priv_key: ^Private_Key) -> bool {
private_key_clear(priv_key)
if !crypto.HAS_RAND_BYTES {
return false
}
b: [PRIVATE_KEY_SIZE]byte
defer crypto.zero_explicit(&b, size_of(b))
crypto.rand_bytes(b[:])
private_key_set_bytes(priv_key, b[:])
return true
}
// private_key_set_bytes decodes a byte-encoded private key, and returns
// true iff the operation was successful.
private_key_set_bytes :: proc(priv_key: ^Private_Key, b: []byte) -> bool {

View File

@@ -34,6 +34,7 @@ package all
@(require) import chash "core:crypto/hash"
@(require) import "core:crypto/deoxysii"
@(require) import "core:crypto/ecdh"
@(require) import "core:crypto/ecdsa"
@(require) import "core:crypto/ed25519"
@(require) import "core:crypto/hkdf"
@(require) import "core:crypto/hmac"

View File

@@ -39,6 +39,7 @@ package all
@(require) import chash "core:crypto/hash"
@(require) import "core:crypto/deoxysii"
@(require) import "core:crypto/ecdh"
@(require) import "core:crypto/ecdsa"
@(require) import "core:crypto/ed25519"
@(require) import "core:crypto/hkdf"
@(require) import "core:crypto/hmac"

View File

@@ -9,12 +9,16 @@ import "core:time"
import "core:crypto"
import "core:crypto/ecdh"
import "core:crypto/ecdsa"
import "core:crypto/ed25519"
import "core:crypto/hash"
@(private = "file")
ECDH_ITERS :: 10000
@(private = "file")
DSA_ITERS :: 10000
@(private = "file")
MSG : string : "Got a job for you, 621."
@(test)
benchmark_crypto_ecc :: proc(t: ^testing.T) {
@@ -24,7 +28,7 @@ benchmark_crypto_ecc :: proc(t: ^testing.T) {
bench_dsa()
}
@(private = "file")
@(private="file")
bench_ecdh :: proc() {
if !crypto.HAS_RAND_BYTES {
log.warnf("ECDH benchmarks skipped, no system entropy source")
@@ -78,7 +82,7 @@ bench_ecdh :: proc() {
log_table(&tbl)
}
@(private = "file")
@(private="file")
bench_dsa :: proc() {
tbl: table.Table
table.init(&tbl)
@@ -102,6 +106,20 @@ bench_dsa :: proc() {
append_tbl(&tbl, "ed25519", "sign", sig)
append_tbl(&tbl, "ed25519", "verify", verif)
table.row(&tbl)
sk, sig, verif = bench_ecdsa(.SECP256R1, .SHA256)
append_tbl(&tbl, "secp256r1/SHA256/RFC6979", "private_key_set_bytes", sk)
append_tbl(&tbl, "secp256r1/SHA256/RFC6979", "sign", sig)
append_tbl(&tbl, "secp256r1/SHA256/RFC6979", "verify", verif)
table.row(&tbl)
sk, sig, verif = bench_ecdsa(.SECP384R1, .SHA384)
append_tbl(&tbl, "secp384r1/SHA384/RFC6979", "private_key_set_bytes", sk)
append_tbl(&tbl, "secp384r1/SHA384/RFC6979", "sign", sig)
append_tbl(&tbl, "secp384r1/SHA384/RFC6979", "verify", verif)
log_table(&tbl)
}
@@ -122,9 +140,8 @@ bench_ed25519 :: proc() -> (sk, sig, verif: time.Duration) {
ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes[:])
assert(ok, "public key should deserialize")
msg := "Got a job for you, 621."
sig_bytes: [ed25519.SIGNATURE_SIZE]byte
msg_bytes := transmute([]byte)(msg)
msg_bytes := transmute([]byte)(MSG)
start = time.tick_now()
for _ in 0 ..< DSA_ITERS {
ed25519.sign(&priv_key, msg_bytes, sig_bytes[:])
@@ -140,3 +157,37 @@ bench_ed25519 :: proc() -> (sk, sig, verif: time.Duration) {
return
}
@(private="file")
bench_ecdsa :: proc(curve: ecdsa.Curve, hash: hash.Algorithm) -> (sk, sig, verif: time.Duration) {
priv_bytes := make([]byte, ecdsa.PRIVATE_KEY_SIZES[curve], context.temp_allocator)
crypto.set(raw_data(priv_bytes), 0x69, len(priv_bytes))
priv_key: ecdsa.Private_Key
start := time.tick_now()
for _ in 0 ..< DSA_ITERS {
ok := ecdsa.private_key_set_bytes(&priv_key, curve, priv_bytes)
assert(ok, "private key should deserialize")
}
sk = time.tick_since(start) / DSA_ITERS
pub_key: ecdsa.Public_Key
ecdsa.public_key_set_priv(&pub_key, &priv_key)
sig_bytes := make([]byte, ecdsa.RAW_SIGNATURE_SIZES[curve], context.temp_allocator)
msg_bytes := transmute([]byte)(MSG)
start = time.tick_now()
for _ in 0 ..< DSA_ITERS {
ok := ecdsa.sign_raw(&priv_key, hash, msg_bytes, sig_bytes, true)
assert(ok, "signing should succeed")
}
sig = time.tick_since(start) / DSA_ITERS
start = time.tick_now()
for _ in 0 ..< DSA_ITERS {
ok := ecdsa.verify_raw(&pub_key, hash, msg_bytes, sig_bytes)
assert(ok, "signature should validate")
}
verif = time.tick_since(start) / DSA_ITERS
return
}

View File

@@ -4,7 +4,6 @@ import "core:encoding/hex"
import "core:testing"
import field "core:crypto/_fiat/field_curve25519"
import "core:crypto/ed25519"
import "core:crypto/ristretto255"
@(test)
@@ -318,311 +317,6 @@ test_ristretto255 :: proc(t: ^testing.T) {
}
}
@(test)
test_ed25519 :: proc(t: ^testing.T) {
test_vectors_rfc := []struct {
priv_key: string,
pub_key: string,
msg: string,
sig: string,
} {
// Test vectors from RFC 8032
{
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
"",
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
},
{
"4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
"3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
"72",
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
},
{
"c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
"fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
"af82",
"6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
},
// TEST 1024 omitted for brevity, because all that does is add more to SHA-512
{
"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
"dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704",
},
}
for v, _ in test_vectors_rfc {
priv_bytes, _ := hex.decode(transmute([]byte)(v.priv_key), context.temp_allocator)
pub_bytes, _ := hex.decode(transmute([]byte)(v.pub_key), context.temp_allocator)
msg_bytes, _ := hex.decode(transmute([]byte)(v.msg), context.temp_allocator)
sig_bytes, _ := hex.decode(transmute([]byte)(v.sig), context.temp_allocator)
priv_key: ed25519.Private_Key
ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes)
testing.expectf(
t,
ok,
"Expected %s to be a valid private key",
v.priv_key,
)
key_bytes: [32]byte
ed25519.private_key_bytes(&priv_key, key_bytes[:])
testing.expectf(
t,
ok,
"Expected private key %s round-trip, got %s",
v.priv_key,
string(hex.encode(key_bytes[:], context.temp_allocator)),
)
pub_key: ed25519.Public_Key
ok = ed25519.public_key_set_bytes(&pub_key, pub_bytes)
testing.expectf(
t,
ok,
"Expected %s to be a valid public key (priv->pub: %s)",
v.pub_key,
string(hex.encode(priv_key._pub_key._b[:], context.temp_allocator)),
)
ed25519.public_key_bytes(&pub_key, key_bytes[:])
testing.expectf(
t,
ok,
"Expected public key %s round-trip, got %s",
v.pub_key,
string(hex.encode(key_bytes[:], context.temp_allocator)),
)
sig: [ed25519.SIGNATURE_SIZE]byte
ed25519.sign(&priv_key, msg_bytes, sig[:])
x := string(hex.encode(sig[:], context.temp_allocator))
testing.expectf(
t,
x == v.sig,
"Expected %s for sign(%s, %s), got %s",
v.sig,
v.priv_key,
v.msg,
x,
)
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
testing.expectf(
t,
ok,
"Expected true for verify(%s, %s, %s)",
v.pub_key,
v.msg,
v.sig,
)
ok = ed25519.verify(&priv_key._pub_key, msg_bytes, sig_bytes)
testing.expectf(
t,
ok,
"Expected true for verify(pub(%s), %s %s)",
v.priv_key,
v.msg,
v.sig,
)
// Corrupt the message and make sure verification fails.
switch len(msg_bytes) {
case 0:
tmp_msg := []byte{69}
msg_bytes = tmp_msg[:]
case:
msg_bytes[0] = msg_bytes[0] ~ 69
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
testing.expectf(
t,
ok == false,
"Expected false for verify(%s, %s (corrupted), %s)",
v.pub_key,
v.msg,
v.sig,
)
}
// Test cases from "Taming the many EdDSAs", which aim to exercise
// all of the ed25519 edge cases/implementation differences.
//
// - https://eprint.iacr.org/2020/1244
// - https://github.com/novifinancial/ed25519-speccheck
test_vectors_speccheck := []struct {
pub_key: string,
msg: string,
sig: string,
pub_key_ok: bool,
sig_ok: bool,
sig_ok_relaxed: bool, // Ok if the small-order A check is relaxed.
} {
// S = 0, small-order A, small-order R
{
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
true,
false,
true,
},
// 0 < S < L, small-order A, mixed-order R
{
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
true,
false,
true,
},
// 0 < S < L, mixed-order A, small-order R
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e",
true,
true,
true,
},
// 0 < S < L, mixed-order A, mixed-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
"9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009",
true,
true,
true,
},
// 0 < S < L, mixed-order A, mixed-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
"160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09",
true,
true, // cofactored-only
true,
},
// 0 < S < L, mixed-order A, L-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
"21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405",
true,
true, // cofactored only, (fail if 8h is pre-reduced)
true,
},
// S > L, L-order A, L-order R
{
"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
"e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514",
true,
false,
false,
},
// S >> L, L-order A, L-order R
{
"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
"8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22",
true,
false,
false,
},
// 0 < S < L, mixed-order A, small-order R (non-canonical R, reduced for hash)
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
true,
false,
false,
},
// 0 < S < L, mixed-order A, small-order R (non-canonical R, not reduced for hash)
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",
true,
false,
false,
},
// 0 < S < L, small-order A, mixed-order R (non-canonical A, reduced for hash)
{
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",
"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
false,
false,
false,
},
// 0 < S < L, small-order A, mixed-order R (non-canonical A, not reduced for hash)
{
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f",
"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
false,
false,
false,
},
}
for v, i in test_vectors_speccheck {
pub_bytes, _ := hex.decode(transmute([]byte)(v.pub_key), context.temp_allocator)
msg_bytes, _ := hex.decode(transmute([]byte)(v.msg), context.temp_allocator)
sig_bytes, _ := hex.decode(transmute([]byte)(v.sig), context.temp_allocator)
pub_key: ed25519.Public_Key
ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes)
testing.expectf(
t,
ok == v.pub_key_ok,
"speccheck/%d: Expected %s to be a (in)valid public key, got %v",
i,
v.pub_key,
ok,
)
// If A is rejected for being non-canonical, skip signature check.
if !v.pub_key_ok {
continue
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
testing.expectf(
t,
ok == v.sig_ok,
"speccheck/%d Expected %v for verify(%s, %s, %s)",
i,
v.sig_ok,
v.pub_key,
v.msg,
v.sig,
)
// If the signature is accepted, skip the relaxed signature check.
if v.sig_ok {
continue
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes, true)
testing.expectf(
t,
ok == v.sig_ok_relaxed,
"speccheck/%d Expected %v for verify(%s, %s, %s, true)",
i,
v.sig_ok_relaxed,
v.pub_key,
v.msg,
v.sig,
)
}
}
@(private="file")
ge_str :: proc(ge: ^ristretto255.Group_Element) -> string {
b: [ristretto255.ELEMENT_SIZE]byte
@@ -635,4 +329,4 @@ fe_str :: proc(fe: ^field.Tight_Field_Element) -> string {
b: [32]byte
field.fe_to_bytes(&b, fe)
return string(hex.encode(b[:], context.temp_allocator))
}
}

View File

@@ -0,0 +1,568 @@
package test_core_crypto
import "core:bytes"
import "core:encoding/hex"
import "core:testing"
import "core:crypto"
import "core:crypto/ecdsa"
import "core:crypto/ed25519"
import "core:crypto/hash"
@(test)
test_ecdsa :: proc(t: ^testing.T) {
test_vectors_deterministic := []struct{
curve: ecdsa.Curve,
hash: hash.Algorithm,
priv_key: string,
pub_key: string,
msg: string,
sig_raw: string,
} {
// Test vectors from RFC 6979
{
.SECP256R1,
.SHA256,
"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
"0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
"sample",
"efd48b2aacb6a8fd1140dd9cd45e81d69d2c877b56aaf991c34d0ea84eaf3716f7cb1c942d657c41d436c7a1b6e29f65f3e900dbb9aff4064dc4ab2f843acda8",
},
{
.SECP256R1,
.SHA384,
"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
"0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
"sample",
"0eafea039b20e9b42309fb1d89e213057cbf973dc0cfc8f129edddc800ef77194861f0491e6998b9455193e34e7b0d284ddd7149a74b95b9261f13abde940954",
},
{
.SECP256R1,
.SHA512,
"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
"0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
"sample",
"8496a60b5e9b47c825488827e0495b0e3fa109ec4568fd3f8d1097678eb97f002362ab1adbe2b8adf9cb9edab740ea6049c028114f2460f96554f61fae3302fe",
},
{
.SECP256R1,
.SHA256,
"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
"0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
"test",
"f1abb023518351cd71d881567b1ea663ed3efcf6c5132b354f28d3b0b7d38367019f4113742a2b14bd25926b49c649155f267e60d3814b4c0cc84250e46f0083",
},
{
.SECP256R1,
.SHA384,
"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
"0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
"test",
"83910e8b48bb0c74244ebdf7f07a1c5413d61472bd941ef3920e623fbccebeb68ddbec54cf8cd5874883841d712142a56a8d0f218f5003cb0296b6b509619f2c",
},
{
.SECP256R1,
.SHA512,
"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
"0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
"test",
"461d93f31b6540894788fd206c07cfa0cc35f46fa3c91816fff1040ad1581a0439af9f15de0db8d97e72719c74820d304ce5226e32dedae67519e840d1194e55",
},
{
.SECP384R1,
.SHA384,
"6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d896d5724e4c70a825f872c9ea60d2edf5",
"04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
"sample",
"94edbb92a5ecb8aad4736e56c691916b3f88140666ce9fa73d64c4ea95ad133c81a648152e44acf96e36dd1e80fabe4699ef4aeb15f178cea1fe40db2603138f130e740a19624526203b6351d0a3a94fa329c145786e679e7b82c71a38628ac8",
},
{
.SECP384R1,
.SHA512,
"6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d896d5724e4c70a825f872c9ea60d2edf5",
"04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
"sample",
"ed0959d5880ab2d869ae7f6c2915c6d60f96507f9cb3e047c0046861da4a799cfe30f35cc900056d7c99cd7882433709512c8cceee3890a84058ce1e22dbc2198f42323ce8aca9135329f03c068e5112dc7cc3ef3446defceb01a45c2667fdd5",
},
{
.SECP384R1,
.SHA384,
"6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d896d5724e4c70a825f872c9ea60d2edf5",
"04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
"test",
"8203b63d3c853e8d77227fb377bcf7b7b772e97892a80f36ab775d509d7a5feb0542a7f0812998da8f1dd3ca3cf023dbddd0760448d42d8a43af45af836fce4de8be06b485e9b61b827c2f13173923e06a739f040649a667bf3b828246baa5a5",
},
{
.SECP384R1,
.SHA512,
"6b9d3dad2e1b8c1c05b19875b6659f4de23c3b667bf297ba9aa47740787137d896d5724e4c70a825f872c9ea60d2edf5",
"04ec3a4e415b4e19a4568618029f427fa5da9a8bc4ae92e02e06aae5286b300c64def8f0ea9055866064a254515480bc138015d9b72d7d57244ea8ef9ac0c621896708a59367f9dfb9f54ca84b3f1c9db1288b231c3ae0d4fe7344fd2533264720",
"test",
"a0d5d090c9980faf3c2ce57b7ae951d31977dd11c775d314af55f76c676447d06fb6495cd21b4b6e340fc236584fb277976984e59b4c77b0e8e4460dca3d9f20e07b9bb1f63beefaf576f6b2e8b224634a2092cd3792e0159ad9cee37659c736",
},
// Special case that exercises the rejection sampling.
// https://github.com/C2SP/CCTV/tree/main/RFC6979
{
.SECP256R1,
.SHA256,
"c9afa9d845ba75166b5c215767b1d6934e50c3db36e89b127b8a622b120f6721",
"0460fed4ba255a9d31c961eb74c6356d68c049b8923b61fa6ce669622e60f29fb67903fe1008b8bc99a41ae9e95628bc64f2f1b20c2d7e9f5177a3c294d4462299",
"wv[vnX",
"efd9073b652e76da1b5a019c0e4a2e3fa529b035a6abb91ef67f0ed7a1f212343db4706c9d9f4a4fe13bb5e08ef0fab53a57dbab2061c83a35fa411c68d2ba33",
},
}
for v, _ in test_vectors_deterministic {
priv_bytes, _ := hex.decode(transmute([]byte)(v.priv_key), context.temp_allocator)
pub_bytes, _ := hex.decode(transmute([]byte)(v.pub_key), context.temp_allocator)
msg_bytes := bytes.clone(transmute([]byte)(v.msg), context.temp_allocator)
sig_bytes, _ := hex.decode(transmute([]byte)(v.sig_raw), context.temp_allocator)
priv_key: ecdsa.Private_Key
ok := ecdsa.private_key_set_bytes(&priv_key, v.curve, priv_bytes)
testing.expectf(
t,
ok,
"Expected %s to be a valid %v private key",
v.priv_key,
v.curve,
)
priv_key_bytes := make([]byte, ecdsa.PRIVATE_KEY_SIZES[v.curve], context.temp_allocator)
ecdsa.private_key_bytes(&priv_key, priv_key_bytes)
priv_s := string(hex.encode(priv_key_bytes, context.temp_allocator))
testing.expectf(
t,
priv_s == v.priv_key,
"Expected private key %s round-trip, got %s",
v.priv_key,
priv_s,
)
pub_key: ecdsa.Public_Key
ok = ecdsa.public_key_set_bytes(&pub_key, v.curve, pub_bytes)
testing.expectf(
t,
ok,
"Expected %s to be a valid %s public key",
v.pub_key,
v.curve,
)
pub_key_bytes := make([]byte, ecdsa.PUBLIC_KEY_SIZES[v.curve], context.temp_allocator)
ecdsa.public_key_bytes(&pub_key, pub_key_bytes)
pub_s := string(hex.encode(pub_key_bytes, context.temp_allocator))
testing.expectf(
t,
pub_s == v.pub_key,
"Expected public key %s round-trip, got %s",
v.pub_key,
pub_s,
)
priv_pub_key: ecdsa.Public_Key
ecdsa.public_key_set_priv(&priv_pub_key, &priv_key)
ok = ecdsa.public_key_equal(&pub_key, &priv_pub_key)
testing.expectf(
t,
ok,
"Expected %v to be %s's public key",
&priv_pub_key,
v.priv_key,
)
ok = ecdsa.verify_raw(&pub_key, v.hash, msg_bytes, sig_bytes)
testing.expectf(
t,
ok,
"Expected true for verify(%s, %v, %s, %s)",
v.pub_key,
v.hash,
v.msg,
v.sig_raw,
)
// Signatures are deterministic for these test cases.
sig := make([]byte, ecdsa.RAW_SIGNATURE_SIZES[v.curve], context.temp_allocator)
ok = ecdsa.sign_raw(&priv_key, v.hash, msg_bytes, sig, true)
x := string(hex.encode(sig[:], context.temp_allocator))
testing.expectf(
t,
ok && x == v.sig_raw,
"Expected %s for sign(%s, %v, %s), got %s",
v.sig_raw,
v.priv_key,
v.hash,
v.msg,
x,
)
// But when possible, we also add entropy by default.
when crypto.HAS_RAND_BYTES {
ok = ecdsa.sign_raw(&priv_key, v.hash, msg_bytes, sig)
x = string(hex.encode(sig[:], context.temp_allocator))
testing.expectf(
t,
ok && x != v.sig_raw,
"Expected not %s for sign(%s, %v, %s), got %s",
v.sig_raw,
v.priv_key,
v.hash,
v.msg,
x,
)
}
// Corrupt the message and make sure verification fails.
msg_bytes[0] ~= 0x69
ok = ecdsa.verify_raw(&pub_key, v.hash, msg_bytes, sig_bytes)
testing.expectf(
t,
ok == false,
"Expected false for verify(%s, %v %s (corrupted), %s)",
v.pub_key,
v.hash,
v.msg,
v.sig_raw,
)
}
// ASN.1 tests requires entorpy.
when crypto.HAS_RAND_BYTES == false {
return
}
msg_str : string : "Feed the Fire. Let the Last Cinders Burn."
for _ in 0 ..< 1000 {
msg := transmute([]byte)(msg_str)
priv_key: ecdsa.Private_Key
ok := ecdsa.private_key_generate(&priv_key, .SECP256R1)
testing.expectf(t, ok, "Failed to generate private key")
sig: []byte
sig, ok = ecdsa.sign_asn1(&priv_key, .SHA256, msg, context.temp_allocator)
testing.expectf(
t,
ok && len(sig) > 0,
"Failed for sign(%v, secp256r1/SHA256, %s)",
priv_key,
msg,
)
pub_key := &priv_key._pub_key
ok = ecdsa.verify_asn1(pub_key, .SHA256, msg, sig)
testing.expectf(
t,
ok,
"Expected true for verify(%v, SHA256, %s, %x)",
pub_key,
msg,
sig,
)
}
}
@(test)
test_ed25519 :: proc(t: ^testing.T) {
test_vectors_rfc := []struct {
priv_key: string,
pub_key: string,
msg: string,
sig: string,
} {
// Test vectors from RFC 8032
{
"9d61b19deffd5a60ba844af492ec2cc44449c5697b326919703bac031cae7f60",
"d75a980182b10ab7d54bfed3c964073a0ee172f3daa62325af021a68f707511a",
"",
"e5564300c360ac729086e2cc806e828a84877f1eb8e5d974d873e065224901555fb8821590a33bacc61e39701cf9b46bd25bf5f0595bbe24655141438e7a100b",
},
{
"4ccd089b28ff96da9db6c346ec114e0f5b8a319f35aba624da8cf6ed4fb8a6fb",
"3d4017c3e843895a92b70aa74d1b7ebc9c982ccf2ec4968cc0cd55f12af4660c",
"72",
"92a009a9f0d4cab8720e820b5f642540a2b27b5416503f8fb3762223ebdb69da085ac1e43e15996e458f3613d0f11d8c387b2eaeb4302aeeb00d291612bb0c00",
},
{
"c5aa8df43f9f837bedb7442f31dcb7b166d38535076f094b85ce3a2e0b4458f7",
"fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025",
"af82",
"6291d657deec24024827e69c3abe01a30ce548a284743a445e3680d7db5ac3ac18ff9b538d16f290ae67f760984dc6594a7c15e9716ed28dc027beceea1ec40a",
},
// TEST 1024 omitted for brevity, because all that does is add more to SHA-512
{
"833fe62409237b9d62ec77587520911e9a759cec1d19755b7da901b96dca3d42",
"ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf",
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f",
"dc2a4459e7369633a52b1bf277839a00201009a3efbf3ecb69bea2186c26b58909351fc9ac90b3ecfdfbc7c66431e0303dca179c138ac17ad9bef1177331a704",
},
}
for v, _ in test_vectors_rfc {
priv_bytes, _ := hex.decode(transmute([]byte)(v.priv_key), context.temp_allocator)
pub_bytes, _ := hex.decode(transmute([]byte)(v.pub_key), context.temp_allocator)
msg_bytes, _ := hex.decode(transmute([]byte)(v.msg), context.temp_allocator)
sig_bytes, _ := hex.decode(transmute([]byte)(v.sig), context.temp_allocator)
priv_key: ed25519.Private_Key
ok := ed25519.private_key_set_bytes(&priv_key, priv_bytes)
testing.expectf(
t,
ok,
"Expected %s to be a valid private key",
v.priv_key,
)
key_bytes: [32]byte
ed25519.private_key_bytes(&priv_key, key_bytes[:])
priv_s := string(hex.encode(key_bytes[:], context.temp_allocator))
testing.expectf(
t,
priv_s == v.priv_key,
"Expected private key %s round-trip, got %s",
v.priv_key,
priv_s,
)
pub_key: ed25519.Public_Key
ok = ed25519.public_key_set_bytes(&pub_key, pub_bytes)
testing.expectf(
t,
ok,
"Expected %s to be a valid public key (priv->pub: %s)",
v.pub_key,
)
ed25519.public_key_bytes(&pub_key, key_bytes[:])
pub_s := string(hex.encode(priv_key._pub_key._b[:], context.temp_allocator))
testing.expectf(
t,
pub_s == v.pub_key,
"Expected public key %s round-trip, got %s",
v.pub_key,
pub_s,
)
sig: [ed25519.SIGNATURE_SIZE]byte
ed25519.sign(&priv_key, msg_bytes, sig[:])
x := string(hex.encode(sig[:], context.temp_allocator))
testing.expectf(
t,
x == v.sig,
"Expected %s for sign(%s, %s), got %s",
v.sig,
v.priv_key,
v.msg,
x,
)
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
testing.expectf(
t,
ok,
"Expected true for verify(%s, %s, %s)",
v.pub_key,
v.msg,
v.sig,
)
ok = ed25519.verify(&priv_key._pub_key, msg_bytes, sig_bytes)
testing.expectf(
t,
ok,
"Expected true for verify(pub(%s), %s %s)",
v.priv_key,
v.msg,
v.sig,
)
// Corrupt the message and make sure verification fails.
switch len(msg_bytes) {
case 0:
tmp_msg := []byte{69}
msg_bytes = tmp_msg[:]
case:
msg_bytes[0] = msg_bytes[0] ~ 69
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
testing.expectf(
t,
ok == false,
"Expected false for verify(%s, %s (corrupted), %s)",
v.pub_key,
v.msg,
v.sig,
)
}
// Test cases from "Taming the many EdDSAs", which aim to exercise
// all of the ed25519 edge cases/implementation differences.
//
// - https://eprint.iacr.org/2020/1244
// - https://github.com/novifinancial/ed25519-speccheck
test_vectors_speccheck := []struct {
pub_key: string,
msg: string,
sig: string,
pub_key_ok: bool,
sig_ok: bool,
sig_ok_relaxed: bool, // Ok if the small-order A check is relaxed.
} {
// S = 0, small-order A, small-order R
{
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"8c93255d71dcab10e8f379c26200f3c7bd5f09d9bc3068d3ef4edeb4853022b6",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a0000000000000000000000000000000000000000000000000000000000000000",
true,
false,
true,
},
// 0 < S < L, small-order A, mixed-order R
{
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43a5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
true,
false,
true,
},
// 0 < S < L, mixed-order A, small-order R
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"aebf3f2601a0c8c5d39cc7d8911642f740b78168218da8471772b35f9d35b9ab",
"c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa8c4bd45aecaca5b24fb97bc10ac27ac8751a7dfe1baff8b953ec9f5833ca260e",
true,
true,
true,
},
// 0 < S < L, mixed-order A, mixed-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"9bd9f44f4dcc75bd531b56b2cd280b0bb38fc1cd6d1230e14861d861de092e79",
"9046a64750444938de19f227bb80485e92b83fdb4b6506c160484c016cc1852f87909e14428a7a1d62e9f22f3d3ad7802db02eb2e688b6c52fcd6648a98bd009",
true,
true,
true,
},
// 0 < S < L, mixed-order A, mixed-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
"160a1cb0dc9c0258cd0a7d23e94d8fa878bcb1925f2c64246b2dee1796bed5125ec6bc982a269b723e0668e540911a9a6a58921d6925e434ab10aa7940551a09",
true,
true, // cofactored-only
true,
},
// 0 < S < L, mixed-order A, L-order R
{
"cdb267ce40c5cd45306fa5d2f29731459387dbf9eb933b7bd5aed9a765b88d4d",
"e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec4011eaccd55b53f56c",
"21122a84e0b5fca4052f5b1235c80a537878b38f3142356b2c2384ebad4668b7e40bc836dac0f71076f9abe3a53f9c03c1ceeeddb658d0030494ace586687405",
true,
true, // cofactored only, (fail if 8h is pre-reduced)
true,
},
// S > L, L-order A, L-order R
{
"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
"e96f66be976d82e60150baecff9906684aebb1ef181f67a7189ac78ea23b6c0e547f7690a0e2ddcd04d87dbc3490dc19b3b3052f7ff0538cb68afb369ba3a514",
true,
false,
false,
},
// S >> L, L-order A, L-order R
{
"442aad9f089ad9e14647b1ef9099a1ff4798d78589e66f28eca69c11f582a623",
"85e241a07d148b41e47d62c63f830dc7a6851a0b1f33ae4bb2f507fb6cffec40",
"8ce5b96c8f26d0ab6c47958c9e68b937104cd36e13c33566acd2fe8d38aa19427e71f98a473474f2f13f06f97c20d58cc3f54b8bd0d272f42b695dd7e89a8c22",
true,
false,
false,
},
// 0 < S < L, mixed-order A, small-order R (non-canonical R, reduced for hash)
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff03be9678ac102edcd92b0210bb34d7428d12ffc5df5f37e359941266a4e35f0f",
true,
false,
false,
},
// 0 < S < L, mixed-order A, small-order R (non-canonical R, not reduced for hash)
{
"f7badec5b8abeaf699583992219b7b223f1df3fbbea919844e3f7c554a43dd43",
"9bedc267423725d473888631ebf45988bad3db83851ee85c85e241a07d148b41",
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffca8c5b64cd208982aa38d4936621a4775aa233aa0505711d8fdcfdaa943d4908",
true,
false,
false,
},
// 0 < S < L, small-order A, mixed-order R (non-canonical A, reduced for hash)
{
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"e96b7021eb39c1a163b6da4e3093dcd3f21387da4cc4572be588fafae23c155b",
"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
false,
false,
false,
},
// 0 < S < L, small-order A, mixed-order R (non-canonical A, not reduced for hash)
{
"ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
"39a591f5321bbe07fd5a23dc2f39d025d74526615746727ceefd6e82ae65c06f",
"a9d55260f765261eb9b84e106f665e00b867287a761990d7135963ee0a7d59dca5bb704786be79fc476f91d3f3f89b03984d8068dcf1bb7dfc6637b45450ac04",
false,
false,
false,
},
}
for v, i in test_vectors_speccheck {
pub_bytes, _ := hex.decode(transmute([]byte)(v.pub_key), context.temp_allocator)
msg_bytes, _ := hex.decode(transmute([]byte)(v.msg), context.temp_allocator)
sig_bytes, _ := hex.decode(transmute([]byte)(v.sig), context.temp_allocator)
pub_key: ed25519.Public_Key
ok := ed25519.public_key_set_bytes(&pub_key, pub_bytes)
testing.expectf(
t,
ok == v.pub_key_ok,
"speccheck/%d: Expected %s to be a (in)valid public key, got %v",
i,
v.pub_key,
ok,
)
// If A is rejected for being non-canonical, skip signature check.
if !v.pub_key_ok {
continue
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes)
testing.expectf(
t,
ok == v.sig_ok,
"speccheck/%d Expected %v for verify(%s, %s, %s)",
i,
v.sig_ok,
v.pub_key,
v.msg,
v.sig,
)
// If the signature is accepted, skip the relaxed signature check.
if v.sig_ok {
continue
}
ok = ed25519.verify(&pub_key, msg_bytes, sig_bytes, true)
testing.expectf(
t,
ok == v.sig_ok_relaxed,
"speccheck/%d Expected %v for verify(%s, %s, %s, true)",
i,
v.sig_ok_relaxed,
v.pub_key,
v.msg,
v.sig,
)
}
}

View File

@@ -1,5 +1,6 @@
package test_core_crypto
import "core:crypto"
import ec "core:crypto/_weierstrass"
import "core:encoding/hex"
import "core:math/big"
@@ -881,7 +882,7 @@ test_p384_scalar_mul :: proc(t: ^testing.T) {
}
@(test)
test_p256_s11n_sec_identity ::proc(t: ^testing.T) {
test_p256_s11n_sec_identity :: proc(t: ^testing.T) {
p: ec.Point_p256r1
ec.pt_generator(&p)
@@ -901,7 +902,7 @@ test_p256_s11n_sec_identity ::proc(t: ^testing.T) {
}
@(test)
test_p256_s11n_sec_generator ::proc(t: ^testing.T) {
test_p256_s11n_sec_generator :: proc(t: ^testing.T) {
p, g: ec.Point_p256r1
ec.pt_generator(&g)
@@ -917,3 +918,33 @@ test_p256_s11n_sec_generator ::proc(t: ^testing.T) {
testing.expectf(t, ok, "%s", s)
testing.expect(t, ec.pt_equal(&g, &p) == 1)
}
@(test)
test_p256_sc_inv :: proc(t: ^testing.T) {
if crypto.HAS_RAND_BYTES == false {
return
}
sc, sc_inv, sc_prod, sc_one: ec.Scalar_p256r1
ec.sc_set_random(&sc)
ec.sc_inv(&sc_inv, &sc)
ec.sc_one(&sc_one)
ec.sc_mul(&sc_prod, &sc, &sc_inv)
testing.expect(t, ec.sc_equal(&sc_prod, &sc_one) == 1)
}
@(test)
test_p384_sc_inv :: proc(t: ^testing.T) {
if crypto.HAS_RAND_BYTES == false {
return
}
sc, sc_inv, sc_prod, sc_one: ec.Scalar_p384r1
ec.sc_set_random(&sc)
ec.sc_inv(&sc_inv, &sc)
ec.sc_one(&sc_one)
ec.sc_mul(&sc_prod, &sc, &sc_inv)
testing.expect(t, ec.sc_equal(&sc_prod, &sc_one) == 1)
}

View File

@@ -15,7 +15,9 @@ import "core:crypto/aes"
import "core:crypto/chacha20"
import "core:crypto/chacha20poly1305"
import "core:crypto/ecdh"
import "core:crypto/ecdsa"
import "core:crypto/ed25519"
import "core:crypto/hash"
import "core:crypto/hkdf"
import "core:crypto/hmac"
import "core:crypto/kmac"
@@ -72,6 +74,9 @@ import "core:crypto/deoxysii"
// - crypto/_weierstrass
// - ecdh_secp256r1_ecpoint_test.json
// - ecdh_secp384r1_ecpoint_test.json
// - ecdsa_secp256r1_sha256_test.json
// - ecdsa_secp256r1_sha512_test.json
// - ecdsa_secp384r1_sha384_test.json
//
// Not covered (not in wycheproof):
// - crypto/blake2b
@@ -653,12 +658,12 @@ test_eddsa_ed25519 :: proc(t: ^testing.T) {
verify_ok := ed25519.verify(&pk, msg, sig)
result_ok := result_check(test_vector.result, verify_ok)
testing.expectf(
t,
result_ok,
"eddsa/ed25519/%d: verify failed: expected %s actual %v",
test_vector.tc_id,
test_vector.result,
verify_ok,
t,
result_ok,
"eddsa/ed25519/%d: verify failed: expected %s actual %v",
test_vector.tc_id,
test_vector.result,
verify_ok,
)
if !result_ok {
num_failed += 1
@@ -680,6 +685,134 @@ test_eddsa_ed25519 :: proc(t: ^testing.T) {
)
}
@(test)
test_ecdsa :: proc(t: ^testing.T) {
arena: mem.Arena
arena_backing := make([]byte, ARENA_SIZE)
defer delete(arena_backing)
mem.arena_init(&arena, arena_backing)
context.allocator = mem.arena_allocator(&arena)
log.debug("ecdsa: starting")
files := []string {
"ecdsa_secp256r1_sha256_test.json",
"ecdsa_secp256r1_sha512_test.json",
"ecdsa_secp384r1_sha384_test.json",
}
for f in files {
mem.free_all() // Probably don't need this, but be safe.
fn, _ := os.join_path([]string{BASE_PATH, f}, context.allocator)
test_vectors: Test_Vectors(Ecdsa_Test_Group)
load_ok := load(&test_vectors, fn)
testing.expectf(t, load_ok, "Unable to load {}", f)
if !load_ok {
continue
}
testing.expectf(t, test_ecdsa_impl(t, &test_vectors), "ecdsa failed")
}
}
test_ecdsa_impl :: proc(t: ^testing.T, test_vectors: ^Test_Vectors(Ecdsa_Test_Group)) -> bool {
curve_str := test_vectors.test_groups[0].public_key.curve
hash_str := test_vectors.test_groups[0].sha
curve_alg: ecdsa.Curve
switch curve_str {
case "secp256r1":
curve_alg = .SECP256R1
case "secp384r1":
curve_alg = .SECP384R1
case:
log.errorf("ecdsa: unsupported curve: %s", curve_str)
}
hash_alg: hash.Algorithm
switch hash_str {
case "SHA-256":
hash_alg = .SHA256
case "SHA-384":
hash_alg = .SHA384
case "SHA-512":
hash_alg = .SHA512
case:
log.errorf("ecdsa: unsupported hash: %s", hash_str)
}
log.debugf("ecdsa/%s/%s: starting", curve_str, hash_str)
num_ran, num_passed, num_failed, num_skipped: int
for &test_group, i in test_vectors.test_groups {
pk_bytes := hexbytes_decode(test_group.public_key.uncompressed)
pk: ecdsa.Public_Key
pk_ok := ecdsa.public_key_set_bytes(&pk, curve_alg, pk_bytes)
testing.expectf(t, pk_ok, "ecdsa/%s/%s/%d: invalid public key: %s", curve_str, hash_str, i, test_group.public_key.uncompressed)
if !pk_ok {
num_failed += len(test_group.tests)
continue
}
for &test_vector in test_group.tests {
num_ran += 1
if comment := test_vector.comment; comment != "" {
log.debugf(
"ecda/%s/%s/%d: %s: %+v",
curve_str,
hash_str,
test_vector.tc_id,
comment,
test_vector.flags,
)
} else {
log.debugf("ecdsa/%s/%s/%d: %+v", curve_str, hash_str, test_vector.tc_id, test_vector.flags)
}
msg := hexbytes_decode(test_vector.msg)
sig := hexbytes_decode(test_vector.sig)
verify_ok := ecdsa.verify_asn1(&pk, hash_alg, msg, sig)
result_ok := result_check(test_vector.result, verify_ok)
testing.expectf(
t,
result_ok,
"ecdsa/%s/%s/%d: verify failed: expected %s actual %v",
curve_str,
hash_str,
test_vector.tc_id,
test_vector.result,
verify_ok,
)
if !result_ok {
num_failed += 1
continue
}
num_passed += 1
}
}
assert(num_ran == test_vectors.number_of_tests)
assert(num_passed + num_failed + num_skipped == num_ran)
log.infof(
"ecdsa/%s/%s: ran %d, passed %d, failed %d, skipped %d",
curve_str,
hash_str,
num_ran,
num_passed,
num_failed,
num_skipped,
)
return num_failed == 0
}
@(test)
test_hkdf :: proc(t: ^testing.T) {
arena: mem.Arena

View File

@@ -173,6 +173,24 @@ Eddsa_Jwk :: struct {
x: string `json:"x"`,
}
Ecdsa_Key :: struct {
type: string `json:"type"`,
curve: string `json:"curve"`,
key_size: int `json:"keySize"`,
uncompressed: Hex_Bytes `json:"uncompressed"`,
wx: Hex_Bytes `json:"wx"`,
wy: Hex_Bytes `json:"wy"`,
}
Ecdsa_Test_Group :: struct {
public_key: Ecdsa_Key `json:"publicKey"`,
public_key_der: Hex_Bytes `json:"publicKeyDer"`,
public_key_pem: string `json:"publicKeyPem"`,
type: string `json:"type"`,
sha: string `json:"sha"`,
tests: []Dsa_Test_Vector `json:"tests"`,
}
Dsa_Test_Vector :: struct {
tc_id: int `json:"tcId"`,
comment: string `json:"comment"`,