Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vartime point-scalar multiplication #708

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 99 additions & 28 deletions k256/src/arithmetic/field.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ cfg_if! {
use crate::FieldBytes;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use elliptic_curve::{
ff::Field,
group::ff::{helpers::sqrt_ratio_generic, Field, PrimeField},
rand_core::RngCore,
subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption},
zeroize::DefaultIsZeroes,
Expand All @@ -48,6 +48,10 @@ use num_bigint::{BigUint, ToBigUint};
pub struct FieldElement(FieldElementImpl);

impl Field for FieldElement {
const ZERO: Self = Self::ZERO;

const ONE: Self = Self::ONE;

fn random(mut rng: impl RngCore) -> Self {
let mut bytes = FieldBytes::default();

Expand All @@ -59,14 +63,6 @@ impl Field for FieldElement {
}
}

fn zero() -> Self {
Self::ZERO
}

fn one() -> Self {
Self::ONE
}

#[must_use]
fn square(&self) -> Self {
self.square()
Expand All @@ -81,11 +77,63 @@ impl Field for FieldElement {
self.invert()
}

fn sqrt_ratio(num: &Self, div: &Self) -> (Choice, Self) {
sqrt_ratio_generic(num, div)
}

fn sqrt(&self) -> CtOption<Self> {
self.sqrt()
}
}

impl From<u64> for FieldElement {
fn from(k: u64) -> Self {
Self(FieldElementImpl::from_u64(k))
}
}

impl PrimeField for FieldElement {
type Repr = FieldBytes;

const NUM_BITS: u32 = 256;
const CAPACITY: u32 = 255;
const S: u32 = 1;

const MULTIPLICATIVE_GENERATOR: Self = Self(FieldElementImpl::from_u64(3));

const ROOT_OF_UNITY: Self = Self(FieldElementImpl::from_bytes_unchecked(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
0xfc, 0x2e,
]));

const MODULUS: &'static str =
"0xFFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFF_FFFFFFFE_FFFFFC2F";
const TWO_INV: Self = Self(FieldElementImpl::from_bytes_unchecked(&[
0x7f, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f, 0xff,
0xfe, 0x18,
]));
const ROOT_OF_UNITY_INV: Self = Self(FieldElementImpl::from_bytes_unchecked(&[
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0xff, 0xff,
0xfc, 0x2e,
]));
const DELTA: Self = Self(FieldElementImpl::from_u64(9));

fn from_repr(bytes: Self::Repr) -> CtOption<Self> {
Self::from_bytes(&bytes)
}

fn to_repr(&self) -> Self::Repr {
self.to_bytes()
}

fn is_odd(&self) -> Choice {
self.0.is_odd()
}
}

impl FieldElement {
/// Zero element.
pub const ZERO: Self = Self(FieldElementImpl::zero());
Expand Down Expand Up @@ -272,7 +320,7 @@ impl FieldElement {

#[cfg(test)]
pub fn modulus_as_biguint() -> BigUint {
Self::one().negate(1).to_biguint().unwrap() + 1.to_biguint().unwrap()
Self::ONE.negate(1).to_biguint().unwrap() + 1.to_biguint().unwrap()
}
}

Expand All @@ -290,7 +338,7 @@ impl ConstantTimeEq for FieldElement {

impl Default for FieldElement {
fn default() -> Self {
Self::zero()
Self::ZERO
}
}

Expand Down Expand Up @@ -420,9 +468,32 @@ impl Neg for &FieldElement {
}
}

impl core::iter::Sum for FieldElement {
fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(core::ops::Add::add).unwrap_or(Self::ZERO)
}
}

impl<'a> core::iter::Sum<&'a Self> for FieldElement {
fn sum<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.cloned().sum()
}
}

impl core::iter::Product for FieldElement {
fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
iter.reduce(core::ops::Mul::mul).unwrap_or(Self::ONE)
}
}

impl<'a> core::iter::Product<&'a Self> for FieldElement {
fn product<I: Iterator<Item = &'a Self>>(iter: I) -> Self {
iter.cloned().product()
}
}

