diff --git a/Cargo.lock b/Cargo.lock index d544db64f..ac56fd7f2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -504,6 +504,7 @@ checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5" name = "p256" version = "0.4.1" dependencies = [ + "byteorder", "ecdsa", "elliptic-curve", "hex", diff --git a/k256/src/arithmetic/projective.rs b/k256/src/arithmetic/projective.rs index bb3ee5929..6e630f3ab 100644 --- a/k256/src/arithmetic/projective.rs +++ b/k256/src/arithmetic/projective.rs @@ -7,7 +7,8 @@ use core::{ ops::{Add, AddAssign, Neg, Sub, SubAssign}, }; use elliptic_curve::{ - group::{self, Group}, + ff::Field, + group::{Curve, Group}, point::Generator, rand_core::RngCore, subtle::{Choice, ConditionallySelectable, ConstantTimeEq}, @@ -240,8 +241,8 @@ impl ProjectivePoint { impl Group for ProjectivePoint { type Scalar = Scalar; - fn random(rng: impl RngCore) -> Self { - Self::generator() * Scalar::generate_vartime(rng) + fn random(mut rng: impl RngCore) -> Self { + Self::generator() * Scalar::random(&mut rng) } fn identity() -> Self { @@ -262,7 +263,7 @@ impl Group for ProjectivePoint { } } -impl group::Curve for ProjectivePoint { +impl Curve for ProjectivePoint { type AffineRepr = AffinePoint; fn to_affine(&self) -> AffinePoint { diff --git a/k256/src/arithmetic/scalar.rs b/k256/src/arithmetic/scalar.rs index 8f4b8261f..7b3149666 100644 --- a/k256/src/arithmetic/scalar.rs +++ b/k256/src/arithmetic/scalar.rs @@ -21,7 +21,7 @@ use core::{ }; use elliptic_curve::{ consts::U32, - ff, + ff::{Field, PrimeField}, ops::Invert, rand_core::{CryptoRng, RngCore}, subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}, @@ -238,7 +238,7 @@ impl Scalar { } } -impl ff::Field for Scalar { +impl Field for Scalar { fn random(rng: &mut R) -> Self { Scalar::generate_vartime(rng) } @@ -275,7 +275,7 @@ impl ff::Field for Scalar { } } -impl ff::PrimeField for Scalar { +impl PrimeField for Scalar { type Repr = ElementBytes; type ReprEndianness = byteorder::BigEndian; diff --git a/p256/Cargo.toml b/p256/Cargo.toml index fa83cb90c..dadb7233d 100644 --- a/p256/Cargo.toml +++ b/p256/Cargo.toml @@ -16,6 +16,7 @@ categories = ["cryptography", "no-std"] keywords = ["crypto", "ecc", "nist", "prime256v1", "secp256r1"] [dependencies] +byteorder = { version = "1", default-features = false } ecdsa-core = { version = "0.7", package = "ecdsa", optional = true, default-features = false } elliptic-curve = { version = "0.5", default-features = false } sha2 = { version = "0.9", optional = true, default-features = false } diff --git a/p256/src/arithmetic/projective.rs b/p256/src/arithmetic/projective.rs index 18e1d086a..27f4fa85f 100644 --- a/p256/src/arithmetic/projective.rs +++ b/p256/src/arithmetic/projective.rs @@ -1,9 +1,16 @@ //! Projective points use super::{AffinePoint, FieldElement, Scalar, CURVE_EQUATION_B}; -use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; +use core::{ + fmt, + iter::Sum, + ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, +}; use elliptic_curve::{ + ff::Field, + group::{Curve, Group}, point::Generator, + rand_core::RngCore, subtle::{Choice, ConditionallySelectable, ConstantTimeEq}, }; @@ -16,6 +23,39 @@ pub struct ProjectivePoint { z: FieldElement, } +impl Group for ProjectivePoint { + type Scalar = Scalar; + + fn random(mut rng: impl RngCore) -> Self { + Self::generator() * Scalar::random(&mut rng) + } + + fn identity() -> Self { + ProjectivePoint::identity() + } + + fn generator() -> Self { + ProjectivePoint::generator() + } + + fn is_identity(&self) -> Choice { + self.ct_eq(&Self::identity()) + } + + #[must_use] + fn double(&self) -> Self { + ProjectivePoint::double(self) + } +} + +impl Curve for ProjectivePoint { + type AffineRepr = AffinePoint; + + fn to_affine(&self) -> AffinePoint { + ProjectivePoint::to_affine(self) + } +} + impl From for ProjectivePoint { fn from(p: AffinePoint) -> Self { let projective = ProjectivePoint { @@ -49,6 +89,8 @@ impl PartialEq for ProjectivePoint { } } +impl Eq for ProjectivePoint {} + impl ProjectivePoint { /// Returns the additive identity of P-256, also known as the "neutral element" or /// "point at infinity". @@ -208,6 +250,14 @@ impl Default for ProjectivePoint { } } +impl Add for ProjectivePoint { + type Output = ProjectivePoint; + + fn add(self, other: ProjectivePoint) -> ProjectivePoint { + ProjectivePoint::add(&self, &other) + } +} + impl Add<&ProjectivePoint> for &ProjectivePoint { type Output = ProjectivePoint; @@ -236,6 +286,14 @@ impl AddAssign<&ProjectivePoint> for ProjectivePoint { } } +impl Add for ProjectivePoint { + type Output = ProjectivePoint; + + fn add(self, other: AffinePoint) -> ProjectivePoint { + ProjectivePoint::add_mixed(&self, &other) + } +} + impl Add<&AffinePoint> for &ProjectivePoint { type Output = ProjectivePoint; @@ -258,6 +316,32 @@ impl AddAssign for ProjectivePoint { } } +impl AddAssign<&AffinePoint> for ProjectivePoint { + fn add_assign(&mut self, rhs: &AffinePoint) { + *self = ProjectivePoint::add_mixed(self, rhs); + } +} + +impl Sum for ProjectivePoint { + fn sum>(iter: I) -> Self { + iter.fold(ProjectivePoint::identity(), |a, b| a + b) + } +} + +impl<'a> Sum<&'a ProjectivePoint> for ProjectivePoint { + fn sum>(iter: I) -> Self { + iter.cloned().sum() + } +} + +impl Sub for ProjectivePoint { + type Output = ProjectivePoint; + + fn sub(self, other: ProjectivePoint) -> ProjectivePoint { + ProjectivePoint::sub(&self, &other) + } +} + impl Sub<&ProjectivePoint> for &ProjectivePoint { type Output = ProjectivePoint; @@ -286,6 +370,14 @@ impl SubAssign<&ProjectivePoint> for ProjectivePoint { } } +impl Sub for ProjectivePoint { + type Output = ProjectivePoint; + + fn sub(self, other: AffinePoint) -> ProjectivePoint { + ProjectivePoint::sub_mixed(&self, &other) + } +} + impl Sub<&AffinePoint> for &ProjectivePoint { type Output = ProjectivePoint; @@ -308,6 +400,20 @@ impl SubAssign for ProjectivePoint { } } +impl SubAssign<&AffinePoint> for ProjectivePoint { + fn sub_assign(&mut self, rhs: &AffinePoint) { + *self = ProjectivePoint::sub_mixed(self, rhs); + } +} + +impl Mul for ProjectivePoint { + type Output = ProjectivePoint; + + fn mul(self, other: Scalar) -> ProjectivePoint { + ProjectivePoint::mul(&self, &other) + } +} + impl Mul<&Scalar> for &ProjectivePoint { type Output = ProjectivePoint; @@ -352,6 +458,12 @@ impl<'a> Neg for &'a ProjectivePoint { } } +impl fmt::Display for ProjectivePoint { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) + } +} + #[cfg(test)] mod tests { use super::{AffinePoint, ProjectivePoint, Scalar}; @@ -432,7 +544,7 @@ mod tests { ADD_TEST_VECTORS[i] ); - p = p + &generator; + p += &generator; } } diff --git a/p256/src/arithmetic/scalar.rs b/p256/src/arithmetic/scalar.rs index 5a207966a..4dc24c4d3 100644 --- a/p256/src/arithmetic/scalar.rs +++ b/p256/src/arithmetic/scalar.rs @@ -5,10 +5,12 @@ pub mod blinding; use crate::{ElementBytes, NistP256, SecretKey}; use core::{ convert::TryInto, + fmt, ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}, }; use elliptic_curve::{ consts::U32, + ff::{Field, PrimeField}, ops::Invert, rand_core::{CryptoRng, RngCore}, subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}, @@ -73,6 +75,104 @@ pub type NonZeroScalar = elliptic_curve::scalar::NonZeroScalar; #[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))] pub struct Scalar(pub(crate) [u64; LIMBS]); +impl Field for Scalar { + fn random(rng: &mut R) -> Self { + let mut bytes = ElementBytes::default(); + + // Generate a uniformly random scalar using rejection sampling, + // which produces a uniformly random distribution of scalars. + // + // This method is not constant time, but should be secure so long as + // rejected RNG outputs are unrelated to future ones (which is a + // necessary property of a `CryptoRng`). + // + // With an unbiased RNG, the probability of failing to complete after 4 + // iterations is vanishingly small. + loop { + rng.fill_bytes(&mut bytes); + let scalar = Scalar::from_bytes(&bytes); + if scalar.is_some().into() { + #[cfg(feature = "zeroize")] + bytes.zeroize(); + return scalar.unwrap(); + } + } + } + + fn zero() -> Self { + Scalar::zero() + } + + fn one() -> Self { + Scalar::one() + } + + fn is_zero(&self) -> bool { + self.is_zero().into() + } + + #[must_use] + fn square(&self) -> Self { + Scalar::square(self) + } + + #[must_use] + fn double(&self) -> Self { + self.add(self) + } + + fn invert(&self) -> CtOption { + Scalar::invert(self) + } + + // TODO(tarcieri); stub! + fn sqrt(&self) -> CtOption { + todo!(); + } +} + +impl PrimeField for Scalar { + type Repr = ElementBytes; + type ReprEndianness = byteorder::BigEndian; + + const NUM_BITS: u32 = 256; + const CAPACITY: u32 = 256; + const S: u32 = 6; + + fn from_repr(repr: ElementBytes) -> Option { + let result = Scalar::from_bytes(&repr); + + // TODO(tarcieri): replace with into conversion when available (see subtle#73) + if result.is_some().into() { + Some(result.unwrap()) + } else { + None + } + } + + fn to_repr(&self) -> ElementBytes { + self.to_bytes() + } + + fn is_odd(&self) -> bool { + self.0[0] as u8 == 1 + } + + fn char() -> Self::Repr { + unimplemented!(); // removed in newer versions of `ff` + } + + // TODO(tarcieri): stub! + fn multiplicative_generator() -> Self { + todo!(); + } + + // TODO(tarcieri): stub! + fn root_of_unity() -> Self { + todo!(); + } +} + impl From for Scalar { fn from(k: u64) -> Self { Scalar([k, 0, 0, 0]) @@ -483,7 +583,7 @@ impl Scalar { res = res.square(); if ((*e >> i) & 1) == 1 { - res = res * self; + res *= self; } } } @@ -566,11 +666,11 @@ impl Scalar { // sub-step if u >= v { - u = u - &v; - A = A - &C; + u -= &v; + A -= &C; } else { - v = v - &u; - C = C - &A; + v -= &u; + C -= &A; } } @@ -578,6 +678,14 @@ impl Scalar { } } +impl Add for Scalar { + type Output = Scalar; + + fn add(self, other: Scalar) -> Scalar { + Scalar::add(&self, &other) + } +} + impl Add<&Scalar> for &Scalar { type Output = Scalar; @@ -600,6 +708,20 @@ impl AddAssign for Scalar { } } +impl AddAssign<&Scalar> for Scalar { + fn add_assign(&mut self, rhs: &Scalar) { + *self = Scalar::add(self, rhs); + } +} + +impl Sub for Scalar { + type Output = Scalar; + + fn sub(self, other: Scalar) -> Scalar { + Scalar::subtract(&self, &other) + } +} + impl Sub<&Scalar> for &Scalar { type Output = Scalar; @@ -622,6 +744,20 @@ impl SubAssign for Scalar { } } +impl SubAssign<&Scalar> for Scalar { + fn sub_assign(&mut self, rhs: &Scalar) { + *self = Scalar::subtract(self, rhs); + } +} + +impl Mul for Scalar { + type Output = Scalar; + + fn mul(self, other: Scalar) -> Scalar { + Scalar::mul(&self, &other) + } +} + impl Mul<&Scalar> for &Scalar { type Output = Scalar; @@ -644,11 +780,17 @@ impl MulAssign for Scalar { } } +impl MulAssign<&Scalar> for Scalar { + fn mul_assign(&mut self, rhs: &Scalar) { + *self = Scalar::mul(self, rhs); + } +} + impl Neg for Scalar { type Output = Scalar; fn neg(self) -> Scalar { - Scalar::zero() - &self + Scalar::zero() - self } } @@ -694,28 +836,21 @@ impl From for ElementBytes { } } +impl From<&Scalar> for ElementBytes { + fn from(scalar: &Scalar) -> Self { + scalar.to_bytes() + } +} + impl Generate for Scalar { fn generate(mut rng: impl CryptoRng + RngCore) -> Self { - let mut bytes = ElementBytes::default(); + Self::random(&mut rng) + } +} - // Generate a uniformly random scalar using rejection sampling, - // which produces a uniformly random distribution of scalars. - // - // This method is not constant time, but should be secure so long as - // rejected RNG outputs are unrelated to future ones (which is a - // necessary property of a `CryptoRng`). - // - // With an unbiased RNG, the probability of failing to complete after 4 - // iterations is vanishingly small. - loop { - rng.fill_bytes(&mut bytes); - let scalar = Scalar::from_bytes(&bytes); - if scalar.is_some().into() { - #[cfg(feature = "zeroize")] - bytes.zeroize(); - return scalar.unwrap(); - } - } +impl fmt::Display for Scalar { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "{:?}", self) } } diff --git a/p256/src/arithmetic/scalar/blinding.rs b/p256/src/arithmetic/scalar/blinding.rs index f4dea69f8..eb1c59d9b 100644 --- a/p256/src/arithmetic/scalar/blinding.rs +++ b/p256/src/arithmetic/scalar/blinding.rs @@ -50,9 +50,9 @@ impl Invert for BlindedScalar { fn invert(&self) -> CtOption { // prevent side channel analysis of scalar inversion by pre-and-post-multiplying // with the random masking scalar - (self.scalar * &self.mask) + (self.scalar * self.mask) .invert_vartime() - .map(|s| s * &self.mask) + .map(|s| s * self.mask) } } diff --git a/p256/src/ecdsa.rs b/p256/src/ecdsa.rs index b98aa6a10..8043c6ef9 100644 --- a/p256/src/ecdsa.rs +++ b/p256/src/ecdsa.rs @@ -96,7 +96,7 @@ impl SignPrimitive for Scalar { let r = Scalar::from_bytes_reduced(&x.to_bytes()); // Compute `s` as a signature over `r` and `z`. - let s = k_inverse * &(z + &(r * self)); + let s = k_inverse * (z + &(r * self)); if s.is_zero().into() { return Err(Error::new()); @@ -113,9 +113,9 @@ impl VerifyPrimitive for AffinePoint { let s = signature.s(); let s_inv = s.invert().unwrap(); let u1 = z * &s_inv; - let u2 = *r * &s_inv; + let u2 = *r * s_inv; - let x = ((&ProjectivePoint::generator() * &u1) + &(ProjectivePoint::from(*self) * &u2)) + let x = ((ProjectivePoint::generator() * u1) + (ProjectivePoint::from(*self) * u2)) .to_affine() .x;