mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-22 06:15:20 +00:00
Merge pull request #6239 from Yawning/feature/ecdsa
core:crypto/ecdsa: Add ECDSA support
This commit is contained in:
@@ -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})
|
||||
}
|
||||
|
||||
@@ -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})
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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[:])
|
||||
|
||||
7
core/crypto/ecdsa/doc.odin
Normal file
7
core/crypto/ecdsa/doc.odin
Normal 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
|
||||
321
core/crypto/ecdsa/ecdsa.odin
Normal file
321
core/crypto/ecdsa/ecdsa.odin
Normal 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))
|
||||
}
|
||||
183
core/crypto/ecdsa/ecdsa_asn1.odin
Normal file
183
core/crypto/ecdsa/ecdsa_asn1.odin
Normal 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
|
||||
}
|
||||
98
core/crypto/ecdsa/ecdsa_k_rfc6979.odin
Normal file
98
core/crypto/ecdsa/ecdsa_k_rfc6979.odin
Normal 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)
|
||||
}
|
||||
201
core/crypto/ecdsa/ecdsa_sign.odin
Normal file
201
core/crypto/ecdsa/ecdsa_sign.odin
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
148
core/crypto/ecdsa/ecdsa_verify.odin
Normal file
148
core/crypto/ecdsa/ecdsa_verify.odin
Normal 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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
|
||||
568
tests/core/crypto/test_core_crypto_exdsa.odin
Normal file
568
tests/core/crypto/test_core_crypto_exdsa.odin
Normal 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,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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"`,
|
||||
|
||||
Reference in New Issue
Block a user