diff --git a/core/crypto/_edwards25519/edwards25519_scalar_mul.odin b/core/crypto/_edwards25519/edwards25519_scalar_mul.odin index c24447072..6e0eb42c7 100644 --- a/core/crypto/_edwards25519/edwards25519_scalar_mul.odin +++ b/core/crypto/_edwards25519/edwards25519_scalar_mul.odin @@ -1,7 +1,7 @@ package _edwards25519 import field "core:crypto/_fiat/field_scalar25519" -import "core:math/bits" +import subtle "core:crypto/_subtle" import "core:mem" // GE_BASEPOINT_TABLE is 1 * G, ... 15 * G, in precomputed format. @@ -281,8 +281,8 @@ mul_tbl_add :: proc "contextless" ( {2, 0, 0, 0, 0}, // z * 2 } for i := u64(1); i < 16; i = i + 1 { - _, ctrl := bits.sub_u64(0, (i ~ idx), 0) - ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(~ctrl) & 1) + ctrl := subtle.eq(i, idx) + ge_addend_conditional_assign(tmp_addend, &tbl[i - 1], int(ctrl)) } ge_add_addend(ge, ge, tmp_addend, tmp_add) } diff --git a/core/crypto/_fiat/field_scalar25519/field.odin b/core/crypto/_fiat/field_scalar25519/field.odin index 933637c54..96b279ce7 100644 --- a/core/crypto/_fiat/field_scalar25519/field.odin +++ b/core/crypto/_fiat/field_scalar25519/field.odin @@ -1,5 +1,6 @@ package field_scalar25519 +import subtle "core:crypto/_subtle" import "core:encoding/endian" import "core:math/bits" import "core:mem" @@ -121,13 +122,11 @@ fe_equal :: proc "contextless" (arg1, arg2: ^Montgomery_Domain_Field_Element) -> tmp: Montgomery_Domain_Field_Element fe_sub(&tmp, arg1, arg2) - // This will only underflow iff arg1 == arg2, and we return the borrow, - // which will be 1. - _, borrow := bits.sub_u64(fe_non_zero(&tmp), 1, 0) + is_eq := subtle.eq(fe_non_zero(&tmp), 0) fe_clear(&tmp) - return int(borrow) + return int(is_eq) } fe_zero :: proc "contextless" (out1: ^Montgomery_Domain_Field_Element) { diff --git a/core/crypto/_subtle/subtle.odin b/core/crypto/_subtle/subtle.odin new file mode 100644 index 000000000..89328072c --- /dev/null +++ b/core/crypto/_subtle/subtle.odin @@ -0,0 +1,42 @@ +/* +Various useful bit operations in constant time. +*/ +package _subtle + +import "core:math/bits" + +// byte_eq returns 1 iff a == b, 0 otherwise. +@(optimization_mode="none") +byte_eq :: proc "contextless" (a, b: byte) -> int { + v := a ~ b + + // v == 0 iff a == b. The subtraction will underflow, setting the + // sign bit, which will get returned. + return int((u32(v)-1) >> 31) +} + +// u64_eq returns 1 iff a == b, 0 otherwise. +@(optimization_mode="none") +u64_eq :: proc "contextless" (a, b: u64) -> u64 { + _, borrow := bits.sub_u64(0, a ~ b, 0) + return (~borrow) & 1 +} + +eq :: proc { + byte_eq, + u64_eq, +} + +// u64_is_zero returns 1 iff a == 0, 0 otherwise. +@(optimization_mode="none") +u64_is_zero :: proc "contextless" (a: u64) -> u64 { + _, borrow := bits.sub_u64(a, 1, 0) + return borrow +} + +// u64_is_non_zero returns 1 iff a != 0, 0 otherwise. +@(optimization_mode="none") +u64_is_non_zero :: proc "contextless" (a: u64) -> u64 { + is_zero := u64_is_zero(a) + return (~is_zero) & 1 +} diff --git a/core/crypto/crypto.odin b/core/crypto/crypto.odin index 7ccf126e6..435c5daaf 100644 --- a/core/crypto/crypto.odin +++ b/core/crypto/crypto.odin @@ -2,6 +2,7 @@ package crypto import "base:runtime" +import subtle "core:crypto/_subtle" import "core:mem" // HAS_RAND_BYTES is true iff the runtime provides a cryptographic @@ -44,7 +45,17 @@ compare_byte_ptrs_constant_time :: proc "contextless" (a, b: ^byte, n: int) -> i // After the loop, v == 0 iff a == b. The subtraction will underflow // iff v == 0, setting the sign-bit, which gets returned. - return int((u32(v)-1) >> 31) + return subtle.eq(0, v) +} + +// is_zero_constant_time returns 1 iff b is all 0s, 0 otherwise. +is_zero_constant_time :: proc "contextless" (b: []byte) -> int { + v: byte + for b_ in b { + v |= b_ + } + + return subtle.byte_eq(0, v) } // rand_bytes fills the dst buffer with cryptographic entropy taken from