#[cfg(test)]
mod tests {
use elliptic_curve::ff::Field;
use num_bigint::{BigUint, ToBigUint};
use proptest::prelude::*;

Expand All @@ -448,23 +519,23 @@ mod tests {

#[test]
fn zero_is_additive_identity() {
let zero = FieldElement::zero();
let one = FieldElement::one();
let zero = FieldElement::ZERO;
let one = FieldElement::ONE;
assert_eq!((zero + &zero).normalize(), zero);
assert_eq!((one + &zero).normalize(), one);
}

#[test]
fn one_is_multiplicative_identity() {
let one = FieldElement::one();
let one = FieldElement::ONE;
assert_eq!((one * &one).normalize(), one);
}

#[test]
fn from_bytes() {
assert_eq!(
FieldElement::from_bytes(&FieldBytes::default()).unwrap(),
FieldElement::zero()
FieldElement::ZERO
);
assert_eq!(
FieldElement::from_bytes(
Expand All @@ -475,7 +546,7 @@ mod tests {
.into()
)
.unwrap(),
FieldElement::one()
FieldElement::ONE
);
assert!(bool::from(
FieldElement::from_bytes(&[0xff; 32].into()).is_none()
Expand All @@ -484,9 +555,9 @@ mod tests {

#[test]
fn to_bytes() {
assert_eq!(FieldElement::zero().to_bytes(), [0; 32].into());
assert_eq!(FieldElement::ZERO.to_bytes(), [0; 32].into());
assert_eq!(
FieldElement::one().to_bytes(),
FieldElement::ONE.to_bytes(),
[
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 1
Expand All @@ -497,7 +568,7 @@ mod tests {

#[test]
fn repeated_add() {
let mut r = FieldElement::one();
let mut r = FieldElement::ONE;
for i in 0..DBL_TEST_VECTORS.len() {
assert_eq!(r.to_bytes(), DBL_TEST_VECTORS[i].into());
r = (r + &r).normalize();
Expand All @@ -506,7 +577,7 @@ mod tests {

#[test]
fn repeated_double() {
let mut r = FieldElement::one();
let mut r = FieldElement::ONE;
for i in 0..DBL_TEST_VECTORS.len() {
assert_eq!(r.to_bytes(), DBL_TEST_VECTORS[i].into());
r = r.double().normalize();
Expand All @@ -515,7 +586,7 @@ mod tests {

#[test]
fn repeated_mul() {
let mut r = FieldElement::one();
let mut r = FieldElement::ONE;
let two = r + &r;
for i in 0..DBL_TEST_VECTORS.len() {
assert_eq!(r.normalize().to_bytes(), DBL_TEST_VECTORS[i].into());
Expand All @@ -525,17 +596,17 @@ mod tests {

#[test]
fn negation() {
let two = FieldElement::one().double();
let two = FieldElement::ONE.double();
let neg_two = two.negate(2);
assert_eq!((two + &neg_two).normalize(), FieldElement::zero());
assert_eq!((two + &neg_two).normalize(), FieldElement::ZERO);
assert_eq!(neg_two.negate(3).normalize(), two.normalize());
}

#[test]
fn invert() {
assert!(bool::from(FieldElement::zero().invert().is_none()));
assert!(bool::from(FieldElement::ZERO.invert().is_none()));

let one = FieldElement::one();
let one = FieldElement::ONE;
assert_eq!(one.invert().unwrap().normalize(), one);

let two = one + &one;
Expand All @@ -545,7 +616,7 @@ mod tests {

#[test]
fn sqrt() {
let one = FieldElement::one();
let one = FieldElement::ONE;
let two = one + &one;
let four = two.square();
assert_eq!(four.sqrt().unwrap().normalize(), two.normalize());
Expand Down Expand Up @@ -663,7 +734,7 @@ mod tests {
fn fuzzy_invert(
a in field_element()
) {
let a = if bool::from(a.is_zero()) { FieldElement::one() } else { a };
let a = if bool::from(a.is_zero()) { FieldElement::ONE } else { a };
let a_bi = a.to_biguint().unwrap();
let inv = a.invert().unwrap().normalize();
let inv_bi = inv.to_biguint().unwrap();
Expand Down
8 changes: 8 additions & 0 deletions k256/src/arithmetic/field/field_10x26.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,14 @@ impl FieldElement10x26 {
CtOption::new(res, !overflow)
}

pub const fn from_u64(val: u64) -> Self {
let w0 = (val as u32) & 0x3FFFFFF;
let val = val >> 26;
let w1 = (val as u32) & 0x3FFFFFF;
let w2 = (val >> 26) as u32;
Self([w0, w1, w2, 0, 0, 0, 0, 0, 0, 0])
}

/// Returns the SEC1 encoding of this field element.
pub fn to_bytes(self) -> FieldBytes {
let mut r = FieldBytes::default();
Expand Down
6 changes: 6 additions & 0 deletions k256/src/arithmetic/field/field_5x52.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,12 @@ impl FieldElement5x52 {
CtOption::new(res, !overflow)
}

pub const fn from_u64(val: u64) -> Self {
let w0 = val & 0xFFFFFFFFFFFFF;
let w1 = val >> 52;
Self([w0, w1, 0, 0, 0])
}

/// Returns the SEC1 encoding of this field element.
pub fn to_bytes(self) -> FieldBytes {
let mut ret = FieldBytes::default();
Expand Down
4 changes: 4 additions & 0 deletions k256/src/arithmetic/field/field_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ impl FieldElementImpl {
Self::new_normalized(&value)
}

pub(crate) const fn from_u64(val: u64) -> Self {
Self::new_normalized(&FieldElementUnsafeImpl::from_u64(val))
}

pub fn from_bytes(bytes: &FieldBytes) -> CtOption<Self> {
let value = FieldElementUnsafeImpl::from_bytes(bytes);
CtOption::map(value, |x| Self::new_normalized(&x))
Expand Down
4 changes: 2 additions & 2 deletions k256/src/arithmetic/hash2curve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl OsswuMap for FieldElement {
const PARAMS: OsswuMapParams<Self> = OsswuMapParams {
// See section 8.7 in
// <https://datatracker.ietf.org/doc/draft-irtf-cfrg-hash-to-curve/>
c1: [
c1: &[
0xffff_ffff_bfff_ff0b,
0xffff_ffff_ffff_ffff,
0xffff_ffff_ffff_ffff,
Expand Down Expand Up @@ -88,7 +88,7 @@ impl OsswuMap for FieldElement {
let tv3 = Self::PARAMS.z * tv1; // Z * u^2
let mut tv2 = tv3.square(); // tv3^2
let mut xd = tv2 + tv3; // tv3^2 + tv3
let x1n = Self::PARAMS.map_b * (xd + Self::one()); // B * (xd + 1)
let x1n = Self::PARAMS.map_b * (xd + Self::ONE); // B * (xd + 1)
xd = (xd * Self::PARAMS.map_a.negate(1)).normalize(); // -A * xd

let tv = Self::PARAMS.z * Self::PARAMS.map_a;
Expand Down
2 changes: 1 addition & 1 deletion k256/src/arithmetic/projective.rs
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ impl From<AffinePoint> for ProjectivePoint {
let projective = ProjectivePoint {
x: p.x,
y: p.y,
z: FieldElement::one(),
z: FieldElement::ONE,
};
Self::conditional_select(&projective, &Self::IDENTITY, p.is_identity())
}
Expand Down
Loading