core/crypto/_subtle: Refactor out common helpers

This commit is contained in:
Yawning Angel
2026-01-02 23:59:38 +09:00
parent 429e8a46db
commit 6bbd060352
4 changed files with 60 additions and 8 deletions

View File

@@ -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)
}

View File

@@ -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) {

View File

@@ -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
}

View File

@@ -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