Skip to content

Commit

Permalink
ec/suite_b: Thread CPU features through functions using Modulus.
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
briansmith committed Dec 10, 2024
1 parent 3518d05 commit aca068c
Show file tree
Hide file tree
Showing 15 changed files with 283 additions and 209 deletions.
8 changes: 4 additions & 4 deletions mk/generate_curves.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@
// %(q_minus_3)s
#[inline]
fn sqr_mul(a: &Elem<R>, squarings: LeakyWord, b: &Elem<R>) -> Elem<R> {
elem_sqr_mul(&COMMON_OPS, a, squarings, b)
fn sqr_mul(q: &Modulus<Q>, a: &Elem<R>, squarings: LeakyWord, b: &Elem<R>) -> Elem<R> {
elem_sqr_mul(&COMMON_OPS, a, squarings, b, q.cpu())
}
#[inline]
fn sqr_mul_acc(a: &mut Elem<R>, squarings: LeakyWord, b: &Elem<R>) {
elem_sqr_mul_acc(&COMMON_OPS, a, squarings, b)
fn sqr_mul_acc(q: &Modulus<Q>, a: &mut Elem<R>, squarings: LeakyWord, b: &Elem<R>) {
elem_sqr_mul_acc(&COMMON_OPS, a, squarings, b, q.cpu())
}
let b_1 = &a;
Expand Down
9 changes: 6 additions & 3 deletions src/ec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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],
Expand Down
6 changes: 5 additions & 1 deletion src/ec/curve25519/x25519.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,18 @@ 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(())
}

fn x25519_generate_private_key(
rng: &dyn rand::SecureRandom,
out: &mut [u8],
_: cpu::Features,
) -> Result<(), error::Unspecified> {
rng.fill(out)
}
Expand Down
8 changes: 4 additions & 4 deletions src/ec/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Self, error::Unspecified> {
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<Self, error::Unspecified> {
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,
Expand Down
43 changes: 17 additions & 26 deletions src/ec/suite_b.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Q>,
(x, y): (&Elem<R>, &Elem<R>),
) -> 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
Expand All @@ -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<Q>,
p: &Point,
) -> Result<Elem<R>, 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:
//
Expand Down Expand Up @@ -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)
}

Expand Down Expand Up @@ -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<Q>,
(x, y): (&Elem<R>, &Elem<R>),
a_scaled: &Elem<R>,
b_scaled: &Elem<R>,
) -> 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);
}

Expand Down
14 changes: 9 additions & 5 deletions src/ec/suite_b/curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
}
};
Expand Down
8 changes: 4 additions & 4 deletions src/ec/suite_b/ecdh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand All @@ -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).
Expand All @@ -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);

Expand All @@ -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
Expand Down
5 changes: 3 additions & 2 deletions src/ec/suite_b/ecdsa/digest_scalar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,11 @@ fn digest_scalar_(n: &Modulus<N>, 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| {
Expand All @@ -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());
Expand Down
14 changes: 7 additions & 7 deletions src/ec/suite_b/ecdsa/signing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down Expand Up @@ -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?
Expand All @@ -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;
}

Expand All @@ -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) {
Expand Down
Loading

0 comments on commit aca068c

Please sign in to comment.