diff --git a/Cargo.lock b/Cargo.lock index 06c03780..8454ed07 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -17,6 +17,11 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "biguint-literal" +version = "0.1.0" +source = "git+https://github.com/RustCrypto/utils?branch=biguint#f7b73ea9042a8f34728e92bb369cfcf9278b4979" + [[package]] name = "bit-set" version = "0.5.2" @@ -1017,6 +1022,18 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "weierstrass" +version = "0.1.0" +dependencies = [ + "biguint-literal", + "byteorder", + "generic-array", + "hex-literal", + "rand_core", + "subtle", +] + [[package]] name = "winapi" version = "0.3.9" diff --git a/Cargo.toml b/Cargo.toml index e68cd2da..e78afda2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,4 +3,5 @@ members = [ "k256", "p256", "p384", + "weierstrass", ] diff --git a/weierstrass/Cargo.toml b/weierstrass/Cargo.toml new file mode 100644 index 00000000..45bec1f2 --- /dev/null +++ b/weierstrass/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "weierstrass" +version = "0.1.0" +authors = ["RustCrypto Developers"] +edition = "2018" + +[dependencies] +generic-array = "0.14" +byteorder = { version = "1", default-features = false } +subtle = { version = "2.3", default-features = false } +rand_core = { version = "0.5", default-features = false } +biguint-literal = { git = "https://github.com/RustCrypto/utils", branch = "biguint" } +hex-literal = "0.3" diff --git a/weierstrass/src/affine.rs b/weierstrass/src/affine.rs new file mode 100644 index 00000000..b7758ee1 --- /dev/null +++ b/weierstrass/src/affine.rs @@ -0,0 +1,173 @@ +use core::ops::{Mul, Neg, Shl}; +use generic_array::{ArrayLength, typenum::{B1, U1}}; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use core::ops::Add; + +use crate::{ + WeierstrassCurve, Word, + Words, WordsLen, + DoubleWordsLen, + WordsBytesLen, + WordsP1Len, +}; +use crate::scalar::Scalar; +use crate::projective::ProjectivePoint; +use crate::field::FieldElement; + +#[derive(Clone, Copy)] +pub struct AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + pub x: FieldElement, + pub y: FieldElement, + pub infinity: Choice, +} + +impl AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + /// Returns the base point + pub fn generator() -> Self { + Self { + x: C::GENERATOR_X, + y: C::GENERATOR_Y, + infinity: Choice::from(0), + } + } + + /// Returns the identity of the group: the point at infinity. + pub fn identity() -> Self { + Self { + x: FieldElement::zero(), + y: FieldElement::zero(), + infinity: Choice::from(1), + } + } + + /// Is this point the identity point? + pub fn is_identity(&self) -> Choice { + self.infinity + } +} + +impl ConditionallySelectable for AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + AffinePoint { + x: FieldElement::conditional_select(&a.x, &b.x, choice), + y: FieldElement::conditional_select(&a.y, &b.y, choice), + infinity: Choice::conditional_select(&a.infinity, &b.infinity, choice), + } + } +} + +impl ConstantTimeEq for AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.x.ct_eq(&other.x) + & self.y.ct_eq(&other.y) + & self.infinity.ct_eq(&other.infinity) + } +} + +impl PartialEq for AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl Eq for AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{} + +impl Mul> for AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + fn mul(self, scalar: Scalar) -> Self { + (ProjectivePoint::from(self) * scalar).to_affine() + } +} + +impl Neg for AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + fn neg(self) -> Self::Output { + AffinePoint { + x: self.x, + y: -self.y, + infinity: self.infinity, + } + } +} + +#[cfg(feature = "zeroize")] +impl Zeroize for AffinePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn zeroize(&mut self) { + self.x.zeroize(); + self.y.zeroize(); + } +} diff --git a/weierstrass/src/field.rs b/weierstrass/src/field.rs new file mode 100644 index 00000000..f0818274 --- /dev/null +++ b/weierstrass/src/field.rs @@ -0,0 +1,411 @@ +use core::{fmt, ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Neg, Shl}}; +use generic_array::ArrayLength; +use generic_array::typenum::{B1, U1}; +use subtle::{ConditionallySelectable, Choice, ConstantTimeEq, CtOption}; +use rand_core::{RngCore, CryptoRng}; +use crate::utils::BigUintExt; + +use crate::{ + WeierstrassCurve, Word, WORD_WIDTH_BITS, + Words, WordsLen, + DoubleWords, DoubleWordsLen, + WordsBytes, WordsBytesLen, + WordsP1, WordsP1Len, + random_word +}; +use crate::utils::{adc, sbb, mac}; + +#[derive(Default, Debug, Copy, Clone)] +pub struct FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + pub words: Words, +} + +impl FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + /// Returns the zero element (additive identity). + pub fn zero() -> Self { + Self { + words: Default::default(), + } + } + + /// Returns the multiplicative identity. + pub fn one() -> Self { + C::R + } + + /// Returns a uniformly-random element within the field. + pub fn generate(mut rng: impl CryptoRng + RngCore) -> Self { + // We reduce a random value with a double length, which results in a + // negligible bias from the uniform distribution. + let mut t = DoubleWords::::default(); + t.iter_mut().for_each(|wt| *wt = random_word(&mut rng)); + FieldElement::montgomery_reduce(t) + } + + /// Attempts to parse the given byte array. + /// + /// Returns None if resulting integer is not in the range [0, p). + pub fn from_bytes(bytes: &WordsBytes) -> CtOption { + let words = B::bytes2biguint(bytes); + + let mut borrow = Word::default(); + for (&w, &wm) in words.iter().zip(C::MODULUS_P.iter()) { + borrow = sbb(w, wm, borrow).1; + } + let is_some = (borrow as u8) & 1; + + // Convert w to Montgomery form: w * R^2 * R^-1 mod p = wR mod p + CtOption::new(Self { words }.mul(C::R2), Choice::from(is_some)) + } + + /// Returns byte encoding of this field element. + pub fn to_bytes(&self) -> WordsBytes { + // Convert from Montgomery form to canonical form + let mut w = DoubleWords::::default(); + let n = self.words.len(); + w[..n].copy_from_slice(&self.words); + let t = Self::montgomery_reduce(w).words; + B::biguint2bytes(&t) + } + + /// Determine if this `FieldElement` is zero. + pub fn is_zero(&self) -> Choice { + self.ct_eq(&FieldElement::zero()) + } + + /// Returns self + rhs mod p + pub fn add(&self, rhs: &Self) -> Self { + // Last bit of p is usually set, so addition can result in five words. + let mut t: WordsP1 = Default::default(); + let mut carry = Word::default(); + let pairs = self.words.iter().zip(rhs.words.iter()); + for (wt, (&wb, &wa)) in t.iter_mut().zip(pairs) { + let t = adc(wa, wb, carry); + *wt = t.0; + carry = t.1; + } + *t.last_mut().unwrap() = carry; + // Attempt to subtract the MODULUS_P, to ensure the result is in the field. + Self::sub_inner(t, Self { words: C::MODULUS_P }) + } + + /// Returns 2*self. + pub fn double(&self) -> Self { + self.add(self) + } + + /// Returns self - rhs mod p + pub fn subtract(&self, rhs: &Self) -> Self { + let mut t: WordsP1 = Default::default(); + let n = self.words.len(); + t[..n].copy_from_slice(&self.words); + Self::sub_inner(t, *rhs) + } + + fn sub_inner(a: WordsP1, mut b: Self) -> Self { + let mut borrow = Word::default(); + for (wb, wa) in b.words.iter_mut().zip(a.iter()) { + let t = sbb(*wa, *wb, borrow); + *wb = t.0; + borrow = t.1; + } + let (_, borrow) = sbb(a[a.len() - 1], 0, borrow); + + // If underflow occurred on the final word, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the + // MODULUS_P. + let mut carry = Word::default(); + for (wb, &wm) in b.words.iter_mut().zip(C::MODULUS_P.iter()) { + let t = adc(*wb, wm & borrow, carry); + *wb = t.0; + carry = t.1; + } + b + } + + /// Montgomery Reduction + fn montgomery_reduce(v: DoubleWords) -> Self { + // `DoubleWords` length is always multiple of 2 + let n = v.len() / 2; + let mut r = WordsP1::::default(); + r[..n].copy_from_slice(&v[..n]); + for i in 0..n { + let u = r[0].wrapping_mul(C::PT); + let (_, mut carry) = mac(r[0], u, C::MODULUS_P[0], 0); + for j in 1..n { + let t = mac(r[j], u, C::MODULUS_P[j], carry); + r[j - 1] = t.0; + carry = t.1; + } + let t = adc(v[i + n], r[n], carry); + r[n - 1] = t.0; + r[n] = t.1; + } + Self::sub_inner(r, Self { words: C::MODULUS_P }) + } + + /// Returns self * rhs mod p + pub fn mul(&self, rhs: &Self) -> Self { + let mut w = DoubleWords::::default(); + let n = rhs.words.len(); + + // Schoolbook multiplication. + for i in 0..n { + let mut carry = Word::default(); + for j in 0..n { + let t = mac(w[i + j], self.words[i], rhs.words[j], carry); + w[i + j] = t.0; + carry = t.1; + } + w[i + n] = carry; + } + + Self::montgomery_reduce(w) + } + + /// Returns self * self mod p + pub fn square(&self) -> Self { + self.mul(self) + } + + /// Returns `self^by mod p`, where `by` is a little-endian integer exponent. + /// + /// **This operation is variable time with respect to the exponent.** + /// If the exponent is fixed, this operation is effectively constant time. + pub fn pow_vartime(&self, by: &Words) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..WORD_WIDTH_BITS).rev() { + res = res.square(); + if ((*e >> i) & 1) == 1 { + res = res.mul(*self); + } + } + } + res + } + + /// Returns the multiplicative inverse of self, if self is non-zero. + pub fn invert(&self) -> CtOption { + // We need to find b such that b * a ≡ 1 mod p. As we are in a prime + // field, we can apply Fermat's Little Theorem: + // + // a^p ≡ a mod p + // a^(p-1) ≡ 1 mod p + // a^(p-2) * a ≡ 1 mod p + // + // Thus inversion can be implemented with a single exponentiation. + let inverse = self.pow_vartime(&C::MODULUS_P_M2); + CtOption::new(inverse, !self.is_zero()) + } + + /// Returns the square root of self mod p, or `None` if no square root exists. + pub fn sqrt(&self) -> CtOption { + todo!(); + } +} + +impl fmt::UpperHex for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for word in self.words.iter().rev() { + write!(f, "{:016X}", word)?; + } + Ok(()) + } +} + +impl ConditionallySelectable for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let mut res = Self::zero(); + let pairs = a.words.iter().zip(b.words.iter()); + for (vr, (va, vb)) in res.words.iter_mut().zip(pairs) { + *vr = Word::conditional_select(va, vb, choice); + } + res + } +} + +impl ConstantTimeEq for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn ct_eq(&self, other: &Self) -> Choice { + let mut res = 1u8.into(); + for (a, b) in self.words.iter().zip(other.words.iter()) { + res &= a.ct_eq(b); + } + res + } +} + +impl PartialEq for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl Add> for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn add(self, other: Self) -> Self { + FieldElement::add(&self, &other) + } +} + +impl AddAssign> for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + #[inline] + fn add_assign(&mut self, other: Self) { + *self = FieldElement::add(self, &other); + } +} + +impl Sub> for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn sub(self, other: Self) -> Self { + FieldElement::subtract(&self, &other) + } +} + +impl SubAssign> for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + #[inline] + fn sub_assign(&mut self, other: Self) { + *self = FieldElement::subtract(self, &other); + } +} + + +impl Mul> for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn mul(self, other: Self) -> Self { + FieldElement::mul(&self, &other) + } +} + +impl MulAssign> for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + #[inline] + fn mul_assign(&mut self, other: Self) { + *self = FieldElement::mul(self, &other); + } +} + +impl Neg for FieldElement + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn neg(self) -> Self { + FieldElement::zero() - self + } +} + +#[cfg(feature = "zeroize")] +impl Zeroize for FieldElement { + fn zeroize(&mut self) { + self.words.zeroize(); + } +} diff --git a/weierstrass/src/lib.rs b/weierstrass/src/lib.rs new file mode 100644 index 00000000..0a1f458b --- /dev/null +++ b/weierstrass/src/lib.rs @@ -0,0 +1,98 @@ +#![no_std] +pub use {generic_array, subtle, byteorder}; +use generic_array::{ArrayLength, GenericArray}; +use generic_array::typenum::{ + self, + B1, U1, U2, + Unsigned, + operator_aliases::{Sum, Quot, Double}, +}; +use rand_core::{RngCore, CryptoRng}; +use core::ops::{Div, Add, Shl}; + +mod affine; +mod field; +mod scalar; +mod projective; +mod utils; + +pub use affine::AffinePoint; +pub use field::FieldElement; +pub use scalar::Scalar; +pub use projective::ProjectivePoint; +pub use utils::BigUintExt; + +// TODO: add cfgs for other word sizes +pub type Word = u64; +type DoubleWord = u128; +type WordWidth = typenum::U8; +fn random_word(mut rng: impl CryptoRng + RngCore) -> Word { + rng.next_u64() +} + + +const WORD_WIDTH_BITS: usize = 8*WordWidth::USIZE; + +pub type WordsLen = Quot<::Size, WordWidth>; +pub type Words = GenericArray>; +pub type WordsBytesLen = ::Size; +pub type WordsBytes = GenericArray>; + +pub type WordsP1Len = Sum, U1>; +pub type WordsP1 = GenericArray>; + +pub type DoubleWordsLen = Double>; +pub type DoubleWords = GenericArray>; +pub type DoubleWordsBytesLen = Double<::Size>; +pub type DoubleWordsBytes = GenericArray>; + +pub enum CurveKind { + General, + Minus3, + Zero, +} + +pub trait WeierstrassCurve + where + Self: Sized + Copy + Default, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Size: Unsigned + Div + Div + Shl; + + const A: FieldElement; + const B: FieldElement; + /// 3*B + const B3: FieldElement; + const CURVE_KIND: CurveKind; + const MODULUS_P: Words; + /// p - 2 + const MODULUS_P_M2: Words; + + const MODULUS_Q: Words; + /// q - 2 + const MODULUS_Q_M2: Words; + /// floor(q/m), where `m` is a biggest representable number with given + /// number of bits (i.e. `0xFFFF...FFFF`) + const MODULUS_Q_REDUCE_N: usize; + + // we can't define GENERATOR, because `Choice` does not + // support const construction + const GENERATOR_X: FieldElement; + const GENERATOR_Y: FieldElement; + /// R = 2^Bits mod p + const R: FieldElement; + /// R2 = 2^(2*Bits) mod p + const R2: FieldElement; + + /// MU = floor(2^512 / q) + const MU: WordsP1; + /// P*PT (mod 2^WORD_WIDTH) == -1 + const PT: Word; + + /// The elliptic curve group order divided by subgroup order (m/q) + const N: Word; +} diff --git a/weierstrass/src/projective.rs b/weierstrass/src/projective.rs new file mode 100644 index 00000000..74b8527a --- /dev/null +++ b/weierstrass/src/projective.rs @@ -0,0 +1,844 @@ +use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign, Shl}; +use core::iter::Sum; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq}; +use generic_array::{ArrayLength, typenum::{B1, U1}}; + +use crate::{ + WeierstrassCurve, CurveKind, + Word, WORD_WIDTH_BITS, + Words, WordsLen, + DoubleWordsLen, + WordsBytesLen, + WordsP1Len, +}; +use crate::field::FieldElement; +use crate::affine::AffinePoint; +use crate::scalar::Scalar; + + +#[derive(Clone, Copy, Debug)] +pub struct ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + pub x: FieldElement, + pub y: FieldElement, + pub z: FieldElement, +} + +impl From> for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn from(p: AffinePoint) -> Self { + let projective = ProjectivePoint { + x: p.x, + y: p.y, + z: FieldElement::one(), + }; + Self::conditional_select(&projective, &Self::identity(), p.infinity) + } +} + +impl ConditionallySelectable for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ProjectivePoint { + x: FieldElement::conditional_select(&a.x, &b.x, choice), + y: FieldElement::conditional_select(&a.y, &b.y, choice), + z: FieldElement::conditional_select(&a.z, &b.z, choice), + } + } +} + +impl ConstantTimeEq for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn ct_eq(&self, other: &Self) -> Choice { + self.to_affine().ct_eq(&other.to_affine()) + } +} + +impl PartialEq for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl Eq for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{} + +impl ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + /// Returns the additive identity of P-256, also known as the "neutral element" or + /// "point at infinity". + pub fn identity() -> Self { + Self { + x: FieldElement::zero(), + y: FieldElement::one(), + z: FieldElement::zero(), + } + } + + /// Returns the base point of P-256. + pub fn generator() -> Self { + AffinePoint::generator().into() + } + + /// Returns the affine representation of this point, or `None` if it is the identity. + pub fn to_affine(&self) -> AffinePoint { + self.z + .invert() + .map(|zinv| AffinePoint { + x: self.x * zinv, + y: self.y * zinv, + infinity: Choice::from(0), + }) + .unwrap_or_else(AffinePoint::identity) + } + + /// Returns `-self`. + fn neg(&self) -> Self { + ProjectivePoint { + x: self.x, + y: self.y.neg(), + z: self.z, + } + } + + /// Returns `self + other`. + fn add(&self, other: &Self) -> Self { + // We implement the complete addition formula from Renes-Costello-Batina 2015 + // (https://eprint.iacr.org/2015/1060). The comments after each line + // indicate which algorithm steps are being performed. + let &ProjectivePoint { x: x1, y: y1, z: z1 } = self; + let &ProjectivePoint { x: x2, y: y2, z: z2 } = other; + + let b1 = C::B; + let b3 = C::B3; + let a1 = C::A; + + let (mut t0, mut t1, mut t2, mut t3, mut t4, mut t5); + let (mut x3, mut y3, mut z3); + + + match C::CURVE_KIND { + CurveKind::General => { + // Algorithm 1 + t0 = x1 * x2; // 1 + t1 = y1 * y2; // 2 + t2 = z1 * z2; // 3 + + t3 = x1 + y1; // 4 + t4 = x2 + y2; // 5 + t3 = t3 * t4; // 6 + + t4 = t0 + t1; // 7 + t3 = t3 - t4; // 8 + t4 = x1 + z1; // 9 + + t5 = x2 + z2; // 10 + t4 = t4 * t5; // 11 + t5 = t0 + t2; // 12 + + t4 = t4 - t5; // 13 + t5 = y1 + z1; // 14 + x3 = y2 + z2; // 15 + + t5 = t5 * x3; // 16 + x3 = t1 + t2; // 17 + t5 = t5 - x3; // 18 + + z3 = a1 * t4; // 19 + x3 = b3 * t2; // 20 + z3 = x3 + z3; // 21 + + x3 = t1 - z3; // 22 + z3 = t1 + z3; // 23 + y3 = x3 * z3; // 24 + + t1 = t0 + t0; // 25 + t1 = t1 + t0; // 26 + t2 = a1 * t2; // 27 + + t4 = b3 * t4; // 28 + t1 = t1 + t2; // 29 + t2 = t0 - t2; // 30 + + t2 = a1 * t2; // 31 + t4 = t4 + t2; // 32 + t0 = t1 * t4; // 33 + + y3 = y3 + t0; // 34 + t0 = t5 * t4; // 35 + x3 = t3 * x3; // 36 + + x3 = x3 - t0; // 37 + t0 = t3 * t1; // 38 + z3 = t5 * z3; // 39 + + z3 = z3 + t0; // 40 + } + CurveKind::Minus3 => { + // Algorithm 4 + t0 = x1 * x2; // 1 + t1 = y1 * y2; // 2 + t2 = z1 * z2; // 3 + + t3 = x1 + y1; // 4 + t4 = x2 + y2; // 5 + t3 = t3 * t4; // 6 + + t4 = t0 + t1; // 7 + t3 = t3 - t4; // 8 + t4 = y1 + z1; // 9 + + x3 = y2 + z2; // 10 + t4 = t4 * x3; // 11 + x3 = t1 + t2; // 12 + + t4 = t4 - x3; // 13 + x3 = x1 + z1; // 14 + y3 = x2 + z2; // 15 + + x3 = x3 * y3; // 16 + y3 = t0 + t2; // 17 + y3 = x3 - y3; // 18 + + z3 = b1 * t2; // 19 + x3 = y3 - z3; // 20 + z3 = x3 + x3; // 21 + + x3 = x3 + z3; // 22 + z3 = t1 - x3; // 23 + x3 = t1 + x3; // 24 + + y3 = b1 * y3; // 25 + t1 = t2 + t2; // 26 + t2 = t1 + t2; // 27 + + y3 = y3 - t2; // 28 + y3 = y3 - t0; // 29 + t1 = y3 + y3; // 30 + + y3 = t1 + y3; // 31 + t1 = t0 + t0; // 32 + t0 = t1 + t0; // 33 + + t0 = t0 - t2; // 34 + t1 = t4 * y3; // 35 + t2 = t0 * y3; // 36 + + y3 = x3 * z3; // 37 + y3 = y3 + t2; // 38 + x3 = t3 * x3; // 39 + + x3 = x3 - t1; // 40 + z3 = t4 * z3; // 41 + t1 = t3 * t0; // 42 + + z3 = z3 + t1; // 43 + } + CurveKind::Zero => { + // Algorithm 7 + t0 = x1 * x2; // 1 + t1 = y1 * y2; // 2 + t2 = z1 * z2; // 3 + + t3 = x1 + y1; // 4 + t4 = x2 + y2; // 5 + t3 = t3 * t4; // 6 + + t4 = t0 + t1; // 7 + t3 = t3 - t4; // 8 + t4 = y1 + z1; // 9 + + x3 = y2 + z2; // 10 + t4 = t4 * x3; // 11 + x3 = t1 + t2; // 12 + + t4 = t4 - x3; // 13 + x3 = x1 + z1; // 14 + y3 = x2 + z2; // 15 + + x3 = x3 * y3; // 16 + y3 = t0 + t2; // 17 + y3 = x3 - y3; // 18 + + x3 = t0 + t0; // 19 + t0 = x3 + t0; // 20 + t2 = b3 * t2; // 21 + + z3 = t1 + t2; // 22 + t1 = t1 - t2; // 23 + y3 = b3 * y3; // 24 + + x3 = t4 * y3; // 25 + t2 = t3 * t1; // 26 + x3 = t2 - x3; // 27 + + y3 = y3 * t0; // 28 + t1 = t1 * z3; // 29 + y3 = t1 + y3; // 30 + + t0 = t0 * t3; // 31 + z3 = z3 * t4; // 32 + z3 = z3 + t0; // 33 + } + } + ProjectivePoint { x: x3, y: y3, z: z3 } + } + + /// Returns `self + other`. + fn add_mixed(&self, other: &AffinePoint) -> Self { + // We implement the complete mixed addition formula from Renes-Costello-Batina + // 2015. The comments after each line indicate which algorithm steps + // are being performed. + let &ProjectivePoint { x: x1, y: y1, z: z1 } = self; + let &AffinePoint { x: x2, y: y2, .. } = other; + + let b1 = C::B; + let b3 = C::B; + let a1 = C::A; + + let (mut t0, mut t1, mut t2, mut t3, mut t4, mut t5); + let (mut x3, mut y3, mut z3); + + match C::CURVE_KIND { + CurveKind::General => { + // Algorithm 2 + t0 = x1 * x2; // 1 + t1 = y1 * y2; // 2 + t3 = x2 + y2; // 3 + + t4 = x1 + y1; // 4 + t3 = t3 * t4; // 5 + t4 = t0 + t1; // 6 + + t3 = t3 - t4; // 7 + t4 = x2 * z1; // 8 + t4 = t4 + x1; // 9 + + t5 = y2 * z1; // 10 + t5 = t5 + y1; // 11 + z3 = a1 * t4; // 12 + + x3 = b3 * z1; // 13 + z3 = x3 + z3; // 14 + x3 = t1 - z3; // 15 + + z3 = t1 + z3; // 16 + y3 = x3 * z3; // 17 + t1 = t0 + t0; // 18 + + t1 = t1 + t0; // 19 + t2 = a1 * z1; // 20 + t4 = b3 * t4; // 21 + + t1 = t1 + t2; // 22 + t2 = t0 - t2; // 23 + t2 = a1 * t2; // 24 + + t4 = t4 + t2; // 25 + t0 = t1 * t4; // 26 + y3 = y3 + t0; // 27 + + t0 = t5 * t4; // 28 + x3 = t3 * x3; // 29 + x3 = x3 - t0; // 30 + + t0 = t3 * t1; // 31 + z3 = t5 * z3; // 32 + z3 = z3 + t0; // 33 + } + CurveKind::Minus3 => { + // Algorithm 5 + t0 = x1 * x2; // 1 + t1 = y1 * y2; // 2 + t3 = x2 + y2; // 3 + + t4 = x1 + y1; // 4 + t3 = t3 * t4; // 5 + t4 = t0 + t1; // 6 + + t3 = t3 - t4; // 7 + t4 = y2 * z1; // 8 + t4 = t4 + y1; // 9 + + y3 = x2 * z1; // 10 + y3 = y3 + x1; // 11 + z3 = b1 * z1; // 12 + + x3 = y3 - z3; // 13 + z3 = x3 + x3; // 14 + x3 = x3 + z3; // 15 + + z3 = t1 - x3; // 16 + x3 = t1 + x3; // 17 + y3 = b1 * y3; // 18 + + t1 = z1 + z1; // 19 + t2 = t1 + z1; // 20 + y3 = y3 - t2; // 21 + + y3 = y3 - t0; // 22 + t1 = y3 + y3; // 23 + y3 = t1 + y3; // 24 + + t1 = t0 + t0; // 25 + t0 = t1 + t0; // 26 + t0 = t0 - t2; // 27 + + t1 = t4 * y3; // 28 + t2 = t0 * y3; // 29 + y3 = x3 * z3; // 30 + + y3 = y3 + t2; // 31 + x3 = t3 * x3; // 32 + x3 = x3 - t1; // 33 + + z3 = t4 * z3; // 34 + t1 = t3 * t0; // 35 + z3 = z3 + t1; // 36 + } + CurveKind::Zero => { + // Algorithm 8 + t0 = x1 * x2; // 1 + t1 = y1 * y2; // 2 + t3 = x2 + y2; // 3 + + t4 = x1 + y1; // 4 + t3 = t3 * t4; // 5 + t4 = t0 + t1; // 6 + + t3 = t3 - t4; // 7 + t4 = y2 * z1; // 8 + t4 = t4 + y1; // 9 + + y3 = x2 * z1; // 10 + y3 = y3 + x1; // 11 + x3 = t0 + t0; // 12 + + t0 = x3 + t0; // 13 + t2 = b3 * z1; // 14 + z3 = t1 + t2; // 15 + + t1 = t1 - t2; // 16 + y3 = b3 * y3; // 17 + x3 = t4 * y3; // 18 + + t2 = t3 * t1; // 19 + x3 = t2 - x3; // 20 + y3 = y3 * t0; // 21 + + t1 = t1 * z3; // 22 + y3 = t1 + y3; // 23 + t0 = t0 * t3; // 24 + + z3 = z3 * t4; // 25 + z3 = z3 + t0; // 26 + } + } + ProjectivePoint { x: x3, y: y3, z: z3 } + } + + /// Doubles this point. + pub fn double(&self) -> Self { + // We implement the complete doubling formula from Renes-Costello-Batina 2015 + // (https://eprint.iacr.org/2015/1060). The comments after each line + // indicate which algorithm steps are being performed. + let &ProjectivePoint { x: x1, y: y1, z: z1 } = self; + + let b1 = C::B; + let b3 = C::B3; + let a1 = C::A; + + let (mut t0, mut t1, mut t2, mut t3); + let (mut x3, mut y3, mut z3); + match C::CURVE_KIND { + CurveKind::General => { + // Algorithm 3 + t0 = x1 * x1; // 1 + t1 = y1 * y1; // 2 + t2 = z1 * z1; // 3 + + t3 = x1 * y1; // 4 + t3 = t3 + t3; // 5 + z3 = x1 * z1; // 6 + + z3 = z3 + z3; // 7 + x3 = a1 * z3; // 8 + y3 = b3 * t2; // 9 + + y3 = x3 + y3; // 10 + x3 = t1 - y3; // 11 + y3 = t1 + y3; // 12 + + y3 = x3 * y3; // 13 + x3 = t3 * x3; // 14 + z3 = b3 * z3; // 15 + + t2 = a1 * t2; // 16 + t3 = t0 - t2; // 17 + t3 = a1 * t3; // 18 + + t3 = t3 + z3; // 19 + z3 = t0 + t0; // 20 + t0 = z3 + t0; // 21 + + t0 = t0 + t2; // 22 + t0 = t0 * t3; // 23 + y3 = y3 + t0; // 24 + + t2 = y1 * z1; // 25 + t2 = t2 + t2; // 26 + t0 = t2 * t3; // 27 + + x3 = x3 - t0; // 28 + z3 = t2 * t1; // 29 + z3 = z3 + z3; // 30 + + z3 = z3 + z3; // 31 + } + CurveKind::Minus3 => { + // Algorithm 6 + t0 = x1 * x1; // 1 + t1 = y1 * y1; // 2 + t2 = z1 * z1; // 3 + + t3 = x1 * y1; // 4 + t3 = t3 + t3; // 5 + z3 = x1 * z1; // 6 + + z3 = z3 + z3; // 7 + y3 = b1 * t2; // 8 + y3 = y3 - z3; // 9 + + x3 = y3 + y3; // 10 + y3 = x3 + y3; // 11 + x3 = t1 - y3; // 12 + + y3 = t1 + y3; // 13 + y3 = x3 * y3; // 14 + x3 = x3 * t3; // 15 + + t3 = t2 + t2; // 16 + t2 = t2 + t3; // 17 + z3 = b1 * z3; // 18 + + z3 = z3 - t2; // 19 + z3 = z3 - t0; // 20 + t3 = z3 + z3; // 21 + + z3 = z3 + t3; // 22 + t3 = t0 + t0; // 23 + t0 = t3 + t0; // 24 + + t0 = t0 - t2; // 25 + t0 = t0 * z3; // 26 + y3 = y3 + t0; // 27 + + t0 = y1 * z1; // 28 + t0 = t0 + t0; // 29 + z3 = t0 * z3; // 30 + + x3 = x3 - z3; // 31 + z3 = t0 * t1; // 32 + z3 = z3 + z3; // 33 + + z3 = z3 + z3; // 34 + } + CurveKind::Zero => { + // Algorithm 9 + t0 = y1 * y1; // 1 + z3 = t0 + t0; // 2 + z3 = z3 + z3; // 3 + + z3 = z3 + z3; // 4 + t1 = y1 * z1; // 5 + t2 = z1 * z1; // 6 + + t2 = b3 * t2; // 7 + x3 = t2 * z3; // 8 + y3 = t0 + t2; // 9 + + z3 = t1 * z3; // 10 + t1 = t2 + t2; // 11 + t2 = t1 + t2; // 12 + + t0 = t0 - t2; // 13 + y3 = t0 * y3; // 14 + y3 = x3 + y3; // 15 + + t1 = x1 * y1; // 16 + x3 = t0 * t1; // 17 + x3 = x3 + x3; // 18 + } + } + ProjectivePoint { x: x3, y: y3, z: z3 } + } + + /// Returns `self - other`. + fn sub(&self, other: &Self) -> Self { + self.add(&other.neg()) + } + + /// Returns `self - other`. + fn sub_mixed(&self, other: &AffinePoint) -> Self { + self.add_mixed(&other.neg()) + } + + /// Returns `[k] self`. + fn mul(&self, k: &Scalar) -> Self { + let mut ret = ProjectivePoint::identity(); + + for word in k.words.iter().rev() { + for i in (0..WORD_WIDTH_BITS).rev() { + ret = ret.double(); + let choice = ((word >> i) & (1 as Word)) as u8; + ret.conditional_assign(&(ret + *self), Choice::from(choice)); + } + } + + ret + } +} + +impl Default for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn default() -> Self { + Self::identity() + } +} + +impl Add for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + fn add(self, other: Self) -> Self { + ProjectivePoint::add(&self, &other) + } +} + +impl AddAssign for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn add_assign(&mut self, rhs: Self) { + *self = ProjectivePoint::add(self, &rhs); + } +} + +impl Add> for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + fn add(self, other: AffinePoint) -> Self { + ProjectivePoint::add_mixed(&self, &other) + } +} + +impl AddAssign> for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn add_assign(&mut self, rhs: AffinePoint) { + *self = ProjectivePoint::add_mixed(self, &rhs); + } +} + +impl Sum for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn sum>(iter: I) -> Self { + iter.fold(ProjectivePoint::identity(), |a, b| a + b) + } +} + +impl Sub for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + fn sub(self, other: Self) -> Self { + ProjectivePoint::sub(&self, &other) + } +} + +impl SubAssign for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn sub_assign(&mut self, rhs: Self) { + *self = ProjectivePoint::sub(self, &rhs); + } +} + +impl Sub> for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + fn sub(self, other: AffinePoint) -> Self { + ProjectivePoint::sub_mixed(&self, &other) + } +} + +impl SubAssign> for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn sub_assign(&mut self, rhs: AffinePoint) { + *self = ProjectivePoint::sub_mixed(self, &rhs); + } +} + +impl Mul> for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + fn mul(self, other: Scalar) -> Self { + ProjectivePoint::mul(&self, &other) + } +} + +impl MulAssign> for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn mul_assign(&mut self, rhs: Scalar) { + *self = ProjectivePoint::mul(self, &rhs); + } +} + +impl Neg for ProjectivePoint + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + fn neg(self) -> Self { + ProjectivePoint::neg(&self) + } +} diff --git a/weierstrass/src/scalar.rs b/weierstrass/src/scalar.rs new file mode 100644 index 00000000..1801a5c5 --- /dev/null +++ b/weierstrass/src/scalar.rs @@ -0,0 +1,566 @@ +use core::{fmt, ops::{Add, AddAssign, Sub, SubAssign, Mul, MulAssign, Neg, Shl}}; +use subtle::{ConditionallySelectable, Choice, ConstantTimeEq, CtOption}; +use generic_array::{ArrayLength, typenum::{B1, U1}}; +use crate::utils::BigUintExt; + +use super::{ + WeierstrassCurve, Word, WORD_WIDTH_BITS, Words, WordsLen, + DoubleWordsLen, DoubleWords, + WordsBytesLen, WordsBytes, + WordsP1Len, WordsP1, +}; +use crate::utils::{adc, sbb, mac}; + +#[derive(Default, Copy, Clone, Debug, Eq)] +pub struct Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + pub(crate) words: Words, +} + +impl Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + /// Returns the zero scalar (additive identity). + pub fn zero() -> Self { + Self::default() + } + + /// Returns the multiplicative identity. + pub fn one() -> Self { + let mut t = Self::default(); + t.words[0] = 1; + t + } + + /// Determine if this `Scalar` is zero. + pub fn is_zero(&self) -> Choice { + self.ct_eq(&Scalar::zero()) + } + + /// Returns self + rhs mod n + pub fn add(&self, rhs: &Self) -> Self { + // Last bit of p is usually set, so addition can result in five words. + let mut t: WordsP1 = Default::default(); + let mut carry = Word::default(); + let pairs = self.words.iter().zip(rhs.words.iter()); + for (wt, (&wb, &wa)) in t.iter_mut().zip(pairs) { + let t = adc(wa, wb, carry); + *wt = t.0; + carry = t.1; + } + *t.last_mut().unwrap() = carry; + // Attempt to subtract the MODULUS_Q, to ensure the result is in the field. + Self::sub_inner(t, Self { words: C::MODULUS_Q }) + } + + /// Returns 2*self. + pub fn double(&self) -> Self { + self.add(self) + } + + /// Returns self - rhs mod n + pub fn subtract(&self, rhs: &Self) -> Self { + let mut t: WordsP1 = Default::default(); + let n = self.words.len(); + t[..n].copy_from_slice(&self.words); + Self::sub_inner(t, *rhs) + } + + fn sub_inner(a: WordsP1, mut b: Self) -> Self { + let mut borrow = Word::default(); + for (wb, wa) in b.words.iter_mut().zip(a.iter()) { + let t = sbb(*wa, *wb, borrow); + *wb = t.0; + borrow = t.1; + } + let (_, borrow) = sbb(a[a.len() - 1], 0, borrow); + + // If underflow occurred on the final word, borrow = 0xfff...fff, otherwise + // borrow = 0x000...000. Thus, we use it as a mask to conditionally add the + // MODULUS_Q. + let mut carry = Word::default(); + for (wb, &wm) in b.words.iter_mut().zip(C::MODULUS_Q.iter()) { + let t = adc(*wb, wm & borrow, carry); + *wb = t.0; + carry = t.1; + } + b + } + + /// Barrett Reduction + /// + /// The general algorithm is: + /// ```text + /// p = n = order of group + /// b = 2^64 = 64bit machine word + /// k = 4 + /// a \in [0, 2^512] + /// mu := floor(b^{2k} / p) + /// q1 := floor(a / b^{k - 1}) + /// q2 := q1 * mu + /// q3 := <- floor(a / b^{k - 1}) + /// r1 := a mod b^{k + 1} + /// r2 := q3 * m mod b^{k + 1} + /// r := r1 - r2 + /// + /// if r < 0: r := r + b^{k + 1} + /// while r >= p: do r := r - p (at most twice) + /// ``` + /// + /// References: + /// - Handbook of Applied Cryptography, Chapter 14 + /// Algorithm 14.42 + /// http://cacr.uwaterloo.ca/hac/about/chap14.pdf + #[inline] + #[allow(clippy::too_many_arguments)] + fn barrett_reduce(a: DoubleWords) -> Self { + // `DoubleWords` length is always multiple of 2 + let k = a.len() / 2; + let mut q1 = WordsP1::::default(); + q1.copy_from_slice(&a[k-1..]); + + let mut q3 = WordsP1::::default(); + let n = q3.len(); + // Schoolbook multiplication + take last `n` words + for i in 0..n { + let (_, mut carry) = mac(q3[0], q1[i], C::MU[0], 0); + for j in 1..n { + let t = mac(q3[j], q1[i], C::MU[j], carry); + q3[j - 1] = t.0; + carry = t.1; + } + q3[n - 1] = carry; + } + + let mut r1 = WordsP1::::default(); + r1.copy_from_slice(&a[..k+1]); + + let mut r2 = WordsP1::::default(); + // Schoolbook multiplication + take first `n` words + for i in 0..n { + let mut carry = Word::default(); + for j in 0..(n - i) { + let wm = C::MODULUS_Q.get(j).cloned().unwrap_or(0); + let t = mac(r2[i + j], q3[i], wm, carry); + r2[i + j] = t.0; + carry = t.1; + } + } + + // If underflow occurred on the final word - don't care (= add b^{k+1}). + let mut borrow = Word::default(); + for (w1, &w2) in r1.iter_mut().zip(r2.iter()) { + let t = sbb(*w1, w2, borrow); + *w1 = t.0; + borrow = t.1; + } + let mut r = r1; + + fn sub_mod_if_necessary(r: &mut WordsP1) + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, + { + let mut borrow = Word::default(); + let n = r.len(); + for (wr, &wm) in r.iter_mut().zip(C::MODULUS_Q.iter()) { + let t = sbb(*wr, wm, borrow); + *wr = t.0; + borrow = t.1; + } + let t = sbb(r[n - 1], 0, borrow); + r[n - 1] = t.0; + borrow = t.1; + + // If underflow occurred on the final limb, borrow = 0xfff...fff, + // otherwise borrow = 0x000...000. Thus, we use it as a mask to + // conditionally add the modulus. + let mut carry = Word::default(); + for (wr, &wm) in r.iter_mut().zip(C::MODULUS_Q.iter()) { + let t = adc(*wr, wm & borrow, carry); + *wr = t.0; + carry = t.1; + } + r[n - 1] = adc(r[n - 1], 0, carry).0; + } + + // Result is in range (0, 3*n - 1), + // and 90% of the time, no subtraction will be needed. + sub_mod_if_necessary::(&mut r); + sub_mod_if_necessary::(&mut r); + + let mut res = Self::default(); + res.words.copy_from_slice(&r[..k]); + res + } + + /// Returns self * rhs mod n + pub fn mul(&self, rhs: &Self) -> Self { + let mut w = DoubleWords::::default(); + let n = rhs.words.len(); + + // Schoolbook multiplication. + for i in 0..n { + let mut carry = Word::default(); + for j in 0..n { + let t = mac(w[i + j], self.words[i], rhs.words[j], carry); + w[i + j] = t.0; + carry = t.1; + } + w[i + n] = carry; + } + Self::barrett_reduce(w) + } + + pub fn mul_word(&self, rhs: Word) -> Self { + let mut w = DoubleWords::::default(); + let n = self.words.len(); + + // Schoolbook multiplication. + for i in 0..n { + let mut carry = Word::default(); + let t = mac(w[i], self.words[i], rhs, carry); + w[i] = t.0; + carry = t.1; + for j in 1..n { + let t = mac(w[i + j], self.words[i], 0, carry); + w[i + j] = t.0; + carry = t.1; + } + w[i + n] = carry; + } + Self::barrett_reduce(w) + } + + /// Returns self * self mod p + pub fn square(&self) -> Self { + self.mul(self) + } + + /// Returns `self^by`, where `by` is a little-endian integer exponent. + /// + /// **This operation is variable time with respect to the exponent.** + /// If the exponent is fixed, this operation is effectively constant time. + pub fn pow_vartime(&self, by: &Words) -> Self { + let mut res = Self::one(); + for e in by.iter().rev() { + for i in (0..WORD_WIDTH_BITS).rev() { + res = res.square(); + if ((*e >> i) & 1) == 1 { + res *= *self; + } + } + } + res + } + + /// Returns the multiplicative inverse of self, if self is non-zero + pub fn invert(&self) -> CtOption { + // We need to find b such that b * a ≡ 1 mod p. As we are in a prime + // field, we can apply Fermat's Little Theorem: + // + // a^p ≡ a mod p + // a^(p-1) ≡ 1 mod p + // a^(p-2) * a ≡ 1 mod p + // + // Thus inversion can be implemented with a single exponentiation. + // + // This is `n - 2`, so the top right two digits are `4f` instead of `51`. + let inverse = self.pow_vartime(&C::MODULUS_Q_M2); + CtOption::new(inverse, !self.is_zero()) + } + + /// Is integer representing equivalence class odd + pub fn is_odd(&self) -> Choice { + ((self.words[0] & 1) as u8).into() + } + + /// Is integer representing equivalence class even + pub fn is_even(&self) -> Choice { + !self.is_odd() + } + + // fn shr1(&mut self) { + // todo!(); + // } + + /// Faster inversion using Stein's algorithm + pub fn invert_vartime(&self) -> CtOption { + // https://link.springer.com/article/10.1007/s13389-016-0135-4 + todo!(); + } + + pub fn from_bytes(bytes: WordsBytes) -> Option { + let words = B::bytes2biguint(&bytes); + + // If w is in the range [0, n) then w - n will overflow, resulting + // in a borrow value of 2^64 - 1. + let mut borrow = Word::default(); + for (&w, &wm) in words.iter().zip(C::MODULUS_Q.iter()) { + borrow = sbb(w, wm, borrow).1; + } + let is_some = (borrow as u8) & 1; + + CtOption::new(Self { words }, Choice::from(is_some)).into() + } + + /// Parse the given byte array as a scalar. + /// + /// Subtracts the modulus when the byte array is larger than the modulus. + pub fn from_bytes_reduced(bytes: &WordsBytes) -> Self { + let mut words = WordsP1::::default(); + let t = B::bytes2biguint(bytes); + words[..t.len()].copy_from_slice(&t); + + let modulus = Self { words: C::MODULUS_Q }; + let mut res = Self::sub_inner(words, modulus); + for _ in 1..C::MODULUS_Q_REDUCE_N { + res -= modulus; + } + res + } + + /// Returns BE encoding of this scalar. + pub fn to_bytes(&self) -> WordsBytes { + B::biguint2bytes(&self.words) + } +} + + +impl fmt::UpperHex for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + for word in self.words.iter().rev() { + write!(f, "{:016X}", word)?; + } + Ok(()) + } +} + +impl ConditionallySelectable for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let mut res = Self::zero(); + let pairs = a.words.iter().zip(b.words.iter()); + for (vr, (va, vb)) in res.words.iter_mut().zip(pairs) { + *vr = Word::conditional_select(va, vb, choice); + } + res + } +} + +impl ConstantTimeEq for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn ct_eq(&self, other: &Self) -> Choice { + let mut res = 1u8.into(); + for (a, b) in self.words.iter().zip(other.words.iter()) { + res &= a.ct_eq(b); + } + res + } +} + +impl PartialEq for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + fn eq(&self, other: &Self) -> bool { + self.ct_eq(other).into() + } +} + +impl Add> for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn add(self, other: Self) -> Self { + Scalar::add(&self, &other) + } +} + +impl AddAssign> for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + #[inline] + fn add_assign(&mut self, other: Self) { + *self = Scalar::add(self, &other); + } +} + +impl Sub> for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn sub(self, other: Self) -> Self { + Scalar::subtract(&self, &other) + } +} + +impl SubAssign> for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + #[inline] + fn sub_assign(&mut self, other: Self) { + *self = Scalar::subtract(self, &other); + } +} + + +impl Mul> for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn mul(self, other: Self) -> Self { + Scalar::mul(&self, &other) + } +} + +impl MulAssign> for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + #[inline] + fn mul_assign(&mut self, other: Self) { + *self = Scalar::mul(self, &other); + } +} + +impl Mul for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn mul(self, other: Word) -> Self { + Scalar::mul_word(&self, other) + } +} + +impl MulAssign for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + #[inline] + fn mul_assign(&mut self, other: Word) { + *self = Scalar::mul_word(self, other); + } +} + +impl Neg for Scalar + where + C: WeierstrassCurve, + WordsLen: ArrayLength + Add + Shl, + DoubleWordsLen: ArrayLength, + WordsP1Len: ArrayLength, + WordsBytesLen: ArrayLength, + Words: Copy, +{ + type Output = Self; + + #[inline] + fn neg(self) -> Self { + Scalar::zero() - self + } +} + +#[cfg(feature = "zeroize")] +impl Zeroize for Scalar { + fn zeroize(&mut self) { + self.words.zeroize(); + } +} diff --git a/weierstrass/src/utils.rs b/weierstrass/src/utils.rs new file mode 100644 index 00000000..5afd5d73 --- /dev/null +++ b/weierstrass/src/utils.rs @@ -0,0 +1,105 @@ +use crate::{DoubleWord, Word, WordWidth, WORD_WIDTH_BITS}; +use byteorder::{ByteOrder, BigEndian, LittleEndian}; +use generic_array::{GenericArray, ArrayLength, typenum::{Unsigned, Quot}}; +use core::ops::Div; +use core::mem; +use core::convert::TryInto; + +/// Computes `a + b + carry`, returning the result along with the new carry. +#[inline(always)] +pub(crate) const fn adc(a: Word, b: Word, carry: Word) -> (Word, Word) { + let ret = (a as DoubleWord) + (b as DoubleWord) + (carry as DoubleWord); + (ret as Word, (ret >> WORD_WIDTH_BITS) as Word) +} + +/// Computes `a - (b + borrow)`, returning the result along with the new borrow. +#[inline(always)] +pub(crate) const fn sbb(a: Word, b: Word, borrow: Word) -> (Word, Word) { + let (a, b) = (a as DoubleWord, b as DoubleWord); + let t = (borrow >> (WORD_WIDTH_BITS - 1)) as DoubleWord; + let ret = a.wrapping_sub(b + t); + (ret as Word, (ret >> WORD_WIDTH_BITS) as Word) +} + +/// Computes `a + (b * c) + carry`, returning the result along with the new carry. +#[inline(always)] +pub(crate) const fn mac(a: Word, b: Word, c: Word, carry: Word) -> (Word, Word) { + let (a, b, c) = (a as DoubleWord, b as DoubleWord, c as DoubleWord); + let ret = a + b * c + (carry as DoubleWord); + (ret as Word, (ret >> WORD_WIDTH_BITS) as Word) +} + +/// Extension of the `ByteOrder` trait for biguint <-> bytes conversion. +pub trait BigUintExt: ByteOrder { + fn bytes2biguint(bytes: &GenericArray) -> Words + where + N: Unsigned + ArrayLength + Div, + Quot: ArrayLength; + + fn biguint2bytes(words: &Words) -> GenericArray + where + N: Unsigned + ArrayLength + Div, + Quot: ArrayLength; +} + +type Words = GenericArray>; + +impl BigUintExt for BigEndian { + fn bytes2biguint(bytes: &GenericArray) -> Words + where + N: Unsigned + ArrayLength + Div, + Quot: ArrayLength + { + let mut words = Words::::default(); + let m = mem::size_of::(); + let iter = words.iter_mut().zip(bytes.chunks_exact(m).rev()); + for (w, chunk) in iter { + *w = Word::from_be_bytes(chunk.try_into().unwrap()); + } + words + } + + fn biguint2bytes(words: &Words) -> GenericArray + where + N: Unsigned + ArrayLength + Div, + Quot: ArrayLength + { + let mut buf = GenericArray::::default(); + let m = mem::size_of::(); + let iter = buf.chunks_exact_mut(m).zip(words.iter().rev()); + for (chunk, w) in iter { + chunk.copy_from_slice(&w.to_be_bytes()); + } + buf + } +} + +impl BigUintExt for LittleEndian { + fn bytes2biguint(bytes: &GenericArray) -> Words + where + N: Unsigned + ArrayLength + Div, + Quot: ArrayLength + { + let mut words = Words::::default(); + let n = mem::size_of::(); + let iter = words.iter_mut().zip(bytes.chunks_exact(n)); + for (wm, chunk) in iter { + *wm = Word::from_le_bytes(chunk.try_into().unwrap()); + } + words + } + + fn biguint2bytes(words: &Words) -> GenericArray + where + N: Unsigned + ArrayLength + Div, + Quot: ArrayLength + { + let mut buf = GenericArray::::default(); + let m = mem::size_of::(); + let iter = buf.chunks_exact_mut(m).zip(words.iter()); + for (chunk, w) in iter { + chunk.copy_from_slice(&w.to_le_bytes()); + } + buf + } +}