From 6212ecbdf5dde3e3bb3b49179766ba5fc1f6e258 Mon Sep 17 00:00:00 2001 From: Brian Smith Date: Mon, 9 Dec 2024 17:16:27 -0800 Subject: [PATCH] ec/suite_b: Thread CPU features through functions using `Modulus`. Follow the same pattern that is used in arithmetic/bigint.rs: Since we have to construct a `Modulus` at roughly the same place we have to get the proof we've done CPU feature detection, just have `Modulus` contain the `cpu::Features`, so that any function that takes a `Modulus` no longer needs a `cpu::Features` argument. Then, make a step towards threading `cpu::Features` all the way down to to the callers of the lowest-level C/assembly functions. This will facilitate a future refactoring where all dispatching based on CPU features is moved out of the assembly code, from upstream. --- mk/generate_curves.py | 8 +- src/ec.rs | 9 +- src/ec/curve25519/x25519.rs | 6 +- src/ec/keys.rs | 8 +- src/ec/suite_b.rs | 43 +++--- src/ec/suite_b/curve.rs | 14 +- src/ec/suite_b/ecdh.rs | 8 +- src/ec/suite_b/ecdsa/digest_scalar.rs | 5 +- src/ec/suite_b/ecdsa/signing.rs | 14 +- src/ec/suite_b/ecdsa/verification.rs | 36 ++--- src/ec/suite_b/ops.rs | 207 ++++++++++++++++++-------- src/ec/suite_b/ops/p256.rs | 36 ++--- src/ec/suite_b/ops/p384.rs | 39 ++--- src/ec/suite_b/private_key.rs | 44 +++--- src/ec/suite_b/public_key.rs | 15 +- 15 files changed, 283 insertions(+), 209 deletions(-) diff --git a/mk/generate_curves.py b/mk/generate_curves.py index c1939da02..e5d4f6485 100644 --- a/mk/generate_curves.py +++ b/mk/generate_curves.py @@ -77,13 +77,13 @@ // %(q_minus_3)s #[inline] - fn sqr_mul(a: &Elem, squarings: LeakyWord, b: &Elem) -> Elem { - elem_sqr_mul(&COMMON_OPS, a, squarings, b) + fn sqr_mul(q: &Modulus, a: &Elem, squarings: LeakyWord, b: &Elem) -> Elem { + elem_sqr_mul(&COMMON_OPS, a, squarings, b, q.cpu()) } #[inline] - fn sqr_mul_acc(a: &mut Elem, squarings: LeakyWord, b: &Elem) { - elem_sqr_mul_acc(&COMMON_OPS, a, squarings, b) + fn sqr_mul_acc(q: &Modulus, a: &mut Elem, squarings: LeakyWord, b: &Elem) { + elem_sqr_mul_acc(&COMMON_OPS, a, squarings, b, q.cpu()) } let b_1 = &a; diff --git a/src/ec.rs b/src/ec.rs index faa44fd1a..a1cfa09a4 100644 --- a/src/ec.rs +++ b/src/ec.rs @@ -23,10 +23,13 @@ pub struct Curve { pub id: CurveID, // Precondition: `bytes` is the correct length. - check_private_key_bytes: fn(bytes: &[u8]) -> Result<(), error::Unspecified>, + check_private_key_bytes: fn(bytes: &[u8], cpu: cpu::Features) -> Result<(), error::Unspecified>, - generate_private_key: - fn(rng: &dyn rand::SecureRandom, &mut [u8]) -> Result<(), error::Unspecified>, + generate_private_key: fn( + rng: &dyn rand::SecureRandom, + &mut [u8], + cpu: cpu::Features, + ) -> Result<(), error::Unspecified>, public_from_private: fn( public_out: &mut [u8], diff --git a/src/ec/curve25519/x25519.rs b/src/ec/curve25519/x25519.rs index 5bf0f21dd..ef9c370ab 100644 --- a/src/ec/curve25519/x25519.rs +++ b/src/ec/curve25519/x25519.rs @@ -40,7 +40,10 @@ pub static X25519: agreement::Algorithm = agreement::Algorithm { }; #[allow(clippy::unnecessary_wraps)] -fn x25519_check_private_key_bytes(bytes: &[u8]) -> Result<(), error::Unspecified> { +fn x25519_check_private_key_bytes( + bytes: &[u8], + _: cpu::Features, +) -> Result<(), error::Unspecified> { debug_assert_eq!(bytes.len(), PRIVATE_KEY_LEN); Ok(()) } @@ -48,6 +51,7 @@ fn x25519_check_private_key_bytes(bytes: &[u8]) -> Result<(), error::Unspecified fn x25519_generate_private_key( rng: &dyn rand::SecureRandom, out: &mut [u8], + _: cpu::Features, ) -> Result<(), error::Unspecified> { rng.fill(out) } diff --git a/src/ec/keys.rs b/src/ec/keys.rs index c7ea9f73d..6cf925035 100644 --- a/src/ec/keys.rs +++ b/src/ec/keys.rs @@ -32,26 +32,26 @@ impl Seed { pub(crate) fn generate( curve: &'static Curve, rng: &dyn rand::SecureRandom, - _cpu_features: cpu::Features, + cpu: cpu::Features, ) -> Result { let mut r = Self { bytes: [0u8; SEED_MAX_BYTES], curve, }; - (curve.generate_private_key)(rng, &mut r.bytes[..curve.elem_scalar_seed_len])?; + (curve.generate_private_key)(rng, &mut r.bytes[..curve.elem_scalar_seed_len], cpu)?; Ok(r) } pub(crate) fn from_bytes( curve: &'static Curve, bytes: untrusted::Input, - _cpu_features: cpu::Features, + cpu: cpu::Features, ) -> Result { let bytes = bytes.as_slice_less_safe(); if curve.elem_scalar_seed_len != bytes.len() { return Err(error::Unspecified); } - (curve.check_private_key_bytes)(bytes)?; + (curve.check_private_key_bytes)(bytes, cpu)?; let mut r = Self { bytes: [0; SEED_MAX_BYTES], curve, diff --git a/src/ec/suite_b.rs b/src/ec/suite_b.rs index 8f3809ecb..cfe849874 100644 --- a/src/ec/suite_b.rs +++ b/src/ec/suite_b.rs @@ -30,17 +30,10 @@ use crate::{arithmetic::montgomery::*, cpu, ec, error, io::der, pkcs8}; // y**2 == (x**2 + a)*x + b (mod q) // fn verify_affine_point_is_on_the_curve( - ops: &CommonOps, q: &Modulus, (x, y): (&Elem, &Elem), ) -> Result<(), error::Unspecified> { - verify_affine_point_is_on_the_curve_scaled( - ops, - q, - (x, y), - &Elem::from(&ops.a), - &Elem::from(&ops.b), - ) + verify_affine_point_is_on_the_curve_scaled(q, (x, y), &Elem::from(q.a()), &Elem::from(q.b())) } // Use `verify_affine_point_is_on_the_curve` instead of this function whenever @@ -53,17 +46,16 @@ fn verify_affine_point_is_on_the_curve( // // This function also verifies that the point is not at infinity. fn verify_jacobian_point_is_on_the_curve( - ops: &CommonOps, q: &Modulus, p: &Point, ) -> Result, error::Unspecified> { - let z = ops.point_z(p); + let z = q.point_z(p); // Verify that the point is not at infinity. - ops.elem_verify_is_not_zero(&z)?; + q.elem_verify_is_not_zero(&z)?; - let x = ops.point_x(p); - let y = ops.point_y(p); + let x = q.point_x(p); + let y = q.point_y(p); // We are given Jacobian coordinates (x, y, z). So, we have: // @@ -107,12 +99,12 @@ fn verify_jacobian_point_is_on_the_curve( // // y**2 == (x**2 + z**4 * a) * x + (z**6) * b // - let z2 = ops.elem_squared(&z); - let z4 = ops.elem_squared(&z2); - 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, &Elem::from(&ops.b)); - verify_affine_point_is_on_the_curve_scaled(ops, q, (&x, &y), &z4_a, &z6_b)?; + let z2 = q.elem_squared(&z); + let z4 = q.elem_squared(&z2); + let z4_a = q.elem_product(&z4, &Elem::from(q.a())); + let z6 = q.elem_product(&z4, &z2); + let z6_b = q.elem_product(&z6, &Elem::from(q.b())); + verify_affine_point_is_on_the_curve_scaled(q, (&x, &y), &z4_a, &z6_b)?; Ok(z2) } @@ -142,20 +134,19 @@ fn verify_jacobian_point_is_on_the_curve( // Elliptic Curve Cryptosystems" by Johannes Blömer, Martin Otto, and // Jean-Pierre Seifert. fn verify_affine_point_is_on_the_curve_scaled( - ops: &CommonOps, q: &Modulus, (x, y): (&Elem, &Elem), a_scaled: &Elem, b_scaled: &Elem, ) -> Result<(), error::Unspecified> { - let lhs = ops.elem_squared(y); + let lhs = q.elem_squared(y); - let mut rhs = ops.elem_squared(x); - q.elem_add(&mut rhs, a_scaled); - ops.elem_mul(&mut rhs, x); - q.elem_add(&mut rhs, b_scaled); + let mut rhs = q.elem_squared(x); + q.add_assign(&mut rhs, a_scaled); + q.elem_mul(&mut rhs, x); + q.add_assign(&mut rhs, b_scaled); - if !ops.elems_are_equal(&lhs, &rhs).leak() { + if !q.elems_are_equal(&lhs, &rhs).leak() { return Err(error::Unspecified); } diff --git a/src/ec/suite_b/curve.rs b/src/ec/suite_b/curve.rs index dc4aedbc0..28314c240 100644 --- a/src/ec/suite_b/curve.rs +++ b/src/ec/suite_b/curve.rs @@ -41,28 +41,32 @@ macro_rules! suite_b_curve { public_from_private: $public_from_private, }; - fn $check_private_key_bytes(bytes: &[u8]) -> Result<(), error::Unspecified> { + fn $check_private_key_bytes( + bytes: &[u8], + cpu: cpu::Features, + ) -> Result<(), error::Unspecified> { debug_assert_eq!(bytes.len(), $bits / 8); - ec::suite_b::private_key::check_scalar_big_endian_bytes($private_key_ops, bytes) + ec::suite_b::private_key::check_scalar_big_endian_bytes($private_key_ops, bytes, cpu) } fn $generate_private_key( rng: &dyn rand::SecureRandom, out: &mut [u8], + cpu: cpu::Features, ) -> Result<(), error::Unspecified> { - ec::suite_b::private_key::generate_private_scalar_bytes($private_key_ops, rng, out) + ec::suite_b::private_key::generate_private_scalar_bytes($private_key_ops, rng, out, cpu) } fn $public_from_private( public_out: &mut [u8], private_key: &ec::Seed, - cpu_features: cpu::Features, + cpu: cpu::Features, ) -> Result<(), error::Unspecified> { ec::suite_b::private_key::public_from_private( $private_key_ops, public_out, private_key, - cpu_features, + cpu, ) } }; diff --git a/src/ec/suite_b/ecdh.rs b/src/ec/suite_b/ecdh.rs index d90e9d760..a93008d29 100644 --- a/src/ec/suite_b/ecdh.rs +++ b/src/ec/suite_b/ecdh.rs @@ -93,7 +93,7 @@ fn ecdh( // The "NSA Guide" steps are from section 3.1 of the NSA guide, "Ephemeral // Unified Model." - let q = &public_key_ops.common.elem_modulus(); + let q = &public_key_ops.common.elem_modulus(cpu); // NSA Guide Step 1 is handled separately. @@ -103,7 +103,7 @@ fn ecdh( // `parse_uncompressed_point` verifies that the point is not at infinity // and that it is on the curve, using the Partial Public-Key Validation // Routine. - let peer_public_key = parse_uncompressed_point(public_key_ops, q, peer_public_key, cpu)?; + let peer_public_key = parse_uncompressed_point(public_key_ops, q, peer_public_key)?; // NIST SP 800-56Ar2 Step 1. // NSA Guide Step 3 (except point at infinity check). @@ -125,7 +125,7 @@ fn ecdh( // information about their values can be recovered. This doesn't meet the // NSA guide's explicit requirement to "zeroize" them though. // TODO: this only needs common scalar ops - let n = &private_key_ops.common.scalar_modulus(); + let n = &private_key_ops.common.scalar_modulus(cpu); let my_private_key = private_key_as_scalar(n, my_private_key); let product = private_key_ops.point_mul(&my_private_key, &peer_public_key, cpu); @@ -137,7 +137,7 @@ fn ecdh( // `big_endian_affine_from_jacobian` verifies that the result is not at // infinity and also does an extra check to verify that the point is on // the curve. - big_endian_affine_from_jacobian(private_key_ops, q, out, None, &product, cpu) + big_endian_affine_from_jacobian(private_key_ops, q, out, None, &product) // NSA Guide Step 5 & 6 are deferred to the caller. Again, we have a // pretty liberal interpretation of the NIST's spec's "Destroy" that diff --git a/src/ec/suite_b/ecdsa/digest_scalar.rs b/src/ec/suite_b/ecdsa/digest_scalar.rs index 0a66ba336..224c866e4 100644 --- a/src/ec/suite_b/ecdsa/digest_scalar.rs +++ b/src/ec/suite_b/ecdsa/digest_scalar.rs @@ -68,10 +68,11 @@ fn digest_scalar_(n: &Modulus, digest: &[u8]) -> Scalar { #[cfg(test)] mod tests { use super::digest_bytes_scalar; - use crate::{digest, ec::suite_b::ops::*, limb, test}; + use crate::{cpu, digest, ec::suite_b::ops::*, limb, test}; #[test] fn test() { + let cpu = cpu::features(); test::run( test_file!("ecdsa_digest_scalar_tests.txt"), |section, test_case| { @@ -91,7 +92,7 @@ mod tests { panic!("Unsupported curve+digest: {}+{}", curve_name, digest_name); } }; - let n = &ops.scalar_ops.scalar_modulus(); + let n = &ops.scalar_ops.scalar_modulus(cpu); assert_eq!(input.len(), digest_alg.output_len()); assert_eq!(output.len(), ops.scalar_ops.scalar_bytes_len()); diff --git a/src/ec/suite_b/ecdsa/signing.rs b/src/ec/suite_b/ecdsa/signing.rs index fbbf592bf..90ecd386c 100644 --- a/src/ec/suite_b/ecdsa/signing.rs +++ b/src/ec/suite_b/ecdsa/signing.rs @@ -156,7 +156,7 @@ impl EcdsaKeyPair { let cpu = cpu::features(); let (seed, public_key) = key_pair.split(); - let n = &alg.private_scalar_ops.scalar_ops.scalar_modulus(); + let n = &alg.private_scalar_ops.scalar_ops.scalar_modulus(cpu); let d = private_key::private_key_as_scalar(n, &seed); let d = alg.private_scalar_ops.to_mont(&d, cpu); @@ -240,8 +240,8 @@ impl EcdsaKeyPair { let scalar_ops = ops.scalar_ops; let cops = scalar_ops.common; let private_key_ops = self.alg.private_key_ops; - let q = &cops.elem_modulus(); - let n = &scalar_ops.scalar_modulus(); + let q = &cops.elem_modulus(cpu); + let n = &scalar_ops.scalar_modulus(cpu); for _ in 0..100 { // XXX: iteration conut? @@ -254,11 +254,11 @@ impl EcdsaKeyPair { // Step 3. let r = { - let (x, _) = private_key::affine_from_jacobian(private_key_ops, q, &r, cpu)?; - let x = cops.elem_unencoded(&x); + let (x, _) = private_key::affine_from_jacobian(private_key_ops, q, &r)?; + let x = q.elem_unencoded(&x); n.elem_reduced_to_scalar(&x) }; - if cops.is_zero(&r) { + if n.is_zero(&r) { continue; } @@ -270,7 +270,7 @@ impl EcdsaKeyPair { // Step 6. let s = { let mut e_plus_dr = scalar_ops.scalar_product(&self.d, &r, cpu); - n.elem_add(&mut e_plus_dr, &e); + n.add_assign(&mut e_plus_dr, &e); scalar_ops.scalar_product(&k_inv, &e_plus_dr, cpu) }; if cops.is_zero(&s) { diff --git a/src/ec/suite_b/ecdsa/verification.rs b/src/ec/suite_b/ecdsa/verification.rs index 8cf7c5080..b867a2930 100644 --- a/src/ec/suite_b/ecdsa/verification.rs +++ b/src/ec/suite_b/ecdsa/verification.rs @@ -56,6 +56,7 @@ impl signature::VerificationAlgorithm for EcdsaVerificationAlgorithm { msg: untrusted::Input, signature: untrusted::Input, ) -> Result<(), error::Unspecified> { + let cpu = cpu::features(); let e = { // NSA Guide Step 2: "Use the selected hash function to compute H = // Hash(M)." @@ -63,7 +64,7 @@ impl signature::VerificationAlgorithm for EcdsaVerificationAlgorithm { // NSA Guide Step 3: "Convert the bit string H to an integer e as // described in Appendix B.2." - let n = &self.ops.scalar_ops.scalar_modulus(); + let n = &self.ops.scalar_ops.scalar_modulus(cpu); digest_scalar(n, h) }; @@ -85,8 +86,8 @@ impl EcdsaVerificationAlgorithm { let public_key_ops = self.ops.public_key_ops; let scalar_ops = self.ops.scalar_ops; - let q = &public_key_ops.common.elem_modulus(); - let n = &scalar_ops.scalar_modulus(); + let q = &public_key_ops.common.elem_modulus(cpu); + let n = &scalar_ops.scalar_modulus(cpu); // NSA Guide Prerequisites: // @@ -105,7 +106,7 @@ impl EcdsaVerificationAlgorithm { // can do. Prerequisite #2 is handled implicitly as the domain // parameters are hard-coded into the source. Prerequisite #3 is // handled by `parse_uncompressed_point`. - let peer_pub_key = parse_uncompressed_point(public_key_ops, q, public_key, cpu)?; + let peer_pub_key = parse_uncompressed_point(public_key_ops, q, public_key)?; let (r, s) = signature.read_all(error::Unspecified, |input| { (self.split_rs)(scalar_ops, input) @@ -137,7 +138,7 @@ impl EcdsaVerificationAlgorithm { // `verify_affine_point_is_on_the_curve_scaled` for details on why). // But, we're going to avoid converting to affine for performance // reasons, so we do the verification using the Jacobian coordinates. - let z2 = verify_jacobian_point_is_on_the_curve(public_key_ops.common, q, &product)?; + let z2 = verify_jacobian_point_is_on_the_curve(q, &product)?; // NSA Guide Step 7: "Compute v = xR mod n." // NSA Guide Step 8: "Compare v and r0. If v = r0, output VALID; @@ -145,26 +146,20 @@ impl EcdsaVerificationAlgorithm { // // Instead, we use Greg Maxwell's trick to avoid the inversion mod `q` // that would be necessary to compute the affine X coordinate. - let x = public_key_ops.common.point_x(&product); - fn sig_r_equals_x( - ops: &PublicScalarOps, - r: &Elem, - x: &Elem, - z2: &Elem, - ) -> bool { - let cops = ops.public_key_ops.common; - let r_jacobian = cops.elem_product(z2, r); - let x = cops.elem_unencoded(x); - ops.elem_equals_vartime(&r_jacobian, &x) + let x = q.point_x(&product); + fn sig_r_equals_x(q: &Modulus, r: &Elem, x: &Elem, z2: &Elem) -> bool { + let r_jacobian = q.elem_product(z2, r); + let x = q.elem_unencoded(x); + q.elem_equals_vartime(&r_jacobian, &x) } let mut r = self.ops.scalar_as_elem(&r); - if sig_r_equals_x(self.ops, &r, &x, &z2) { + if sig_r_equals_x(q, &r, &x, &z2) { return Ok(()); } if q.elem_less_than_vartime(&r, &self.ops.q_minus_n) { let n = Elem::from(self.ops.n()); - q.elem_add(&mut r, &n); - if sig_r_equals_x(self.ops, &r, &x, &z2) { + q.add_assign(&mut r, &n); + if sig_r_equals_x(q, &r, &x, &z2) { return Ok(()); } } @@ -287,6 +282,7 @@ mod tests { #[test] fn test_digest_based_test_vectors() { + let cpu = cpu::features(); test::run( test_file!("../../../../crypto/fipsmodule/ecdsa/ecdsa_verify_tests.txt"), |section, test_case| { @@ -319,7 +315,7 @@ mod tests { panic!("Unsupported curve: {}", curve_name); } }; - let n = &alg.ops.scalar_ops.scalar_modulus(); + let n = &alg.ops.scalar_ops.scalar_modulus(cpu); let digest = super::super::digest_scalar::digest_bytes_scalar(n, &digest[..]); let actual_result = alg.verify_digest( diff --git a/src/ec/suite_b/ops.rs b/src/ec/suite_b/ops.rs index 359eea281..2f10fcc3f 100644 --- a/src/ec/suite_b/ops.rs +++ b/src/ec/suite_b/ops.rs @@ -42,7 +42,9 @@ pub(super) struct Modulus { // TODO: [Limb; elem::NumLimbs::MAX] limbs: &'static [Limb; elem::NumLimbs::MAX], num_limbs: elem::NumLimbs, + cops: &'static CommonOps, m: PhantomData, + cpu: cpu::Features, } pub struct Point { @@ -79,21 +81,25 @@ pub struct CommonOps { } impl CommonOps { - pub(super) fn elem_modulus(&'static self) -> Modulus { + pub(super) fn elem_modulus(&'static self, cpu_features: cpu::Features) -> Modulus { Modulus { // TODO: limbs: self.q.p.map(Limb::from), limbs: &self.q.p, num_limbs: self.num_limbs, + cops: self, m: PhantomData, + cpu: cpu_features, } } - pub(super) fn scalar_modulus(&'static self) -> Modulus { + pub(super) fn scalar_modulus(&'static self, cpu_features: cpu::Features) -> Modulus { Modulus { // TODO: limbs: self.n.limbs.map(Limb::from), limbs: &self.n.limbs, num_limbs: self.num_limbs, + cops: self, m: PhantomData, + cpu: cpu_features, } } @@ -111,13 +117,19 @@ impl CommonOps { } impl Modulus { + pub fn cpu(&self) -> cpu::Features { + self.cpu + } + // Keep in sync with `CommonOps::len()`. pub fn bytes_len(&self) -> usize { self.num_limbs.into() * LIMB_BYTES } +} +impl Modulus { #[inline] - pub fn elem_add(&self, a: &mut elem::Elem, b: &elem::Elem) { + pub fn add_assign(&self, a: &mut elem::Elem, b: &elem::Elem) { let num_limbs = self.num_limbs.into(); limbs_add_assign_mod( &mut a.limbs[..num_limbs], @@ -127,7 +139,7 @@ impl Modulus { } } -impl CommonOps { +impl Modulus { #[inline] pub fn elems_are_equal(&self, a: &Elem, b: &Elem) -> LimbMask { let num_limbs = self.num_limbs.into(); @@ -138,17 +150,26 @@ impl CommonOps { pub fn elem_unencoded(&self, a: &Elem) -> Elem { self.elem_product(a, &Elem::one()) } +} + +impl CommonOps { + #[inline] + pub fn is_zero(&self, a: &elem::Elem) -> bool { + let num_limbs = self.num_limbs.into(); + limbs_are_zero_constant_time(&a.limbs[..num_limbs]).leak() + } #[inline] - pub fn elem_mul(&self, a: &mut Elem, b: &Elem) { + fn elem_mul(&self, a: &mut Elem, b: &Elem, _cpu: cpu::Features) { elem::binary_op_assign(self.elem_mul_mont, a, b) } #[inline] - pub fn elem_product( + fn elem_product( &self, a: &Elem, b: &Elem, + _cpu: cpu::Features, ) -> Elem<<(EA, EB) as ProductEncoding>::Output> where (EA, EB): ProductEncoding, @@ -157,21 +178,53 @@ impl CommonOps { } #[inline] - pub fn elem_square(&self, a: &mut Elem) { + fn elem_square(&self, a: &mut Elem, _cpu: cpu::Features) { unary_op_assign(self.elem_sqr_mont, a); } #[inline] - pub fn elem_squared(&self, a: &Elem) -> Elem { + fn elem_squared(&self, a: &Elem, _cpu: cpu::Features) -> Elem { unary_op(self.elem_sqr_mont, a) } +} +impl Modulus { #[inline] - pub fn is_zero(&self, a: &elem::Elem) -> bool { - let num_limbs = self.num_limbs.into(); - limbs_are_zero_constant_time(&a.limbs[..num_limbs]).leak() + pub fn elem_mul(&self, a: &mut Elem, b: &Elem) { + self.cops.elem_mul(a, b, self.cpu) } + #[inline] + pub fn elem_product( + &self, + a: &Elem, + b: &Elem, + ) -> Elem<<(EA, EB) as ProductEncoding>::Output> + where + (EA, EB): ProductEncoding, + { + self.cops.elem_product(a, b, self.cpu) + } + + #[inline] + pub fn elem_square(&self, a: &mut Elem) { + self.cops.elem_square(a, self.cpu) + } + + #[inline] + pub fn elem_squared(&self, a: &Elem) -> Elem { + self.cops.elem_squared(a, self.cpu) + } +} + +impl Modulus { + #[inline] + pub fn is_zero(&self, a: &elem::Elem) -> bool { + self.cops.is_zero(a) + } +} + +impl Modulus { pub fn elem_verify_is_not_zero(&self, a: &Elem) -> Result<(), error::Unspecified> { if self.is_zero(a) { Err(error::Unspecified) @@ -180,6 +233,15 @@ impl CommonOps { } } + pub(super) fn a(&self) -> &'static PublicElem { + &self.cops.a + } + pub(super) fn b(&self) -> &'static PublicElem { + &self.cops.b + } +} + +impl CommonOps { pub(super) fn point_sum(&self, a: &Point, b: &Point, _cpu: cpu::Features) -> Point { let mut r = Point::new_at_infinity(); unsafe { @@ -187,7 +249,9 @@ impl CommonOps { } r } +} +impl Modulus { pub fn point_x(&self, p: &Point) -> Elem { let num_limbs = self.num_limbs.into(); let mut r = Elem::zero(); @@ -218,7 +282,7 @@ struct PublicModulus { /// Operations on private keys, for ECDH and ECDSA signing. pub struct PrivateKeyOps { pub common: &'static CommonOps, - elem_inv_squared: fn(a: &Elem, cpu: cpu::Features) -> Elem, + elem_inv_squared: fn(q: &Modulus, a: &Elem) -> Elem, point_mul_base_impl: fn(a: &Scalar, cpu: cpu::Features) -> Point, point_mul_impl: unsafe extern "C" fn( r: *mut Limb, // [3][num_limbs] @@ -258,8 +322,8 @@ impl PrivateKeyOps { } #[inline] - pub(super) fn elem_inverse_squared(&self, a: &Elem, cpu: cpu::Features) -> Elem { - (self.elem_inv_squared)(a, cpu) + pub(super) fn elem_inverse_squared(&self, q: &Modulus, a: &Elem) -> Elem { + (self.elem_inv_squared)(q, a) } } @@ -279,7 +343,6 @@ impl PublicKeyOps { &self, q: &Modulus, input: &mut untrusted::Reader, - _cpu: cpu::Features, ) -> Result, error::Unspecified> { let _cpu = cpu::features(); let encoded_value = input.read_bytes(self.common.len())?; @@ -308,8 +371,8 @@ pub struct ScalarOps { } impl ScalarOps { - pub(super) fn scalar_modulus(&'static self) -> Modulus { - self.common.scalar_modulus() + pub(super) fn scalar_modulus(&'static self, cpu_features: cpu::Features) -> Modulus { + self.common.scalar_modulus(cpu_features) } // The (maximum) length of a scalar, not including any padding. @@ -365,14 +428,14 @@ impl PublicScalarOps { encoding: PhantomData, } } +} +impl Modulus { pub fn elem_equals_vartime(&self, a: &Elem, b: &Elem) -> bool { - let num_limbs = self.public_key_ops.common.num_limbs.into(); + let num_limbs = self.num_limbs.into(); limbs_equal_limbs_consttime(&a.limbs[..num_limbs], &b.limbs[..num_limbs]).leak() } -} -impl Modulus { pub fn elem_less_than_vartime(&self, a: &Elem, b: &PublicElem) -> bool { let num_limbs = self.num_limbs.into(); // TODO: let b = Elem::from(b); @@ -441,22 +504,34 @@ impl Modulus { } // Returns (`a` squared `squarings` times) * `b`. -fn elem_sqr_mul(ops: &CommonOps, a: &Elem, squarings: LeakyWord, b: &Elem) -> Elem { +fn elem_sqr_mul( + ops: &CommonOps, + a: &Elem, + squarings: LeakyWord, + b: &Elem, + cpu: cpu::Features, +) -> Elem { debug_assert!(squarings >= 1); - let mut tmp = ops.elem_squared(a); + let mut tmp = ops.elem_squared(a, cpu); for _ in 1..squarings { - ops.elem_square(&mut tmp); + ops.elem_square(&mut tmp, cpu); } - ops.elem_product(&tmp, b) + ops.elem_product(&tmp, b, cpu) } // Sets `acc` = (`acc` squared `squarings` times) * `b`. -fn elem_sqr_mul_acc(ops: &CommonOps, acc: &mut Elem, squarings: LeakyWord, b: &Elem) { +fn elem_sqr_mul_acc( + ops: &CommonOps, + acc: &mut Elem, + squarings: LeakyWord, + b: &Elem, + cpu: cpu::Features, +) { debug_assert!(squarings >= 1); for _ in 0..squarings { - ops.elem_square(acc); + ops.elem_square(acc, cpu); } - ops.elem_mul(acc, b) + ops.elem_mul(acc, b, cpu) } #[inline] @@ -540,26 +615,26 @@ mod tests { }; trait Convert { - fn convert(self, cops: &CommonOps) -> Elem; + fn convert(self, q: &Modulus) -> Elem; } impl Convert for Elem { - fn convert(self, _cops: &CommonOps) -> Elem { + fn convert(self, _q: &Modulus) -> Elem { self } } impl Convert for Elem { - fn convert(self, cops: &CommonOps) -> Elem { - cops.elem_unencoded(&self) + fn convert(self, q: &Modulus) -> Elem { + q.elem_unencoded(&self) } } fn q_minus_n_plus_n_equals_0_test(ops: &PublicScalarOps) { let cops = ops.scalar_ops.common; - let q = &cops.elem_modulus(); + let q = &cops.elem_modulus(cpu::features()); let mut x = Elem::from(&ops.q_minus_n); - q.elem_add(&mut x, &Elem::from(&cops.n)); + q.add_assign(&mut x, &Elem::from(&cops.n)); assert!(cops.is_zero(&x)); } @@ -591,7 +666,7 @@ mod tests { fn elem_add_test(ops: &PublicScalarOps, test_file: test::File) { let cops = ops.public_key_ops.common; - let q = &cops.elem_modulus(); + let q = &cops.elem_modulus(cpu::features()); test::run(test_file, |section, test_case| { assert_eq!(section, ""); @@ -600,11 +675,11 @@ mod tests { let expected_sum = consume_elem(q, test_case, "r"); let mut actual_sum = a; - q.elem_add(&mut actual_sum, &b); + q.add_assign(&mut actual_sum, &b); assert_limbs_are_equal(cops, &actual_sum.limbs, &expected_sum.limbs); let mut actual_sum = b; - q.elem_add(&mut actual_sum, &a); + q.add_assign(&mut actual_sum, &a); assert_limbs_are_equal(cops, &actual_sum.limbs, &expected_sum.limbs); Ok(()) @@ -631,7 +706,7 @@ mod tests { elem_sub: unsafe extern "C" fn(r: *mut Limb, a: *const Limb, b: *const Limb), test_file: test::File, ) { - let q = &ops.elem_modulus(); + let q = &ops.elem_modulus(cpu::features()); test::run(test_file, |section, test_case| { assert_eq!(section, ""); @@ -683,7 +758,7 @@ mod tests { elem_div_by_2: unsafe extern "C" fn(r: *mut Limb, a: *const Limb), test_file: test::File, ) { - let q = &ops.elem_modulus(); + let q = &ops.elem_modulus(cpu::features()); test::run(test_file, |section, test_case| { assert_eq!(section, ""); @@ -731,7 +806,7 @@ mod tests { elem_neg: unsafe extern "C" fn(r: *mut Limb, a: *const Limb), test_file: test::File, ) { - let q = &ops.elem_modulus(); + let q = &ops.elem_modulus(cpu::features()); test::run(test_file, |section, test_case| { assert_eq!(section, ""); @@ -771,14 +846,14 @@ mod tests { } fn elem_mul_test(ops: &'static CommonOps, test_file: test::File) { - let q = &ops.elem_modulus(); + let q = &ops.elem_modulus(cpu::features()); test::run(test_file, |section, test_case| { assert_eq!(section, ""); let mut a = consume_elem(q, test_case, "a"); let b = consume_elem(q, test_case, "b"); let r = consume_elem(q, test_case, "r"); - ops.elem_mul(&mut a, &b); + q.elem_mul(&mut a, &b); assert_limbs_are_equal(ops, &a.limbs, &r.limbs); Ok(()) @@ -804,7 +879,7 @@ mod tests { fn scalar_mul_test(ops: &ScalarOps, test_file: test::File) { let cpu = cpu::features(); let cops = ops.common; - let n = &cops.scalar_modulus(); + let n = &cops.scalar_modulus(cpu); test::run(test_file, |section, test_case| { assert_eq!(section, ""); let a = consume_scalar(n, test_case, "a"); @@ -837,8 +912,9 @@ mod tests { sqr_rep: unsafe extern "C" fn(r: *mut Limb, a: *const Limb, rep: LeakyWord), test_file: test::File, ) { + let cpu = cpu::features(); let cops = ops.common; - let n = &cops.scalar_modulus(); + let n = &cops.scalar_modulus(cpu); test::run(test_file, |section, test_case| { assert_eq!(section, ""); let cpu = cpu::features(); @@ -1056,8 +1132,8 @@ mod tests { ) { let cpu = cpu::features(); let cops = pub_ops.common; - let q = &cops.elem_modulus(); - let n = &cops.scalar_modulus(); + let q = &cops.elem_modulus(cpu); + let n = &cops.scalar_modulus(cpu); test::run(test_file, |section, test_case| { assert_eq!(section, ""); let p_scalar = consume_scalar(n, test_case, "p_scalar"); @@ -1067,7 +1143,6 @@ mod tests { pub_ops, q, untrusted::Input::from(&p), - cpu, ) .expect("valid point"); @@ -1084,7 +1159,6 @@ mod tests { x, Some(y), &product, - cpu, ) .expect("successful encoding"); } @@ -1119,7 +1193,7 @@ mod tests { test_file: test::File, ) { let cpu = cpu::features(); - let n = &ops.common.scalar_modulus(); + let n = &ops.common.scalar_modulus(cpu); test::run(test_file, |section, test_case| { assert_eq!(section, ""); let g_scalar = consume_scalar(n, test_case, "g_scalar"); @@ -1140,28 +1214,29 @@ mod tests { let cpu = cpu::features(); let cops = ops.common; - let actual_x = &cops.point_x(actual_point); - let actual_y = &cops.point_y(actual_point); - let actual_z = &cops.point_z(actual_point); + let q = &cops.elem_modulus(cpu); + let actual_x = &q.point_x(actual_point); + let actual_y = &q.point_y(actual_point); + let actual_z = &q.point_z(actual_point); match expected_point { TestPoint::Infinity => { let zero = Elem::zero(); - assert_elems_are_equal(cops, actual_z, &zero); + assert_elems_are_equal(q, actual_z, &zero); } TestPoint::Affine(expected_x, expected_y) => { - let zz_inv = ops.elem_inverse_squared(actual_z, cpu); - let x_aff = cops.elem_product(actual_x, &zz_inv); + let zz_inv = ops.elem_inverse_squared(q, actual_z); + let x_aff = q.elem_product(actual_x, &zz_inv); let y_aff = { - let zzzz_inv = cops.elem_squared(&zz_inv); - let zzz_inv = cops.elem_product(actual_z, &zzzz_inv); - cops.elem_product(actual_y, &zzz_inv) + let zzzz_inv = q.elem_squared(&zz_inv); + let zzz_inv = q.elem_product(actual_z, &zzzz_inv); + q.elem_product(actual_y, &zzz_inv) }; - let x_aff = x_aff.convert(cops); - let y_aff = y_aff.convert(cops); + let x_aff = x_aff.convert(q); + let y_aff = y_aff.convert(q); - assert_elems_are_equal(cops, &x_aff, expected_x); - assert_elems_are_equal(cops, &y_aff, expected_y); + assert_elems_are_equal(q, &x_aff, expected_x); + assert_elems_are_equal(q, &y_aff, expected_y); } } } @@ -1171,7 +1246,7 @@ mod tests { test_case: &mut test::TestCase, name: &str, ) -> Point { - let q = &ops.common.elem_modulus(); + let q = &ops.common.elem_modulus(cpu::features()); let input = test_case.consume_string(name); let elems = input.split(", ").collect::>(); assert_eq!(elems.len(), 3); @@ -1191,7 +1266,7 @@ mod tests { test_case: &mut test::TestCase, name: &str, ) -> AffinePoint { - let q = &ops.common.elem_modulus(); + let q = &ops.common.elem_modulus(cpu::features()); let input = test_case.consume_string(name); let elems = input.split(", ").collect::>(); assert_eq!(elems.len(), 2); @@ -1222,7 +1297,7 @@ mod tests { test_case: &mut test::TestCase, name: &str, ) -> TestPoint { - let q = &ops.common.elem_modulus(); + let q = &ops.common.elem_modulus(cpu::features()); fn consume_point_elem(q: &Modulus, elems: &[&str], i: usize) -> Elem { let bytes = test::from_hex(elems[i]).unwrap(); let bytes = untrusted::Input::from(&bytes); @@ -1247,8 +1322,8 @@ mod tests { TestPoint::Affine(x, y) } - fn assert_elems_are_equal(ops: &CommonOps, a: &Elem, b: &Elem) { - assert_limbs_are_equal(ops, &a.limbs, &b.limbs) + fn assert_elems_are_equal(q: &Modulus, a: &Elem, b: &Elem) { + assert_limbs_are_equal(q.cops, &a.limbs, &b.limbs) } fn assert_limbs_are_equal( diff --git a/src/ec/suite_b/ops/p256.rs b/src/ec/suite_b/ops/p256.rs index 4648a1e0c..20ede07cc 100644 --- a/src/ec/suite_b/ops/p256.rs +++ b/src/ec/suite_b/ops/p256.rs @@ -50,7 +50,7 @@ pub static PRIVATE_KEY_OPS: PrivateKeyOps = PrivateKeyOps { point_mul_impl: p256_point_mul, }; -fn p256_elem_inv_squared(a: &Elem, _cpu: cpu::Features) -> Elem { +fn p256_elem_inv_squared(q: &Modulus, a: &Elem) -> Elem { // Calculate a**-2 (mod q) == a**(q - 3) (mod q) // // The exponent (q - 3) is: @@ -58,39 +58,39 @@ fn p256_elem_inv_squared(a: &Elem, _cpu: cpu::Features) -> Elem { // 0xffffffff00000001000000000000000000000000fffffffffffffffffffffffc #[inline] - fn sqr_mul(a: &Elem, squarings: LeakyWord, b: &Elem) -> Elem { - elem_sqr_mul(&COMMON_OPS, a, squarings, b) + fn sqr_mul(q: &Modulus, a: &Elem, squarings: LeakyWord, b: &Elem) -> Elem { + elem_sqr_mul(&COMMON_OPS, a, squarings, b, q.cpu()) } #[inline] - fn sqr_mul_acc(a: &mut Elem, squarings: LeakyWord, b: &Elem) { - elem_sqr_mul_acc(&COMMON_OPS, a, squarings, b) + fn sqr_mul_acc(q: &Modulus, a: &mut Elem, squarings: LeakyWord, b: &Elem) { + elem_sqr_mul_acc(&COMMON_OPS, a, squarings, b, q.cpu()) } let b_1 = &a; - let b_11 = sqr_mul(b_1, 1, b_1); - let b_111 = sqr_mul(&b_11, 1, b_1); - let f_11 = sqr_mul(&b_111, 3, &b_111); - let fff = sqr_mul(&f_11, 6, &f_11); - let fff_111 = sqr_mul(&fff, 3, &b_111); - let fffffff_11 = sqr_mul(&fff_111, 15, &fff_111); - let ffffffff = sqr_mul(&fffffff_11, 2, &b_11); + let b_11 = sqr_mul(q, b_1, 1, b_1); + let b_111 = sqr_mul(q, &b_11, 1, b_1); + let f_11 = sqr_mul(q, &b_111, 3, &b_111); + let fff = sqr_mul(q, &f_11, 6, &f_11); + let fff_111 = sqr_mul(q, &fff, 3, &b_111); + let fffffff_11 = sqr_mul(q, &fff_111, 15, &fff_111); + let ffffffff = sqr_mul(q, &fffffff_11, 2, &b_11); // ffffffff00000001 - let mut acc = sqr_mul(&ffffffff, 31 + 1, b_1); + let mut acc = sqr_mul(q, &ffffffff, 31 + 1, b_1); // ffffffff00000001000000000000000000000000ffffffff - sqr_mul_acc(&mut acc, 96 + 32, &ffffffff); + sqr_mul_acc(q, &mut acc, 96 + 32, &ffffffff); // ffffffff00000001000000000000000000000000ffffffffffffffff - sqr_mul_acc(&mut acc, 32, &ffffffff); + sqr_mul_acc(q, &mut acc, 32, &ffffffff); // ffffffff00000001000000000000000000000000fffffffffffffffffffffff_11 - sqr_mul_acc(&mut acc, 30, &fffffff_11); + sqr_mul_acc(q, &mut acc, 30, &fffffff_11); // ffffffff00000001000000000000000000000000fffffffffffffffffffffffc - COMMON_OPS.elem_square(&mut acc); - COMMON_OPS.elem_square(&mut acc); + q.elem_square(&mut acc); + q.elem_square(&mut acc); acc } diff --git a/src/ec/suite_b/ops/p384.rs b/src/ec/suite_b/ops/p384.rs index d7d7b68d4..c4ab61a49 100644 --- a/src/ec/suite_b/ops/p384.rs +++ b/src/ec/suite_b/ops/p384.rs @@ -49,7 +49,7 @@ pub static PRIVATE_KEY_OPS: PrivateKeyOps = PrivateKeyOps { point_mul_impl: p384_point_mul, }; -fn p384_elem_inv_squared(a: &Elem, _cpu: cpu::Features) -> Elem { +fn p384_elem_inv_squared(q: &Modulus, a: &Elem) -> Elem { // Calculate a**-2 (mod q) == a**(q - 3) (mod q) // // The exponent (q - 3) is: @@ -58,49 +58,50 @@ fn p384_elem_inv_squared(a: &Elem, _cpu: cpu::Features) -> Elem { // ffffffff0000000000000000fffffffc #[inline] - fn sqr_mul(a: &Elem, squarings: LeakyWord, b: &Elem) -> Elem { - elem_sqr_mul(&COMMON_OPS, a, squarings, b) + fn sqr_mul(q: &Modulus, a: &Elem, squarings: LeakyWord, b: &Elem) -> Elem { + elem_sqr_mul(&COMMON_OPS, a, squarings, b, q.cpu()) } #[inline] - fn sqr_mul_acc(a: &mut Elem, squarings: LeakyWord, b: &Elem) { - elem_sqr_mul_acc(&COMMON_OPS, a, squarings, b) + fn sqr_mul_acc(q: &Modulus, a: &mut Elem, squarings: LeakyWord, b: &Elem) { + elem_sqr_mul_acc(&COMMON_OPS, a, squarings, b, q.cpu()) } let b_1 = &a; - let b_11 = sqr_mul(b_1, 1, b_1); - let b_111 = sqr_mul(&b_11, 1, b_1); - let f_11 = sqr_mul(&b_111, 3, &b_111); - let fff = sqr_mul(&f_11, 6, &f_11); - let fff_111 = sqr_mul(&fff, 3, &b_111); - let fffffff_11 = sqr_mul(&fff_111, 15, &fff_111); + let b_11 = sqr_mul(q, b_1, 1, b_1); + let b_111 = sqr_mul(q, &b_11, 1, b_1); + let f_11 = sqr_mul(q, &b_111, 3, &b_111); + let fff = sqr_mul(q, &f_11, 6, &f_11); + let fff_111 = sqr_mul(q, &fff, 3, &b_111); + let fffffff_11 = sqr_mul(q, &fff_111, 15, &fff_111); - let fffffffffffffff = sqr_mul(&fffffff_11, 30, &fffffff_11); + let fffffffffffffff = sqr_mul(q, &fffffff_11, 30, &fffffff_11); - let ffffffffffffffffffffffffffffff = sqr_mul(&fffffffffffffff, 60, &fffffffffffffff); + let ffffffffffffffffffffffffffffff = sqr_mul(q, &fffffffffffffff, 60, &fffffffffffffff); // ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff let mut acc = sqr_mul( + q, &ffffffffffffffffffffffffffffff, 120, &ffffffffffffffffffffffffffffff, ); // fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff_111 - sqr_mul_acc(&mut acc, 15, &fff_111); + sqr_mul_acc(q, &mut acc, 15, &fff_111); // fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff - sqr_mul_acc(&mut acc, 1 + 30, &fffffff_11); - sqr_mul_acc(&mut acc, 2, &b_11); + sqr_mul_acc(q, &mut acc, 1 + 30, &fffffff_11); + sqr_mul_acc(q, &mut acc, 2, &b_11); // fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff // 0000000000000000fffffff_11 - sqr_mul_acc(&mut acc, 64 + 30, &fffffff_11); + sqr_mul_acc(q, &mut acc, 64 + 30, &fffffff_11); // fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff // 0000000000000000fffffffc - COMMON_OPS.elem_square(&mut acc); - COMMON_OPS.elem_square(&mut acc); + q.elem_square(&mut acc); + q.elem_square(&mut acc); acc } diff --git a/src/ec/suite_b/private_key.rs b/src/ec/suite_b/private_key.rs index 05f910774..070270fe0 100644 --- a/src/ec/suite_b/private_key.rs +++ b/src/ec/suite_b/private_key.rs @@ -26,14 +26,15 @@ pub(super) fn random_scalar( ) -> Result { let mut bytes = [0; ec::SCALAR_MAX_BYTES]; let bytes = &mut bytes[..ops.common.len()]; - generate_private_scalar_bytes(ops, rng, bytes)?; + generate_private_scalar_bytes(ops, rng, bytes, n.cpu())?; scalar_from_big_endian_bytes(n, bytes) } -pub fn generate_private_scalar_bytes( +pub(super) fn generate_private_scalar_bytes( ops: &PrivateKeyOps, rng: &dyn rand::SecureRandom, out: &mut [u8], + cpu: cpu::Features, ) -> Result<(), error::Unspecified> { // [NSA Suite B Implementer's Guide to ECDSA] Appendix A.1.2, and // [NSA Suite B Implementer's Guide to NIST SP 800-56A] Appendix B.2, @@ -67,7 +68,7 @@ pub fn generate_private_scalar_bytes( rng.fill(candidate)?; // NSA Guide Steps 5, 6, and 7. - if check_scalar_big_endian_bytes(ops, candidate).is_err() { + if check_scalar_big_endian_bytes(ops, candidate, cpu).is_err() { continue; } @@ -93,9 +94,10 @@ pub(super) fn private_key_as_scalar(n: &Modulus, private_key: &ec::Seed) -> S pub(super) fn check_scalar_big_endian_bytes( ops: &PrivateKeyOps, bytes: &[u8], + cpu: cpu::Features, ) -> Result<(), error::Unspecified> { debug_assert_eq!(bytes.len(), ops.common.len()); - let n = &ops.common.scalar_modulus(); + let n = &ops.common.scalar_modulus(cpu); scalar_from_big_endian_bytes(n, bytes).map(|_| ()) } @@ -132,10 +134,10 @@ pub(super) fn public_from_private( my_private_key: &ec::Seed, cpu: cpu::Features, ) -> Result<(), error::Unspecified> { - let q = &ops.common.elem_modulus(); + let q = &ops.common.elem_modulus(cpu); let elem_and_scalar_bytes = ops.common.len(); debug_assert_eq!(public_out.len(), 1 + (2 * elem_and_scalar_bytes)); - let n = &ops.common.scalar_modulus(); + let n = &ops.common.scalar_modulus(cpu); let my_private_key = private_key_as_scalar(n, my_private_key); let my_public_key = ops.point_mul_base(&my_private_key, cpu); public_out[0] = 4; // Uncompressed encoding. @@ -143,42 +145,41 @@ pub(super) fn public_from_private( // `big_endian_affine_from_jacobian` verifies that the point is not at // infinity and is on the curve. - big_endian_affine_from_jacobian(ops, q, x_out, Some(y_out), &my_public_key, cpu) + big_endian_affine_from_jacobian(ops, q, x_out, Some(y_out), &my_public_key) } pub(super) fn affine_from_jacobian( ops: &PrivateKeyOps, q: &Modulus, p: &Point, - cpu: cpu::Features, ) -> Result<(Elem, Elem), error::Unspecified> { - let z = ops.common.point_z(p); + let z = q.point_z(p); // Since we restrict our private key to the range [1, n), the curve has // prime order, and we verify that the peer's point is on the curve, // there's no way that the result can be at infinity. But, use `assert!` // instead of `debug_assert!` anyway - assert!(ops.common.elem_verify_is_not_zero(&z).is_ok()); + assert!(q.elem_verify_is_not_zero(&z).is_ok()); - let x = ops.common.point_x(p); - let y = ops.common.point_y(p); + let x = q.point_x(p); + let y = q.point_y(p); - let zz_inv = ops.elem_inverse_squared(&z, cpu); + let zz_inv = ops.elem_inverse_squared(q, &z); - let x_aff = ops.common.elem_product(&x, &zz_inv); + let x_aff = q.elem_product(&x, &zz_inv); // `y_aff` is needed to validate the point is on the curve. It is also // needed in the non-ECDH case where we need to output it. let y_aff = { - let zzzz_inv = ops.common.elem_squared(&zz_inv); - let zzz_inv = ops.common.elem_product(&z, &zzzz_inv); - ops.common.elem_product(&y, &zzz_inv) + let zzzz_inv = q.elem_squared(&zz_inv); + let zzz_inv = q.elem_product(&z, &zzzz_inv); + q.elem_product(&y, &zzz_inv) }; // If we validated our inputs correctly and then computed (x, y, z), then // (x, y, z) will be on the curve. See // `verify_affine_point_is_on_the_curve_scaled` for the motivation. - verify_affine_point_is_on_the_curve(ops.common, q, (&x_aff, &y_aff))?; + verify_affine_point_is_on_the_curve(q, (&x_aff, &y_aff))?; Ok((x_aff, y_aff)) } @@ -189,13 +190,12 @@ pub(super) fn big_endian_affine_from_jacobian( x_out: &mut [u8], y_out: Option<&mut [u8]>, p: &Point, - cpu: cpu::Features, ) -> Result<(), error::Unspecified> { - let (x_aff, y_aff) = affine_from_jacobian(ops, q, p, cpu)?; - let x = ops.common.elem_unencoded(&x_aff); + let (x_aff, y_aff) = affine_from_jacobian(ops, q, p)?; + let x = q.elem_unencoded(&x_aff); limb::big_endian_from_limbs(ops.leak_limbs(&x), x_out); if let Some(y_out) = y_out { - let y = ops.common.elem_unencoded(&y_aff); + let y = q.elem_unencoded(&y_aff); limb::big_endian_from_limbs(ops.leak_limbs(&y), y_out); } diff --git a/src/ec/suite_b/public_key.rs b/src/ec/suite_b/public_key.rs index f723ac12c..538ceae2c 100644 --- a/src/ec/suite_b/public_key.rs +++ b/src/ec/suite_b/public_key.rs @@ -16,7 +16,7 @@ //! ECDH agreement). use super::{ops::*, verify_affine_point_is_on_the_curve}; -use crate::{arithmetic::montgomery::*, cpu, error}; +use crate::{arithmetic::montgomery::*, error}; /// Parses a public key encoded in uncompressed form. The key is validated /// using the ECC Partial Public-Key Validation Routine from @@ -30,7 +30,6 @@ pub(super) fn parse_uncompressed_point( ops: &PublicKeyOps, q: &Modulus, input: untrusted::Input, - cpu: cpu::Features, ) -> Result<(Elem, Elem), error::Unspecified> { // NIST SP 800-56A Step 1: "Verify that Q is not the point at infinity. // This can be done by inspection if the point is entered in the standard @@ -45,15 +44,15 @@ pub(super) fn parse_uncompressed_point( // NIST SP 800-56A Step 2: "Verify that xQ and yQ are integers in the // interval [0, p-1] in the case that q is an odd prime p[.]" - let x = ops.elem_parse(q, input, cpu)?; - let y = ops.elem_parse(q, input, cpu)?; + let x = ops.elem_parse(q, input)?; + let y = ops.elem_parse(q, input)?; Ok((x, y)) })?; // NIST SP 800-56A Step 3: "If q is an odd prime p, verify that // yQ**2 = xQ**3 + axQ + b in GF(p), where the arithmetic is performed // modulo p." - verify_affine_point_is_on_the_curve(ops.common, q, (&x, &y))?; + verify_affine_point_is_on_the_curve(q, (&x, &y))?; // NIST SP 800-56A Note: "Since its order is not verified, there is no // check that the public key is in the correct EC subgroup." @@ -69,7 +68,7 @@ pub(super) fn parse_uncompressed_point( #[cfg(test)] mod tests { use super::*; - use crate::test; + use crate::{cpu, test}; #[test] fn parse_uncompressed_point_test() { @@ -86,9 +85,9 @@ mod tests { let is_valid = test_case.consume_string("Result") == "P"; let curve_ops = public_key_ops_from_curve_name(&curve_name); - let q = &curve_ops.common.elem_modulus(); + let q = &curve_ops.common.elem_modulus(cpu); - let result = parse_uncompressed_point(curve_ops, q, public_key, cpu); + let result = parse_uncompressed_point(curve_ops, q, public_key); assert_eq!(is_valid, result.is_ok()); // TODO: Verify that we when we re-serialize the parsed (x, y), the