diff options
| -rw-r--r-- | core/crypto/ecdh/ecdh.odin | 71 | ||||
| -rw-r--r-- | tests/core/crypto/test_core_crypto_ecdh.odin | 13 |
2 files changed, 84 insertions, 0 deletions
diff --git a/core/crypto/ecdh/ecdh.odin b/core/crypto/ecdh/ecdh.odin index 3e97ebdc7..80ef3f704 100644 --- a/core/crypto/ecdh/ecdh.odin +++ b/core/crypto/ecdh/ecdh.odin @@ -18,6 +18,7 @@ X448_Buf :: [x448.SCALAR_SIZE]byte Curve :: enum { Invalid, SECP256R1, + SECP384R1, X25519, X448, } @@ -26,6 +27,7 @@ Curve :: enum { CURVE_NAMES := [Curve]string { .Invalid = "Invalid", .SECP256R1 = "secp256r1", + .SECP384R1 = "secp384r1", .X25519 = "X25519", .X448 = "X448", } @@ -34,6 +36,7 @@ CURVE_NAMES := [Curve]string { PRIVATE_KEY_SIZES := [Curve]int { .Invalid = 0, .SECP256R1 = secec.SC_SIZE_P256R1, + .SECP384R1 = secec.SC_SIZE_P384R1, .X25519 = x25519.SCALAR_SIZE, .X448 = x448.SCALAR_SIZE, } @@ -42,6 +45,7 @@ PRIVATE_KEY_SIZES := [Curve]int { PUBLIC_KEY_SIZES := [Curve]int { .Invalid = 0, .SECP256R1 = 1 + 2 * secec.FE_SIZE_P256R1, + .SECP384R1 = 1 + 2 * secec.FE_SIZE_P384R1, .X25519 = x25519.POINT_SIZE, .X448 = x448.POINT_SIZE, } @@ -50,6 +54,7 @@ PUBLIC_KEY_SIZES := [Curve]int { SHARED_SECRET_SIZES := [Curve]int { .Invalid = 0, .SECP256R1 = secec.FE_SIZE_P256R1, + .SECP384R1 = secec.FE_SIZE_P384R1, .X25519 = x25519.POINT_SIZE, .X448 = x448.POINT_SIZE, } @@ -58,6 +63,7 @@ SHARED_SECRET_SIZES := [Curve]int { _PRIV_IMPL_IDS := [Curve]typeid { .Invalid = nil, .SECP256R1 = typeid_of(secec.Scalar_p256r1), + .SECP384R1 = typeid_of(secec.Scalar_p384r1), .X25519 = typeid_of(X25519_Buf), .X448 = typeid_of(X448_Buf), } @@ -66,6 +72,7 @@ _PRIV_IMPL_IDS := [Curve]typeid { _PUB_IMPL_IDS := [Curve]typeid { .Invalid = nil, .SECP256R1 = typeid_of(secec.Point_p256r1), + .SECP384R1 = typeid_of(secec.Point_p384r1), .X25519 = typeid_of(X25519_Buf), .X448 = typeid_of(X448_Buf), } @@ -77,6 +84,7 @@ Private_Key :: struct { _curve: Curve, _impl: union { secec.Scalar_p256r1, + secec.Scalar_p384r1, X25519_Buf, X448_Buf, }, @@ -90,6 +98,7 @@ Public_Key :: struct { _curve: Curve, _impl: union { secec.Point_p256r1, + secec.Point_p384r1, X25519_Buf, X448_Buf, }, @@ -124,6 +133,18 @@ private_key_generate :: proc(priv_key: ^Private_Key, curve: Curve) -> bool { break } } + case .SECP384R1: + sc := &priv_key._impl.(secec.Scalar_p384r1) + + b: [48]byte = --- + defer (mem.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 + } + } case .X25519: sc := &priv_key._impl.(X25519_Buf) crypto.rand_bytes(sc[:]) @@ -166,6 +187,17 @@ private_key_set_bytes :: proc(priv_key: ^Private_Key, curve: Curve, b: []byte) - 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 .X25519: sc := &priv_key._impl.(X25519_Buf) copy(sc[:], b) @@ -190,6 +222,11 @@ private_key_generate_public :: proc(priv_key: ^Private_Key) { 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 X25519_Buf: pub_key: X25519_Buf = --- x25519.scalarmult_basepoint(pub_key[:], sc[:]) @@ -214,6 +251,9 @@ private_key_bytes :: proc(priv_key: ^Private_Key, dst: []byte) { 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 .X25519: sc := &priv_key._impl.(X25519_Buf) copy(dst, sc[:]) @@ -236,6 +276,9 @@ private_key_equal :: proc(p, q: ^Private_Key) -> bool { 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 .X25519: b_p, b_q := &p._impl.(X25519_Buf), &q._impl.(X25519_Buf) return crypto.compare_constant_time(b_p[:], b_q[:]) == 1 @@ -277,6 +320,16 @@ public_key_set_bytes :: proc(pub_key: ^Public_Key, curve: Curve, b: []byte) -> 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 .X25519: pt := &pub_key._impl.(X25519_Buf) copy(pt[:], b) @@ -313,6 +366,14 @@ public_key_bytes :: proc(pub_key: ^Public_Key, dst: []byte) { 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 .X25519: pt := &pub_key._impl.(X25519_Buf) copy(dst, pt[:]) @@ -335,6 +396,9 @@ public_key_equal :: proc(p, q: ^Public_Key) -> bool { 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 .X25519: b_p, b_q := &p._impl.(X25519_Buf), &q._impl.(X25519_Buf) return crypto.compare_constant_time(b_p[:], b_q[:]) == 1 @@ -369,6 +433,13 @@ ecdh :: proc(priv_key: ^Private_Key, pub_key: ^Public_Key, dst: []byte) -> bool secec.pt_scalar_mul(&ss, pt, sc) return secec.pt_bytes(dst, nil, &ss) + case .SECP384R1: + sc, pt := &priv_key._impl.(secec.Scalar_p384r1), &pub_key._impl.(secec.Point_p384r1) + ss: secec.Point_p384r1 + defer secec.pt_clear(&ss) + + secec.pt_scalar_mul(&ss, pt, sc) + return secec.pt_bytes(dst, nil, &ss) case .X25519: sc, pt := &priv_key._impl.(X25519_Buf), &pub_key._impl.(X25519_Buf) x25519.scalarmult(dst, sc[:], pt[:]) diff --git a/tests/core/crypto/test_core_crypto_ecdh.odin b/tests/core/crypto/test_core_crypto_ecdh.odin index 87c9e8a4d..e34671c84 100644 --- a/tests/core/crypto/test_core_crypto_ecdh.odin +++ b/tests/core/crypto/test_core_crypto_ecdh.odin @@ -52,6 +52,19 @@ test_ecdh :: proc(t: ^testing.T) { "04809f04289c64348c01515eb03d5ce7ac1a8cb9498f5caa50197e58d43a86a7aeb29d84e811197f25eba8f5194092cb6ff440e26d4421011372461f579271cda3", "057d636096cb80b67a8c038c890e887d1adfa4195e9b3ce241c8a778c59cda67", }, + // secp384r1 Test vectors (subset) from NIST CAVP + { + .SECP384R1, + "3cc3122a68f0d95027ad38c067916ba0eb8c38894d22e1b15618b6818a661774ad463b205da88cf699ab4d43c9cf98a1", + "04a7c76b970c3b5fe8b05d2838ae04ab47697b9eaf52e764592efda27fe7513272734466b400091adbf2d68c58e0c50066ac68f19f2e1cb879aed43a9969b91a0839c4c38a49749b661efedf243451915ed0905a32b060992b468c64766fc8437a", + "5f9d29dc5e31a163060356213669c8ce132e22f57c9a04f40ba7fcead493b457e5621e766c40a2e3d4d6a04b25e533f1", + }, + { + .SECP384R1, + "92860c21bde06165f8e900c687f8ef0a05d14f290b3f07d8b3a8cc6404366e5d5119cd6d03fb12dc58e89f13df9cd783", + "0430f43fcf2b6b00de53f624f1543090681839717d53c7c955d1d69efaf0349b7363acb447240101cbb3af6641ce4b88e025e46c0c54f0162a77efcc27b6ea792002ae2ba82714299c860857a68153ab62e525ec0530d81b5aa15897981e858757", + "a23742a2c267d7425fda94b93f93bbcc24791ac51cd8fd501a238d40812f4cbfc59aac9520d758cf789c76300c69d2ff", + }, } for v, _ in test_vectors { |