mirror of
https://github.com/saymrwulf/anza-cryptography-source.git
synced 2026-09-06 20:41:07 +00:00
fix 128bits scalar precondition (#44)
* Add prechecked optimized triple-base mul Introduce a prechecked 128/128/256 optimized path for vartime triple-base multiplication: vartime_triple_base_mul_128_128_256 now checks whether a1 and a2 fit in 128 bits and falls back to general multiplication if not. Add vartime_triple_base_mul_128_128_256_prechecked and corresponding serial/vector backend implementations (renamed to *_prechecked). Add scalar_fits_in_128_bits helper and update callers (verification_key) to use the prechecked path. Update docs/comments and add a test to ensure full-width scalars are handled by the fallback path. * bring back the docs * CI
This commit is contained in:
parent
cdc0688a81
commit
53383206b8
5 changed files with 79 additions and 23 deletions
|
|
@ -238,7 +238,8 @@ pub fn vartime_double_base_mul(a: &Scalar, A: &EdwardsPoint, b: &Scalar) -> Edwa
|
||||||
|
|
||||||
/// Compute \\(a_1 A_1 + a_2 A_2 + b B\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
/// Compute \\(a_1 A_1 + a_2 A_2 + b B\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
||||||
///
|
///
|
||||||
/// This function is optimized for the case where \\(a_1\\) and \\(a_2\\) are less than \\(2^{128}\\).
|
/// This function uses an optimized path when \\(a_1\\) and \\(a_2\\) are less than \\(2^{128}\\),
|
||||||
|
/// and falls back to general scalar multiplication otherwise.
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
pub fn vartime_triple_base_mul_128_128_256(
|
pub fn vartime_triple_base_mul_128_128_256(
|
||||||
a1: &Scalar,
|
a1: &Scalar,
|
||||||
|
|
@ -246,14 +247,39 @@ pub fn vartime_triple_base_mul_128_128_256(
|
||||||
a2: &Scalar,
|
a2: &Scalar,
|
||||||
A2: &EdwardsPoint,
|
A2: &EdwardsPoint,
|
||||||
b: &Scalar,
|
b: &Scalar,
|
||||||
|
) -> EdwardsPoint {
|
||||||
|
if !scalar_fits_in_128_bits(a1) || !scalar_fits_in_128_bits(a2) {
|
||||||
|
return (a1 * A1) + (a2 * A2) + EdwardsPoint::mul_base(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
vartime_triple_base_mul_128_128_256_prechecked(a1, A1, a2, A2, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Compute \\(a_1 A_1 + a_2 A_2 + b B\\) using the optimized 128/128/256-bit path.
|
||||||
|
///
|
||||||
|
/// Callers must ensure \\(a_1\\) and \\(a_2\\) are less than \\(2^{128}\\).
|
||||||
|
#[allow(non_snake_case)]
|
||||||
|
pub(crate) fn vartime_triple_base_mul_128_128_256_prechecked(
|
||||||
|
a1: &Scalar,
|
||||||
|
A1: &EdwardsPoint,
|
||||||
|
a2: &Scalar,
|
||||||
|
A2: &EdwardsPoint,
|
||||||
|
b: &Scalar,
|
||||||
) -> EdwardsPoint {
|
) -> EdwardsPoint {
|
||||||
match get_selected_backend() {
|
match get_selected_backend() {
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
BackendKind::Avx2 => {
|
BackendKind::Avx2 => {
|
||||||
vector::scalar_mul::vartime_triple_base::spec_avx2::mul_128_128_256(a1, A1, a2, A2, b)
|
vector::scalar_mul::vartime_triple_base::spec_avx2::mul_128_128_256_prechecked(
|
||||||
|
a1, A1, a2, A2, b,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
BackendKind::Serial => {
|
BackendKind::Serial => {
|
||||||
serial::scalar_mul::vartime_triple_base::mul_128_128_256(a1, A1, a2, A2, b)
|
serial::scalar_mul::vartime_triple_base::mul_128_128_256_prechecked(a1, A1, a2, A2, b)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline]
|
||||||
|
fn scalar_fits_in_128_bits(scalar: &Scalar) -> bool {
|
||||||
|
scalar.as_bytes()[16..32].iter().all(|&byte| byte == 0)
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,12 @@ use crate::window::NafLookupTable5;
|
||||||
/// This function is optimized for the case where \\(a_1\\) and \\(a_2\\) are known to be less than
|
/// This function is optimized for the case where \\(a_1\\) and \\(a_2\\) are known to be less than
|
||||||
/// \\(2^{128}\\), while \\(b\\) is a full 256-bit scalar.
|
/// \\(2^{128}\\), while \\(b\\) is a full 256-bit scalar.
|
||||||
///
|
///
|
||||||
|
/// # Precondition
|
||||||
|
///
|
||||||
|
/// Callers must ensure \\(a_1\\) and \\(a_2\\) are less than \\(2^{128}\\). Use
|
||||||
|
/// `vartime_triple_base_mul_128_128_256` for a checked wrapper that falls back
|
||||||
|
/// to general scalar multiplication for full-width scalars.
|
||||||
|
///
|
||||||
/// # Optimization Strategy
|
/// # Optimization Strategy
|
||||||
///
|
///
|
||||||
/// The function decomposes the 256-bit scalar \\(b\\) as \\(b = b_{lo} + b_{hi} \cdot 2^{128}\\),
|
/// The function decomposes the 256-bit scalar \\(b\\) as \\(b = b_{lo} + b_{hi} \cdot 2^{128}\\),
|
||||||
|
|
@ -50,17 +56,13 @@ use crate::window::NafLookupTable5;
|
||||||
///
|
///
|
||||||
/// The algorithm shares doublings across all four scalar multiplications, processing
|
/// The algorithm shares doublings across all four scalar multiplications, processing
|
||||||
/// only 128 bits instead of 256, providing approximately 2x speedup over the naive approach.
|
/// only 128 bits instead of 256, providing approximately 2x speedup over the naive approach.
|
||||||
pub fn mul_128_128_256(
|
pub(crate) fn mul_128_128_256_prechecked(
|
||||||
a1: &Scalar,
|
a1: &Scalar,
|
||||||
A1: &EdwardsPoint,
|
A1: &EdwardsPoint,
|
||||||
a2: &Scalar,
|
a2: &Scalar,
|
||||||
A2: &EdwardsPoint,
|
A2: &EdwardsPoint,
|
||||||
b: &Scalar,
|
b: &Scalar,
|
||||||
) -> EdwardsPoint {
|
) -> EdwardsPoint {
|
||||||
// assert that a1 and a2 are less than 2^128
|
|
||||||
debug_assert!(a1.as_bytes()[16..32].iter().all(|&b| b == 0));
|
|
||||||
debug_assert!(a2.as_bytes()[16..32].iter().all(|&b| b == 0));
|
|
||||||
|
|
||||||
// Decompose b into b_lo (lower 128 bits) and b_hi (upper 128 bits)
|
// Decompose b into b_lo (lower 128 bits) and b_hi (upper 128 bits)
|
||||||
// b = b_lo + b_hi * 2^128
|
// b = b_lo + b_hi * 2^128
|
||||||
let b_bytes = b.as_bytes();
|
let b_bytes = b.as_bytes();
|
||||||
|
|
@ -183,7 +185,7 @@ mod test {
|
||||||
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(3u64);
|
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(3u64);
|
||||||
|
|
||||||
// Compute using the optimized triple-base function
|
// Compute using the optimized triple-base function
|
||||||
let result = mul_128_128_256(&a1, &A1, &a2, &A2, &b);
|
let result = mul_128_128_256_prechecked(&a1, &A1, &a2, &A2, &b);
|
||||||
|
|
||||||
// Compute using naive addition
|
// Compute using naive addition
|
||||||
let expected = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
let expected = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
@ -217,7 +219,7 @@ mod test {
|
||||||
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(7u64);
|
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(7u64);
|
||||||
|
|
||||||
// Test the optimized 128-bit version
|
// Test the optimized 128-bit version
|
||||||
let result_128 = mul_128_128_256(&a1, &A1, &a2, &A2, &b);
|
let result_128 = mul_128_128_256_prechecked(&a1, &A1, &a2, &A2, &b);
|
||||||
|
|
||||||
// Compute expected result
|
// Compute expected result
|
||||||
let expected = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
let expected = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
@ -234,7 +236,7 @@ mod test {
|
||||||
let A1 = constants::ED25519_BASEPOINT_POINT * Scalar::from(2u64);
|
let A1 = constants::ED25519_BASEPOINT_POINT * Scalar::from(2u64);
|
||||||
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(3u64);
|
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(3u64);
|
||||||
|
|
||||||
let result = mul_128_128_256(&a1, &A1, &a2, &A2, &b);
|
let result = mul_128_128_256_prechecked(&a1, &A1, &a2, &A2, &b);
|
||||||
let expected = a2 * A2 + b * constants::ED25519_BASEPOINT_POINT;
|
let expected = a2 * A2 + b * constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -249,7 +251,7 @@ mod test {
|
||||||
let A1 = EdwardsPoint::identity();
|
let A1 = EdwardsPoint::identity();
|
||||||
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(3u64);
|
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(3u64);
|
||||||
|
|
||||||
let result = mul_128_128_256(&a1, &A1, &a2, &A2, &b);
|
let result = mul_128_128_256_prechecked(&a1, &A1, &a2, &A2, &b);
|
||||||
let expected = a2 * A2 + b * constants::ED25519_BASEPOINT_POINT;
|
let expected = a2 * A2 + b * constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -265,7 +267,7 @@ mod test {
|
||||||
let A1 = constants::ED25519_BASEPOINT_POINT * Scalar::from(11u64);
|
let A1 = constants::ED25519_BASEPOINT_POINT * Scalar::from(11u64);
|
||||||
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(13u64);
|
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(13u64);
|
||||||
|
|
||||||
let result_optimized = mul_128_128_256(&a1, &A1, &a2, &A2, &b);
|
let result_optimized = mul_128_128_256_prechecked(&a1, &A1, &a2, &A2, &b);
|
||||||
let result_general = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
let result_general = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
||||||
assert_eq!(result_optimized, result_general);
|
assert_eq!(result_optimized, result_general);
|
||||||
|
|
@ -287,7 +289,7 @@ mod test {
|
||||||
let A1 = constants::ED25519_BASEPOINT_POINT * Scalar::from(17u64);
|
let A1 = constants::ED25519_BASEPOINT_POINT * Scalar::from(17u64);
|
||||||
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(19u64);
|
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(19u64);
|
||||||
|
|
||||||
let result = mul_128_128_256(&a1, &A1, &a2, &A2, &b);
|
let result = mul_128_128_256_prechecked(&a1, &A1, &a2, &A2, &b);
|
||||||
let expected = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
let expected = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
||||||
assert_eq!(result, expected);
|
assert_eq!(result, expected);
|
||||||
|
|
@ -322,7 +324,7 @@ mod test {
|
||||||
let A2 = constants::ED25519_BASEPOINT_POINT * A2_scalar;
|
let A2 = constants::ED25519_BASEPOINT_POINT * A2_scalar;
|
||||||
|
|
||||||
// Compute using the optimized triple-base function
|
// Compute using the optimized triple-base function
|
||||||
let result_optimized = mul_128_128_256(&a1, &A1, &a2, &A2, &b);
|
let result_optimized = mul_128_128_256_prechecked(&a1, &A1, &a2, &A2, &b);
|
||||||
|
|
||||||
// Compute using raw operations: a1*A1 + a2*A2 + b*B
|
// Compute using raw operations: a1*A1 + a2*A2 + b*B
|
||||||
let expected = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
let expected = (a1 * A1 + a2 * A2) + b * constants::ED25519_BASEPOINT_POINT;
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,12 @@ pub mod spec {
|
||||||
/// This function is optimized for the case where \\(a_1\\) and \\(a_2\\) are known to be less than
|
/// This function is optimized for the case where \\(a_1\\) and \\(a_2\\) are known to be less than
|
||||||
/// \\(2^{128}\\), while \\(b\\) is a full 256-bit scalar.
|
/// \\(2^{128}\\), while \\(b\\) is a full 256-bit scalar.
|
||||||
///
|
///
|
||||||
|
/// # Precondition
|
||||||
|
///
|
||||||
|
/// Callers must ensure \\(a_1\\) and \\(a_2\\) are less than \\(2^{128}\\). Use
|
||||||
|
/// `vartime_triple_base_mul_128_128_256` for a checked wrapper that falls back
|
||||||
|
/// to general scalar multiplication for full-width scalars.
|
||||||
|
///
|
||||||
/// # Optimization Strategy
|
/// # Optimization Strategy
|
||||||
///
|
///
|
||||||
/// The function decomposes the 256-bit scalar \\(b\\) as \\(b = b_{lo} + b_{hi} \cdot 2^{128}\\),
|
/// The function decomposes the 256-bit scalar \\(b\\) as \\(b = b_{lo} + b_{hi} \cdot 2^{128}\\),
|
||||||
|
|
@ -63,17 +69,13 @@ pub mod spec {
|
||||||
///
|
///
|
||||||
/// This SIMD implementation uses vectorized point operations (AVX2 or AVX512-IFMA) for
|
/// This SIMD implementation uses vectorized point operations (AVX2 or AVX512-IFMA) for
|
||||||
/// improved performance over the serial backend.
|
/// improved performance over the serial backend.
|
||||||
pub fn mul_128_128_256(
|
pub(crate) fn mul_128_128_256_prechecked(
|
||||||
a1: &Scalar,
|
a1: &Scalar,
|
||||||
A1: &EdwardsPoint,
|
A1: &EdwardsPoint,
|
||||||
a2: &Scalar,
|
a2: &Scalar,
|
||||||
A2: &EdwardsPoint,
|
A2: &EdwardsPoint,
|
||||||
b: &Scalar,
|
b: &Scalar,
|
||||||
) -> EdwardsPoint {
|
) -> EdwardsPoint {
|
||||||
// assert that a1 and a2 are less than 2^128
|
|
||||||
debug_assert!(a1.as_bytes()[16..32].iter().all(|&b| b == 0));
|
|
||||||
debug_assert!(a2.as_bytes()[16..32].iter().all(|&b| b == 0));
|
|
||||||
|
|
||||||
// Decompose b into b_lo (lower 128 bits) and b_hi (upper 128 bits)
|
// Decompose b into b_lo (lower 128 bits) and b_hi (upper 128 bits)
|
||||||
// b = b_lo + b_hi * 2^128
|
// b = b_lo + b_hi * 2^128
|
||||||
let b_bytes = b.as_bytes();
|
let b_bytes = b.as_bytes();
|
||||||
|
|
|
||||||
|
|
@ -411,8 +411,11 @@ impl VerificationKey {
|
||||||
// Compute τs
|
// Compute τs
|
||||||
let ts = tau * s;
|
let ts = tau * s;
|
||||||
let A = if flip_h { -self.minus_A } else { self.minus_A };
|
let A = if flip_h { -self.minus_A } else { self.minus_A };
|
||||||
// Compute the multi-scalar multiplication
|
// HEEA decomposition guarantees tau and rho fit the optimized
|
||||||
let result = EdwardsPoint::vartime_triple_scalar_mul_basepoint(&tau, &neg_R, &rho, &A, &ts);
|
// 128/128/256-bit multiplication path.
|
||||||
|
let result = crate::backend::vartime_triple_base_mul_128_128_256_prechecked(
|
||||||
|
&tau, &neg_R, &rho, &A, &ts,
|
||||||
|
);
|
||||||
|
|
||||||
if result.mul_by_cofactor().is_identity() {
|
if result.mul_by_cofactor().is_identity() {
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|
|
||||||
|
|
@ -1075,7 +1075,8 @@ impl EdwardsPoint {
|
||||||
|
|
||||||
/// Compute \\(a_1 A_1 + a_2 A_2 + b B\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
/// Compute \\(a_1 A_1 + a_2 A_2 + b B\\) in variable time, where \\(B\\) is the Ed25519 basepoint.
|
||||||
///
|
///
|
||||||
/// This function is optimized for the case where \\(a_1\\) and \\(a_2\\) are less than \\(2^{128}\\).
|
/// This function is optimized for the case where \\(a_1\\) and \\(a_2\\) are less than \\(2^{128}\\),
|
||||||
|
/// and falls back to general scalar multiplication for full-width scalars.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
|
|
@ -2420,6 +2421,28 @@ mod test {
|
||||||
assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
|
assert_eq!(result.compress(), DOUBLE_SCALAR_MULT_RESULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn triple_scalar_mul_basepoint_accepts_full_width_scalars() {
|
||||||
|
let mut a1_bytes = [0u8; 32];
|
||||||
|
a1_bytes[0] = 7;
|
||||||
|
a1_bytes[16] = 1;
|
||||||
|
let a1 = Scalar::from_canonical_bytes(a1_bytes).unwrap();
|
||||||
|
|
||||||
|
let mut a2_bytes = [0u8; 32];
|
||||||
|
a2_bytes[0] = 11;
|
||||||
|
a2_bytes[24] = 1;
|
||||||
|
let a2 = Scalar::from_canonical_bytes(a2_bytes).unwrap();
|
||||||
|
|
||||||
|
let b = B_SCALAR;
|
||||||
|
let A1 = constants::ED25519_BASEPOINT_POINT * Scalar::from(17u64);
|
||||||
|
let A2 = constants::ED25519_BASEPOINT_POINT * Scalar::from(19u64);
|
||||||
|
|
||||||
|
let result = EdwardsPoint::vartime_triple_scalar_mul_basepoint(&a1, &A1, &a2, &A2, &b);
|
||||||
|
let expected = (a1 * A1) + (a2 * A2) + EdwardsPoint::mul_base(&b);
|
||||||
|
|
||||||
|
assert_eq!(result, expected);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
fn multiscalar_mul_vs_ed25519py() {
|
fn multiscalar_mul_vs_ed25519py() {
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue