diff --git a/mk/generate_curves.py b/mk/generate_curves.py index 8c898fae28..08e2596b4d 100644 --- a/mk/generate_curves.py +++ b/mk/generate_curves.py @@ -44,10 +44,10 @@ p: limbs_from_hex("%(q)x"), rr: limbs_from_hex(%(q_rr)s), }, - n: Elem::from_hex("%(n)x"), + n: PublicElem::from_hex("%(n)x"), - a: Elem::from_hex(%(a)s), - b: Elem::from_hex(%(b)s), + a: PublicElem::from_hex(%(a)s), + b: PublicElem::from_hex(%(b)s), elem_mul_mont: p%(bits)s_elem_mul_mont, elem_sqr_mont: p%(bits)s_elem_sqr_mont, @@ -56,8 +56,8 @@ }; pub(super) static GENERATOR: (Elem, Elem) = ( - Elem::from_hex(%(Gx)s), - Elem::from_hex(%(Gy)s), + PublicElem::from_hex(%(Gx)s), + PublicElem::from_hex(%(Gy)s), ); pub static PRIVATE_KEY_OPS: PrivateKeyOps = PrivateKeyOps { @@ -93,7 +93,8 @@ fn p%(bits)s_point_mul_base_impl(a: &Scalar) -> Point { // XXX: Not efficient. TODO: Precompute multiples of the generator. - PRIVATE_KEY_OPS.point_mul(a, &GENERATOR) + let generator = (Elem::from(&GENERATOR.0), Elem::from(&GENERATOR.1)); + PRIVATE_KEY_OPS.point_mul(a, &generator) } pub static PUBLIC_KEY_OPS: PublicKeyOps = PublicKeyOps { @@ -112,7 +113,7 @@ twin_mul_inefficient(&PRIVATE_KEY_OPS, g_scalar, p_scalar, p_xy, cpu) }, - q_minus_n: Elem::from_hex("%(q_minus_n)x"), + q_minus_n: PublicElem::from_hex("%(q_minus_n)x"), // TODO: Use an optimized variable-time implementation. scalar_inv_to_mont_vartime: |s| PRIVATE_SCALAR_OPS.scalar_inv_to_mont(s), @@ -121,7 +122,7 @@ pub static PRIVATE_SCALAR_OPS: PrivateScalarOps = PrivateScalarOps { scalar_ops: &SCALAR_OPS, - oneRR_mod_n: Scalar::from_hex(%(oneRR_mod_n)s), + oneRR_mod_n: PublicScalar::from_hex(%(oneRR_mod_n)s), scalar_inv_to_mont: p%(bits)s_scalar_inv_to_mont, }; diff --git a/src/arithmetic/constant.rs b/src/arithmetic/constant.rs index 1419bd7e07..81d61ea9a7 100644 --- a/src/arithmetic/constant.rs +++ b/src/arithmetic/constant.rs @@ -1,4 +1,4 @@ -use crate::limb::Limb; +use crate::limb::LeakyLimb; use core::mem::size_of; const fn parse_digit(d: u8) -> u8 { @@ -10,16 +10,16 @@ const fn parse_digit(d: u8) -> u8 { } // TODO: this would be nicer as a trait, but currently traits don't support const functions -pub const fn limbs_from_hex(hex: &str) -> [Limb; LIMBS] { +pub const fn limbs_from_hex(hex: &str) -> [LeakyLimb; LIMBS] { let hex = hex.as_bytes(); let mut limbs = [0; LIMBS]; - let limb_nibbles = size_of::() * 2; + let limb_nibbles = size_of::() * 2; let mut i = 0; while i < hex.len() { let char = hex[hex.len() - 1 - i]; let val = parse_digit(char); - limbs[i / limb_nibbles] |= (val as Limb) << ((i % limb_nibbles) * 4); + limbs[i / limb_nibbles] |= (val as LeakyLimb) << ((i % limb_nibbles) * 4); i += 1; } diff --git a/src/ec/curve25519/scalar.rs b/src/ec/curve25519/scalar.rs index 6eddd10d82..23ea971798 100644 --- a/src/ec/curve25519/scalar.rs +++ b/src/ec/curve25519/scalar.rs @@ -25,6 +25,7 @@ impl Scalar { pub fn from_bytes_checked(bytes: [u8; SCALAR_LEN]) -> Result { const ORDER: [limb::Limb; SCALAR_LEN / limb::LIMB_BYTES] = limbs_from_hex("1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed"); + let order = ORDER.map(limb::Limb::from); // `bytes` is in little-endian order. let mut reversed = bytes; @@ -34,7 +35,7 @@ impl Scalar { limb::parse_big_endian_in_range_and_pad_consttime( untrusted::Input::from(&reversed), limb::AllowZero::Yes, - &ORDER, + &order, &mut limbs, )?; diff --git a/src/ec/suite_b.rs b/src/ec/suite_b.rs index 7753052e9b..b0069c2f5b 100644 --- a/src/ec/suite_b.rs +++ b/src/ec/suite_b.rs @@ -33,7 +33,12 @@ fn verify_affine_point_is_on_the_curve( ops: &CommonOps, (x, y): (&Elem, &Elem), ) -> Result<(), error::Unspecified> { - verify_affine_point_is_on_the_curve_scaled(ops, (x, y), &ops.a, &ops.b) + verify_affine_point_is_on_the_curve_scaled( + ops, + (x, y), + &Elem::from(&ops.a), + &Elem::from(&ops.b), + ) } // Use `verify_affine_point_is_on_the_curve` instead of this function whenever @@ -101,9 +106,9 @@ fn verify_jacobian_point_is_on_the_curve( // let z2 = ops.elem_squared(&z); let z4 = ops.elem_squared(&z2); - let z4_a = ops.elem_product(&z4, &ops.a); + let z4_a = ops.elem_product(&z4, &Elem::from(&ops.a)); let z6 = ops.elem_product(&z4, &z2); - let z6_b = ops.elem_product(&z6, &ops.b); + let z6_b = ops.elem_product(&z6, &Elem::from(&ops.b)); verify_affine_point_is_on_the_curve_scaled(ops, (&x, &y), &z4_a, &z6_b)?; Ok(z2) } diff --git a/src/ec/suite_b/ecdsa/verification.rs b/src/ec/suite_b/ecdsa/verification.rs index cd0f3512b0..81fffbf2f9 100644 --- a/src/ec/suite_b/ecdsa/verification.rs +++ b/src/ec/suite_b/ecdsa/verification.rs @@ -159,7 +159,8 @@ impl EcdsaVerificationAlgorithm { return Ok(()); } if self.ops.elem_less_than(&r, &self.ops.q_minus_n) { - self.ops.scalar_ops.common.elem_add(&mut r, self.ops.n()); + let n = Elem::from(self.ops.n()); + self.ops.scalar_ops.common.elem_add(&mut r, &n); if sig_r_equals_x(self.ops, &r, &x, &z2) { return Ok(()); } diff --git a/src/ec/suite_b/ops.rs b/src/ec/suite_b/ops.rs index 35e6d5e4c0..3b18367505 100644 --- a/src/ec/suite_b/ops.rs +++ b/src/ec/suite_b/ops.rs @@ -23,6 +23,7 @@ pub use self::elem::*; /// A field element, i.e. an element of ℤ/qℤ for the curve's field modulus /// *q*. pub type Elem = elem::Elem; +type PublicElem = elem::PublicElem; /// Represents the (prime) order *q* of the curve's prime field. #[derive(Clone, Copy)] @@ -31,6 +32,7 @@ pub enum Q {} /// A scalar. Its value is in [0, n). Zero-valued scalars are forbidden in most /// contexts. pub type Scalar = elem::Elem; +type PublicScalar = elem::PublicElem; /// Represents the prime order *n* of the curve's group. #[derive(Clone, Copy)] @@ -57,10 +59,10 @@ impl Point { pub struct CommonOps { num_limbs: usize, q: Modulus, - n: Elem, + n: PublicElem, - pub a: Elem, // Must be -3 mod q - pub b: Elem, + pub a: PublicElem, // Must be -3 mod q + pub b: PublicElem, // In all cases, `r`, `a`, and `b` may all alias each other. elem_mul_mont: unsafe extern "C" fn(r: *mut Limb, a: *const Limb, b: *const Limb), @@ -98,8 +100,7 @@ impl CommonOps { #[inline] pub fn elem_unencoded(&self, a: &Elem) -> Elem { - const ONE: Elem = Elem::from_hex("1"); - self.elem_product(a, &ONE) + self.elem_product(a, &Elem::one()) } #[inline] @@ -171,8 +172,8 @@ impl CommonOps { } struct Modulus { - p: [Limb; MAX_LIMBS], - rr: [Limb; MAX_LIMBS], + p: [LeakyLimb; MAX_LIMBS], + rr: [LeakyLimb; MAX_LIMBS], } /// Operations on private keys, for ECDH and ECDSA signing. @@ -301,11 +302,11 @@ pub struct PublicScalarOps { cpu: cpu::Features, ) -> Point, scalar_inv_to_mont_vartime: fn(s: &Scalar, cpu: cpu::Features) -> Scalar, - pub(super) q_minus_n: Elem, + pub(super) q_minus_n: PublicElem, } impl PublicScalarOps { - pub fn n(&self) -> &Elem { + pub fn n(&self) -> &PublicElem { &self.scalar_ops.common.n } @@ -323,7 +324,7 @@ impl PublicScalarOps { == b.limbs[..self.public_key_ops.common.num_limbs] } - pub fn elem_less_than(&self, a: &Elem, b: &Elem) -> bool { + pub fn elem_less_than(&self, a: &Elem, b: &PublicElem) -> bool { let num_limbs = self.public_key_ops.common.num_limbs; limbs_less_than_limbs_vartime(&a.limbs[..num_limbs], &b.limbs[..num_limbs]) } @@ -341,13 +342,14 @@ impl PublicScalarOps { pub struct PrivateScalarOps { pub scalar_ops: &'static ScalarOps, - oneRR_mod_n: Scalar, // 1 * R**2 (mod n). TOOD: Use One. + oneRR_mod_n: PublicScalar, // 1 * R**2 (mod n). TOOD: Use One. scalar_inv_to_mont: fn(a: Scalar, cpu: cpu::Features) -> Scalar, } impl PrivateScalarOps { pub(super) fn to_mont(&self, s: &Scalar, cpu: cpu::Features) -> Scalar { - self.scalar_ops.scalar_product(s, &self.oneRR_mod_n, cpu) + self.scalar_ops + .scalar_product(s, &Scalar::from(&self.oneRR_mod_n), cpu) } /// Returns the modular inverse of `a` (mod `n`). Panics if `a` is zero. @@ -417,7 +419,7 @@ pub fn elem_parse_big_endian_fixed_consttime( ops: &CommonOps, bytes: untrusted::Input, ) -> Result, error::Unspecified> { - parse_big_endian_fixed_consttime(ops, bytes, AllowZero::Yes, &ops.q.p[..ops.num_limbs]) + parse_big_endian_fixed_consttime(ops, bytes, AllowZero::Yes, &ops.q.p) } #[inline] @@ -425,7 +427,7 @@ pub fn scalar_parse_big_endian_fixed_consttime( ops: &CommonOps, bytes: untrusted::Input, ) -> Result { - parse_big_endian_fixed_consttime(ops, bytes, AllowZero::No, &ops.n.limbs[..ops.num_limbs]) + parse_big_endian_fixed_consttime(ops, bytes, AllowZero::No, &ops.n.limbs) } #[inline] @@ -434,11 +436,12 @@ pub fn scalar_parse_big_endian_variable( allow_zero: AllowZero, bytes: untrusted::Input, ) -> Result { + let n = ops.n.limbs.map(Limb::from); let mut r = Scalar::zero(); parse_big_endian_in_range_and_pad_consttime( bytes, allow_zero, - &ops.n.limbs[..ops.num_limbs], + &n[..ops.num_limbs], &mut r.limbs[..ops.num_limbs], )?; Ok(r) @@ -463,8 +466,10 @@ fn parse_big_endian_fixed_consttime( ops: &CommonOps, bytes: untrusted::Input, allow_zero: AllowZero, - max_exclusive: &[Limb], + max_exclusive: &[LeakyLimb; MAX_LIMBS], ) -> Result, error::Unspecified> { + let max_exclusive = max_exclusive.map(Limb::from); + if bytes.len() != ops.len() { return Err(error::Unspecified); } @@ -472,7 +477,7 @@ fn parse_big_endian_fixed_consttime( parse_big_endian_in_range_and_pad_consttime( bytes, allow_zero, - max_exclusive, + &max_exclusive[..ops.num_limbs], &mut r.limbs[..ops.num_limbs], )?; Ok(r) @@ -509,8 +514,8 @@ mod tests { fn q_minus_n_plus_n_equals_0_test(ops: &PublicScalarOps) { let cops = ops.scalar_ops.common; - let mut x = ops.q_minus_n; - cops.elem_add(&mut x, &cops.n); + let mut x = Elem::from(&ops.q_minus_n); + cops.elem_add(&mut x, &Elem::from(&cops.n)); assert!(cops.is_zero(&x)); } @@ -958,9 +963,13 @@ mod tests { /// TODO: We should be testing `point_mul` with points other than the generator. #[test] fn p256_point_mul_test() { + let generator = ( + Elem::from(&p256::GENERATOR.0), + Elem::from(&p256::GENERATOR.1), + ); point_mul_base_tests( &p256::PRIVATE_KEY_OPS, - |s, cpu| p256::PRIVATE_KEY_OPS.point_mul(s, &p256::GENERATOR, cpu), + |s, cpu| p256::PRIVATE_KEY_OPS.point_mul(s, &generator, cpu), test_file!("ops/p256_point_mul_base_tests.txt"), ); } @@ -968,9 +977,14 @@ mod tests { /// TODO: We should be testing `point_mul` with points other than the generator. #[test] fn p384_point_mul_test() { + let generator = ( + Elem::from(&p384::GENERATOR.0), + Elem::from(&p384::GENERATOR.1), + ); + point_mul_base_tests( &p384::PRIVATE_KEY_OPS, - |s, cpu| p384::PRIVATE_KEY_OPS.point_mul(s, &p384::GENERATOR, cpu), + |s, cpu| p384::PRIVATE_KEY_OPS.point_mul(s, &generator, cpu), test_file!("ops/p384_point_mul_base_tests.txt"), ); } diff --git a/src/ec/suite_b/ops/elem.rs b/src/ec/suite_b/ops/elem.rs index e8479f2af6..f63a56fc69 100644 --- a/src/ec/suite_b/ops/elem.rs +++ b/src/ec/suite_b/ops/elem.rs @@ -15,9 +15,9 @@ use crate::{ arithmetic::{ limbs_from_hex, - montgomery::{Encoding, ProductEncoding}, + montgomery::{Encoding, ProductEncoding, Unencoded}, }, - limb::{Limb, LIMB_BITS}, + limb::{LeakyLimb, Limb, LIMB_BITS}, }; use core::marker::PhantomData; @@ -36,6 +36,22 @@ pub struct Elem { pub(super) encoding: PhantomData, } +pub struct PublicElem { + pub(super) limbs: [LeakyLimb; MAX_LIMBS], + pub(super) m: PhantomData, + pub(super) encoding: PhantomData, +} + +impl From<&PublicElem> for Elem { + fn from(value: &PublicElem) -> Self { + Self { + limbs: core::array::from_fn(|i| Limb::from(value.limbs[i])), + m: value.m, + encoding: value.encoding, + } + } +} + impl Elem { // There's no need to convert `value` to the Montgomery domain since // 0 * R**2 (mod m) == 0, so neither the modulus nor the encoding are needed @@ -47,9 +63,19 @@ impl Elem { encoding: PhantomData, } } +} +impl Elem { + pub fn one() -> Self { + let mut r = Self::zero(); + r.limbs[0] = 1; + r + } +} + +impl PublicElem { pub const fn from_hex(hex: &str) -> Self { - Elem { + Self { limbs: limbs_from_hex(hex), m: PhantomData, encoding: PhantomData, diff --git a/src/ec/suite_b/ops/p256.rs b/src/ec/suite_b/ops/p256.rs index 67fd8f001f..853a0d6ff9 100644 --- a/src/ec/suite_b/ops/p256.rs +++ b/src/ec/suite_b/ops/p256.rs @@ -24,10 +24,10 @@ pub static COMMON_OPS: CommonOps = CommonOps { p: limbs_from_hex("ffffffff00000001000000000000000000000000ffffffffffffffffffffffff"), rr: limbs_from_hex("4fffffffdfffffffffffffffefffffffbffffffff0000000000000003"), }, - n: Elem::from_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"), + n: PublicElem::from_hex("ffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551"), - a: Elem::from_hex("fffffffc00000004000000000000000000000003fffffffffffffffffffffffc"), - b: Elem::from_hex("dc30061d04874834e5a220abf7212ed6acf005cd78843090d89cdf6229c4bddf"), + a: PublicElem::from_hex("fffffffc00000004000000000000000000000003fffffffffffffffffffffffc"), + b: PublicElem::from_hex("dc30061d04874834e5a220abf7212ed6acf005cd78843090d89cdf6229c4bddf"), elem_mul_mont: p256_mul_mont, elem_sqr_mont: p256_sqr_mont, @@ -36,9 +36,9 @@ pub static COMMON_OPS: CommonOps = CommonOps { }; #[cfg(test)] -pub(super) static GENERATOR: (Elem, Elem) = ( - Elem::from_hex("18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c"), - Elem::from_hex("8571ff1825885d85d2e88688dd21f3258b4ab8e4ba19e45cddf25357ce95560a"), +pub(super) static GENERATOR: (PublicElem, PublicElem) = ( + PublicElem::from_hex("18905f76a53755c679fb732b7762251075ba95fc5fedb60179e730d418a9143c"), + PublicElem::from_hex("8571ff1825885d85d2e88688dd21f3258b4ab8e4ba19e45cddf25357ce95560a"), ); pub static PRIVATE_KEY_OPS: PrivateKeyOps = PrivateKeyOps { @@ -129,7 +129,7 @@ pub static PUBLIC_SCALAR_OPS: PublicScalarOps = PublicScalarOps { twin_mul_inefficient(&PRIVATE_KEY_OPS, g_scalar, p_scalar, p_xy, cpu) }, - q_minus_n: Elem::from_hex("4319055358e8617b0c46353d039cdaae"), + q_minus_n: PublicElem::from_hex("4319055358e8617b0c46353d039cdaae"), // TODO: Use an optimized variable-time implementation. scalar_inv_to_mont_vartime: |s, cpu| PRIVATE_SCALAR_OPS.scalar_inv_to_mont(s, cpu), @@ -164,7 +164,7 @@ fn point_mul_base_vartime(g_scalar: &Scalar, _cpu: cpu::Features) -> Point { pub static PRIVATE_SCALAR_OPS: PrivateScalarOps = PrivateScalarOps { scalar_ops: &SCALAR_OPS, - oneRR_mod_n: Scalar::from_hex( + oneRR_mod_n: PublicScalar::from_hex( "66e12d94f3d956202845b2392b6bec594699799c49bd6fa683244c95be79eea2", ), scalar_inv_to_mont: p256_scalar_inv_to_mont, diff --git a/src/ec/suite_b/ops/p384.rs b/src/ec/suite_b/ops/p384.rs index 47e181dd0e..ec77829b73 100644 --- a/src/ec/suite_b/ops/p384.rs +++ b/src/ec/suite_b/ops/p384.rs @@ -24,10 +24,10 @@ pub static COMMON_OPS: CommonOps = CommonOps { p: limbs_from_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff"), rr: limbs_from_hex("10000000200000000fffffffe000000000000000200000000fffffffe00000001"), }, - n: Elem::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973"), + n: PublicElem::from_hex("ffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973"), - a: Elem::from_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffc0000000000000003fffffffc"), - b: Elem::from_hex("cd08114b604fbff9b62b21f41f022094e3374bee94938ae277f2209b1920022ef729add87a4c32ec081188719d412dcc"), + a: PublicElem::from_hex("fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffbfffffffc0000000000000003fffffffc"), + b: PublicElem::from_hex("cd08114b604fbff9b62b21f41f022094e3374bee94938ae277f2209b1920022ef729add87a4c32ec081188719d412dcc"), elem_mul_mont: p384_elem_mul_mont, elem_sqr_mont: p384_elem_sqr_mont, @@ -35,9 +35,9 @@ pub static COMMON_OPS: CommonOps = CommonOps { point_add_jacobian_impl: p384_point_add, }; -pub(super) static GENERATOR: (Elem, Elem) = ( - Elem::from_hex("4d3aadc2299e1513812ff723614ede2b6454868459a30eff879c3afc541b4d6e20e378e2a0d6ce383dd0756649c0b528"), - Elem::from_hex("2b78abc25a15c5e9dd8002263969a840c6c3521968f4ffd98bade7562e83b050a1bfa8bf7bb4a9ac23043dad4b03a4fe"), +pub(super) static GENERATOR: (PublicElem, PublicElem) = ( + PublicElem::from_hex("4d3aadc2299e1513812ff723614ede2b6454868459a30eff879c3afc541b4d6e20e378e2a0d6ce383dd0756649c0b528"), + PublicElem::from_hex("2b78abc25a15c5e9dd8002263969a840c6c3521968f4ffd98bade7562e83b050a1bfa8bf7bb4a9ac23043dad4b03a4fe"), ); pub static PRIVATE_KEY_OPS: PrivateKeyOps = PrivateKeyOps { @@ -105,7 +105,8 @@ fn p384_elem_inv_squared(a: &Elem, _cpu: cpu::Features) -> Elem { fn p384_point_mul_base_impl(a: &Scalar, cpu: cpu::Features) -> Point { // XXX: Not efficient. TODO: Precompute multiples of the generator. - PRIVATE_KEY_OPS.point_mul(a, &GENERATOR, cpu) + let generator = (Elem::from(&GENERATOR.0), Elem::from(&GENERATOR.1)); + PRIVATE_KEY_OPS.point_mul(a, &generator, cpu) } pub static PUBLIC_KEY_OPS: PublicKeyOps = PublicKeyOps { @@ -124,7 +125,7 @@ pub static PUBLIC_SCALAR_OPS: PublicScalarOps = PublicScalarOps { twin_mul_inefficient(&PRIVATE_KEY_OPS, g_scalar, p_scalar, p_xy, cpu) }, - q_minus_n: Elem::from_hex("389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68c"), + q_minus_n: PublicElem::from_hex("389cb27e0bc8d21fa7e5f24cb74f58851313e696333ad68c"), // TODO: Use an optimized variable-time implementation. scalar_inv_to_mont_vartime: |s, cpu| PRIVATE_SCALAR_OPS.scalar_inv_to_mont(s, cpu), @@ -133,7 +134,7 @@ pub static PUBLIC_SCALAR_OPS: PublicScalarOps = PublicScalarOps { pub static PRIVATE_SCALAR_OPS: PrivateScalarOps = PrivateScalarOps { scalar_ops: &SCALAR_OPS, - oneRR_mod_n: Scalar::from_hex("c84ee012b39bf213fb05b7a28266895d40d49174aab1cc5bc3e483afcb82947ff3d81e5df1aa4192d319b2419b409a9"), + oneRR_mod_n: PublicScalar::from_hex("c84ee012b39bf213fb05b7a28266895d40d49174aab1cc5bc3e483afcb82947ff3d81e5df1aa4192d319b2419b409a9"), scalar_inv_to_mont: p384_scalar_inv_to_mont, }; diff --git a/src/limb.rs b/src/limb.rs index ff58c9ff6e..44757340fb 100644 --- a/src/limb.rs +++ b/src/limb.rs @@ -28,6 +28,7 @@ use core::num::Wrapping; // XXX: Not correct for x32 ABIs. pub type Limb = constant_time::Word; +pub type LeakyLimb = constant_time::LeakyWord; pub const LIMB_BITS: usize = usize_from_u32(Limb::BITS); pub const LIMB_BYTES: usize = (LIMB_BITS + 7) / 8;