Optimize SIDH code for about a 25% speed improvement.

This commit is contained in:
Adam Ierymenko 2021-11-11 15:29:35 -05:00
parent c7b2a3703e
commit 184c4aede1
No known key found for this signature in database
GPG key ID: C8877CF2D7A5D7F3
14 changed files with 358 additions and 120 deletions

View file

@ -17,7 +17,7 @@ pub mod random;
pub mod secret; pub mod secret;
pub mod hex; pub mod hex;
pub mod varint; pub mod varint;
pub mod sidh; pub mod sidhp751;
pub use aes_gmac_siv; pub use aes_gmac_siv;
pub use rand_core; pub use rand_core;

View file

@ -6,8 +6,8 @@
// - Erkan Tairi <erkan.tairi@gmail.com> // - Erkan Tairi <erkan.tairi@gmail.com>
// //
use crate::sidh::field::PrimeFieldElement; use crate::sidhp751::field::PrimeFieldElement;
use crate::sidh::fp::Fp751Element; use crate::sidhp751::fp::Fp751Element;
/// The x-coordinate of `P_A = [3^239](11, oddsqrt(11^3 + 11))` on `E_0(F_p)`. /// The x-coordinate of `P_A = [3^239](11, oddsqrt(11^3 + 11))` on `E_0(F_p)`.
pub const AFFINE_X_PA: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x27914862, 0xd56fe526, 0x96b5baea, 0x1fad60dc, 0xbf07ab91, 0x1e137d0, 0x52161964, 0x404d3e92, 0xcd09a337, 0x3c5385e4, 0x69e4af73, 0x44764267, 0x989dfe33, 0x9790c6db, 0xd2aa8b5e, 0xe06e1c04, 0xedea73b9, 0x38c08185, 0xa4396ca6, 0xaa41f678, 0x2229e9a0, 0x92b9259b, 0x26818be0, 0x2f93]) }; pub const AFFINE_X_PA: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x27914862, 0xd56fe526, 0x96b5baea, 0x1fad60dc, 0xbf07ab91, 0x1e137d0, 0x52161964, 0x404d3e92, 0xcd09a337, 0x3c5385e4, 0x69e4af73, 0x44764267, 0x989dfe33, 0x9790c6db, 0xd2aa8b5e, 0xe06e1c04, 0xedea73b9, 0x38c08185, 0xa4396ca6, 0xaa41f678, 0x2229e9a0, 0x92b9259b, 0x26818be0, 0x2f93]) };

View file

@ -9,9 +9,9 @@
//! This module contains internal curve representation and operations //! This module contains internal curve representation and operations
//! for SIDH, which is not part of the public API. //! for SIDH, which is not part of the public API.
use crate::sidh::fp::Fp751Element; use crate::sidhp751::fp::Fp751Element;
use crate::sidh::field::{PrimeFieldElement, ExtensionFieldElement}; use crate::sidhp751::field::{PrimeFieldElement, ExtensionFieldElement};
use crate::sidh::constants::*; use crate::sidhp751::constants::*;
use std::fmt::Debug; use std::fmt::Debug;
use std::ops::Neg; use std::ops::Neg;
@ -19,6 +19,7 @@ use subtle::{ConditionallySelectable, Choice};
#[cfg(test)] #[cfg(test)]
use quickcheck::{Gen, Arbitrary}; use quickcheck::{Gen, Arbitrary};
use std::mem::zeroed;
// Macro to assign tuples, as Rust does not allow tuples as lvalue. // Macro to assign tuples, as Rust does not allow tuples as lvalue.
macro_rules! assign{ macro_rules! assign{
@ -41,17 +42,20 @@ const CONST_256: ExtensionFieldElement = ExtensionFieldElement {
/// A point on the projective line `P^1(F_{p^2})`. /// A point on the projective line `P^1(F_{p^2})`.
/// ///
/// This is used to work projectively with the curve coefficients. /// This is used to work projectively with the curve coefficients.
#[allow(non_snake_case)]
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct ProjectiveCurveParameters { pub struct ProjectiveCurveParameters {
pub A: ExtensionFieldElement, pub A: ExtensionFieldElement,
pub C: ExtensionFieldElement, pub C: ExtensionFieldElement,
} }
#[allow(non_snake_case)]
struct CachedCurveParameters { struct CachedCurveParameters {
Aplus2C: ExtensionFieldElement, Aplus2C: ExtensionFieldElement,
C4: ExtensionFieldElement, C4: ExtensionFieldElement,
} }
#[allow(non_snake_case)]
struct CachedTripleCurveParameters { struct CachedTripleCurveParameters {
Aminus2C: ExtensionFieldElement, Aminus2C: ExtensionFieldElement,
C2: ExtensionFieldElement, C2: ExtensionFieldElement,
@ -79,8 +83,10 @@ impl ProjectiveCurveParameters {
C: ExtensionFieldElement::one() C: ExtensionFieldElement::one()
} }
} }
/// Recover the curve parameters from three points on the curve. /// Recover the curve parameters from three points on the curve.
pub fn recover_curve_parameters(affine_xP: &ExtensionFieldElement, affine_xQ: &ExtensionFieldElement, affine_xQmP: &ExtensionFieldElement) -> #[allow(non_snake_case)]
pub fn recover_curve_parameters(affine_xP: &ExtensionFieldElement, affine_xQ: &ExtensionFieldElement, affine_xQmP: &ExtensionFieldElement) ->
ProjectiveCurveParameters ProjectiveCurveParameters
{ {
let mut t0 = ExtensionFieldElement::one(); // = 1 let mut t0 = ExtensionFieldElement::one(); // = 1
@ -101,6 +107,7 @@ impl ProjectiveCurveParameters {
ProjectiveCurveParameters{ A: a, C: c } ProjectiveCurveParameters{ A: a, C: c }
} }
/// Compute the j-invariant of the given curve. /// Compute the j-invariant of the given curve.
pub fn j_invariant(&self) -> ExtensionFieldElement { pub fn j_invariant(&self) -> ExtensionFieldElement {
let a = &self.A; let a = &self.A;
@ -122,7 +129,9 @@ impl ProjectiveCurveParameters {
v0 v0
} }
/// Compute cached parameters `A + 2C, 4C`. /// Compute cached parameters `A + 2C, 4C`.
#[allow(non_snake_case)]
fn cached_params(&self) -> CachedCurveParameters { fn cached_params(&self) -> CachedCurveParameters {
let mut Aplus2C = &self.C + &self.C; // = 2*C let mut Aplus2C = &self.C + &self.C; // = 2*C
let C4 = &Aplus2C + &Aplus2C; // = 4*C let C4 = &Aplus2C + &Aplus2C; // = 4*C
@ -130,7 +139,9 @@ impl ProjectiveCurveParameters {
CachedCurveParameters{ Aplus2C, C4 } CachedCurveParameters{ Aplus2C, C4 }
} }
/// Compute cached parameters `A - 2C, 2C`. /// Compute cached parameters `A - 2C, 2C`.
#[allow(non_snake_case)]
fn cached_triple_params(&self) -> CachedTripleCurveParameters { fn cached_triple_params(&self) -> CachedTripleCurveParameters {
let C2 = &self.C + &self.C; // = 2*C let C2 = &self.C + &self.C; // = 2*C
let Aminus2C = &self.A - &C2; // = A -2*C let Aminus2C = &self.A - &C2; // = A -2*C
@ -143,6 +154,7 @@ impl ProjectiveCurveParameters {
/// ///
/// This represents a point on the (Kummer line) of a Montgomery curve. The /// This represents a point on the (Kummer line) of a Montgomery curve. The
/// curve is specified by a ProjectiveCurveParameters struct. /// curve is specified by a ProjectiveCurveParameters struct.
#[allow(non_snake_case)]
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct ProjectivePoint { pub struct ProjectivePoint {
pub X: ExtensionFieldElement, pub X: ExtensionFieldElement,
@ -181,9 +193,11 @@ impl Arbitrary for ProjectivePoint {
impl ProjectivePoint { impl ProjectivePoint {
/// Creates a new zero `ProejctivePoint`. /// Creates a new zero `ProejctivePoint`.
pub fn new() -> ProjectivePoint { pub fn new() -> ProjectivePoint {
ProjectivePoint{ X: ExtensionFieldElement::zero(), Z: ExtensionFieldElement::zero() } unsafe { zeroed() }
//ProjectivePoint{ X: ExtensionFieldElement::zero(), Z: ExtensionFieldElement::zero() }
} }
#[allow(non_snake_case)]
pub fn from_affine_prime_field(x: &PrimeFieldElement) -> ProjectivePoint { pub fn from_affine_prime_field(x: &PrimeFieldElement) -> ProjectivePoint {
let _X = ExtensionFieldElement{ A: x.A, B: ExtensionFieldElement::zero().B }; let _X = ExtensionFieldElement{ A: x.A, B: ExtensionFieldElement::zero().B };
ProjectivePoint{ ProjectivePoint{
@ -203,13 +217,16 @@ impl ProjectivePoint {
let affine_x = &self.Z.inv() * &self.X; let affine_x = &self.Z.inv() * &self.X;
affine_x affine_x
} }
/// Returns true if both sides are equal. Takes variable time. /// Returns true if both sides are equal. Takes variable time.
pub fn vartime_eq(&self, _rhs: &ProjectivePoint) -> bool { pub fn vartime_eq(&self, _rhs: &ProjectivePoint) -> bool {
let t0 = &self.X * &_rhs.Z; let t0 = &self.X * &_rhs.Z;
let t1 = &self.Z * &_rhs.X; let t1 = &self.Z * &_rhs.X;
t0.vartime_eq(&t1) t0.vartime_eq(&t1)
} }
/// Given `xP = x(P), xQ = x(Q)`, and `xPmQ = x(P-Q)`, compute `xR = x(P+Q)`. /// Given `xP = x(P), xQ = x(Q)`, and `xPmQ = x(P-Q)`, compute `xR = x(P+Q)`.
#[allow(non_snake_case)]
fn add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint) -> ProjectivePoint { fn add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint) -> ProjectivePoint {
let xP = *self; let xP = *self;
// Algorithm 1 of Costello-Smith. // Algorithm 1 of Costello-Smith.
@ -225,7 +242,9 @@ impl ProjectivePoint {
ProjectivePoint{ X: x, Z: z } ProjectivePoint{ X: x, Z: z }
} }
/// Given `xP = x(P)` and cached curve parameters `Aplus2C = A + 2*C, C4 = 4*C`, compute `xQ = x([2]P)`. /// Given `xP = x(P)` and cached curve parameters `Aplus2C = A + 2*C, C4 = 4*C`, compute `xQ = x([2]P)`.
#[allow(non_snake_case)]
fn double(&self, curve: &CachedCurveParameters) -> ProjectivePoint { fn double(&self, curve: &CachedCurveParameters) -> ProjectivePoint {
let xP = *self; let xP = *self;
// Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients. // Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients.
@ -243,9 +262,11 @@ impl ProjectivePoint {
// = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ ) // = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ )
ProjectivePoint{ X: x, Z: z } ProjectivePoint{ X: x, Z: z }
} }
/// Calculates the x-coordinate of `2P` and `P+Q` from the x-coordinate of `P, Q` and `P-Q`. /// Calculates the x-coordinate of `2P` and `P+Q` from the x-coordinate of `P, Q` and `P-Q`.
// Params: `C4 = 4*C` and `Aplus2C = (A+2C)` // Params: `C4 = 4*C` and `Aplus2C = (A+2C)`
// Cost: 8M+4S+8A in `Fp2` // Cost: 8M+4S+8A in `Fp2`
#[allow(non_snake_case)]
fn dbl_add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, params: &CachedCurveParameters) -> fn dbl_add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, params: &CachedCurveParameters) ->
(ProjectivePoint, ProjectivePoint) (ProjectivePoint, ProjectivePoint)
{ {
@ -281,17 +302,21 @@ impl ProjectivePoint {
(x2P, xPaddQ) (x2P, xPaddQ)
} }
/// Given the curve parameters, `xP = x(P)`, and `k >= 0`, compute `xQ = x([2^k]P)`. /// Given the curve parameters, `xP = x(P)`, and `k >= 0`, compute `xQ = x([2^k]P)`.
#[allow(non_snake_case)]
pub fn pow2k(&self, curve: &ProjectiveCurveParameters, k: u32) -> ProjectivePoint { pub fn pow2k(&self, curve: &ProjectiveCurveParameters, k: u32) -> ProjectivePoint {
let cached_params = curve.cached_params(); let cached_params = curve.cached_params();
let mut xQ = *self; let mut xQ = *self;
for _ in 0..k { xQ = xQ.double(&cached_params); } for _ in 0..k { xQ = xQ.double(&cached_params); }
xQ xQ
} }
// Uses the efficient Montgomery tripling formulas from FLOR-SIDH-x64 // Uses the efficient Montgomery tripling formulas from FLOR-SIDH-x64
// Reference: A faster SW implementation of SIDH (github.com/armfazh/flor-sidh-x64). // Reference: A faster SW implementation of SIDH (github.com/armfazh/flor-sidh-x64).
/// Given `xP = x(P)` and cached tripling curve parameters `Aminus2C = A - 2*C, C2 = 2*C`, compute `xQ = x([3]P)`. /// Given `xP = x(P)` and cached tripling curve parameters `Aminus2C = A - 2*C, C2 = 2*C`, compute `xQ = x([3]P)`.
/// Returns `xQ` to allow chaining. /// Returns `xQ` to allow chaining.
#[allow(non_snake_case)]
fn triple(&self, curve: &CachedTripleCurveParameters) -> ProjectivePoint { fn triple(&self, curve: &CachedTripleCurveParameters) -> ProjectivePoint {
let xP = *self; let xP = *self;
let (x1, z1) = (&xP.X, &xP.Z); let (x1, z1) = (&xP.X, &xP.Z);
@ -319,19 +344,23 @@ impl ProjectivePoint {
ProjectivePoint{ X: x, Z: z } ProjectivePoint{ X: x, Z: z }
} }
/// Given the curve parameters, `xP = x(P)`, and `k >= 0`, compute `xQ = x([3^k]P)`. /// Given the curve parameters, `xP = x(P)`, and `k >= 0`, compute `xQ = x([3^k]P)`.
#[allow(non_snake_case)]
pub fn pow3k(&self, curve: &ProjectiveCurveParameters, k: u32) -> ProjectivePoint { pub fn pow3k(&self, curve: &ProjectiveCurveParameters, k: u32) -> ProjectivePoint {
let cached_params = curve.cached_triple_params(); let cached_params = curve.cached_triple_params();
let mut xQ = *self; let mut xQ = *self;
for _ in 0..k { xQ = xQ.triple(&cached_params); } for _ in 0..k { xQ = xQ.triple(&cached_params); }
xQ xQ
} }
/// Given `x(P)` and a scalar `m` in little-endian bytes, compute `x([m]P)` using the /// Given `x(P)` and a scalar `m` in little-endian bytes, compute `x([m]P)` using the
/// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith. /// Montgomery ladder. This is described in Algorithm 8 of Costello-Smith.
/// ///
/// This function's execution time is dependent only on the byte-length of the /// This function's execution time is dependent only on the byte-length of the
/// input scalar. All scalars of the same input length execute in uniform time. /// input scalar. All scalars of the same input length execute in uniform time.
/// The scalar can be padded with zero bytes to ensure a uniform length. /// The scalar can be padded with zero bytes to ensure a uniform length.
#[allow(non_snake_case)]
fn scalar_mul(&self, curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint { fn scalar_mul(&self, curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint {
let xP = *self; let xP = *self;
let cached_params = curve.cached_params(); let cached_params = curve.cached_params();
@ -357,12 +386,14 @@ impl ProjectivePoint {
let xQ = x0; let xQ = x0;
xQ xQ
} }
/// Given `P = (x_P, y_P)` in affine coordinates, as well as projective points /// Given `P = (x_P, y_P)` in affine coordinates, as well as projective points
/// `x(Q), x(R) = x(P+Q)`, all in the prime-field subgroup of the starting curve /// `x(Q), x(R) = x(P+Q)`, all in the prime-field subgroup of the starting curve
/// `E_0(F_p)`, use the Okeya-Sakurai coordinate recovery strategy to recover `Q = /// `E_0(F_p)`, use the Okeya-Sakurai coordinate recovery strategy to recover `Q =
/// (X_Q : Y_Q : Z_Q)`. /// (X_Q : Y_Q : Z_Q)`.
/// ///
/// This is Algorithm 5 of Costello-Smith, with the constants `a = 0, b = 1` hardcoded. /// This is Algorithm 5 of Costello-Smith, with the constants `a = 0, b = 1` hardcoded.
#[allow(non_snake_case)]
fn okeya_sakurai_coordinate_recovery(affine_xP: &PrimeFieldElement, affine_yP: &PrimeFieldElement, fn okeya_sakurai_coordinate_recovery(affine_xP: &PrimeFieldElement, affine_yP: &PrimeFieldElement,
xQ: &ProjectivePrimeFieldPoint, xR: &ProjectivePrimeFieldPoint) -> xQ: &ProjectivePrimeFieldPoint, xR: &ProjectivePrimeFieldPoint) ->
(PrimeFieldElement, PrimeFieldElement, PrimeFieldElement) (PrimeFieldElement, PrimeFieldElement, PrimeFieldElement)
@ -387,6 +418,7 @@ impl ProjectivePoint {
(X_Q, Y_Q, Z_Q) (X_Q, Y_Q, Z_Q)
} }
/// Given `x(P), x(Q), x(P-Q)`, as well as a scalar m in little-endian bytes, /// Given `x(P), x(Q), x(P-Q)`, as well as a scalar m in little-endian bytes,
/// compute `x(P + [m]Q)` using the "three-point ladder" of de Feo, Jao, and Plut. /// compute `x(P + [m]Q)` using the "three-point ladder" of de Feo, Jao, and Plut.
/// ///
@ -427,7 +459,8 @@ impl ProjectivePoint {
// //
// return x2 // return x2
// //
pub fn three_point_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, #[allow(non_snake_case)]
pub fn three_point_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint,
curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint
{ {
let cached_params = curve.cached_params(); let cached_params = curve.cached_params();
@ -457,8 +490,10 @@ impl ProjectivePoint {
let xR = x2; let xR = x2;
xR xR
} }
/// Right-to-left point multiplication, which given the x-coordinate /// Right-to-left point multiplication, which given the x-coordinate
/// of `P, Q` and `P-Q` calculates the x-coordinate of `R=P+[k]Q`. /// of `P, Q` and `P-Q` calculates the x-coordinate of `R=P+[k]Q`.
#[allow(non_snake_case)]
pub fn right_to_left_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, pub fn right_to_left_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint,
curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint
{ {
@ -482,8 +517,10 @@ impl ProjectivePoint {
let xR = R1; let xR = R1;
xR xR
} }
/// Given the affine x-coordinate `affine_xP` of `P`, compute the x-coordinate /// Given the affine x-coordinate `affine_xP` of `P`, compute the x-coordinate
/// `x(\tau(P)-P) of \tau(P)-P`. /// `x(\tau(P)-P) of \tau(P)-P`.
#[allow(non_snake_case)]
pub fn distort_and_difference(affine_xP: &PrimeFieldElement) -> ProjectivePoint { pub fn distort_and_difference(affine_xP: &PrimeFieldElement) -> ProjectivePoint {
let mut t0 = affine_xP.square(); // = x_P^2 let mut t0 = affine_xP.square(); // = x_P^2
let t1 = &PrimeFieldElement::one() + &t0; // = x_P^2 + 1 let t1 = &PrimeFieldElement::one() + &t0; // = x_P^2 + 1
@ -496,6 +533,7 @@ impl ProjectivePoint {
let xR = ProjectivePoint{ X: x, Z: z }; let xR = ProjectivePoint{ X: x, Z: z };
xR xR
} }
/// Given an affine point `P = (x_P, y_P)` in the prime-field subgroup of the /// Given an affine point `P = (x_P, y_P)` in the prime-field subgroup of the
/// starting curve `E_0(F_p)`, together with a secret scalar `m`, compute `x(P+[m]Q)`, /// starting curve `E_0(F_p)`, together with a secret scalar `m`, compute `x(P+[m]Q)`,
/// where `Q = \tau(P)` is the image of `P` under the distortion map. /// where `Q = \tau(P)` is the image of `P` under the distortion map.
@ -589,6 +627,7 @@ impl ProjectivePoint {
// These formulas could probably be combined with the formulas for y-recover // These formulas could probably be combined with the formulas for y-recover
// and computed more efficiently, but efficiency isn't the biggest concern // and computed more efficiently, but efficiency isn't the biggest concern
// here, since the bulk of the cost is already in the ladder. // here, since the bulk of the cost is already in the ladder.
#[allow(non_snake_case)]
pub fn secret_point(affine_xP: &PrimeFieldElement, affine_yP: &PrimeFieldElement, scalar: &[u8]) -> ProjectivePoint { pub fn secret_point(affine_xP: &PrimeFieldElement, affine_yP: &PrimeFieldElement, scalar: &[u8]) -> ProjectivePoint {
let mut xQ = ProjectivePrimeFieldPoint::from_affine(affine_xP); let mut xQ = ProjectivePrimeFieldPoint::from_affine(affine_xP);
xQ.X = (&xQ.X).neg(); xQ.X = (&xQ.X).neg();
@ -653,6 +692,7 @@ impl ProjectivePoint {
/// ///
/// This represents a point on the (Kummer line) of the prime-field subgroup of /// This represents a point on the (Kummer line) of the prime-field subgroup of
/// the base curve `E_0(F_p)`, defined by `E_0 : y^2 = x^3 + x`. /// the base curve `E_0(F_p)`, defined by `E_0 : y^2 = x^3 + x`.
#[allow(non_snake_case)]
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
struct ProjectivePrimeFieldPoint { struct ProjectivePrimeFieldPoint {
X: PrimeFieldElement, X: PrimeFieldElement,
@ -691,7 +731,8 @@ impl Arbitrary for ProjectivePrimeFieldPoint {
impl ProjectivePrimeFieldPoint { impl ProjectivePrimeFieldPoint {
/// Creates a new zero `ProjectivePrimeFieldPoint`. /// Creates a new zero `ProjectivePrimeFieldPoint`.
pub fn new() -> ProjectivePrimeFieldPoint { pub fn new() -> ProjectivePrimeFieldPoint {
ProjectivePrimeFieldPoint{ X: PrimeFieldElement::zero(), Z: PrimeFieldElement::zero() } unsafe { zeroed() }
//ProjectivePrimeFieldPoint{ X: PrimeFieldElement::zero(), Z: PrimeFieldElement::zero() }
} }
pub fn from_affine(x: &PrimeFieldElement) -> ProjectivePrimeFieldPoint { pub fn from_affine(x: &PrimeFieldElement) -> ProjectivePrimeFieldPoint {
@ -702,17 +743,19 @@ impl ProjectivePrimeFieldPoint {
} }
pub fn to_affine(&self) -> PrimeFieldElement { pub fn to_affine(&self) -> PrimeFieldElement {
let affine_x = &self.Z.inv() * &self.X; &self.Z.inv() * &self.X
affine_x
} }
/// Returns true if both sides are equal. Takes variable time. /// Returns true if both sides are equal. Takes variable time.
pub fn vartime_eq(&self, _rhs: &ProjectivePrimeFieldPoint) -> bool { pub fn vartime_eq(&self, _rhs: &ProjectivePrimeFieldPoint) -> bool {
let t0 = &self.X * &_rhs.Z; let t0 = &self.X * &_rhs.Z;
let t1 = &self.Z * &_rhs.X; let t1 = &self.Z * &_rhs.X;
t0.vartime_eq(&t1) t0.vartime_eq(&t1)
} }
/// Given `xP = x(P), xQ = x(Q)`, and `xPmQ = x(P-Q)`, compute `xR = x(P+Q)`. /// Given `xP = x(P), xQ = x(Q)`, and `xPmQ = x(P-Q)`, compute `xR = x(P+Q)`.
fn add(&self, xQ: &ProjectivePrimeFieldPoint, xPmQ: &ProjectivePrimeFieldPoint) -> #[allow(non_snake_case)]
fn add(&self, xQ: &ProjectivePrimeFieldPoint, xPmQ: &ProjectivePrimeFieldPoint) ->
ProjectivePrimeFieldPoint ProjectivePrimeFieldPoint
{ {
let xP = *self; let xP = *self;
@ -729,10 +772,12 @@ impl ProjectivePrimeFieldPoint {
ProjectivePrimeFieldPoint{ X: x, Z: z } ProjectivePrimeFieldPoint{ X: x, Z: z }
} }
/// Given `xP = x(P)` and cached curve parameter `aPlus2Over4 = (a+2)/4, compute xQ = x([2]P)`. /// Given `xP = x(P)` and cached curve parameter `aPlus2Over4 = (a+2)/4, compute xQ = x([2]P)`.
// //
// Note that we don't use projective curve coefficients here because we only // Note that we don't use projective curve coefficients here because we only
// ever use a fixed curve (in our case, the base curve E_0). // ever use a fixed curve (in our case, the base curve E_0).
#[allow(non_snake_case)]
fn double(&self, aPlus2Over4: &PrimeFieldElement) -> ProjectivePrimeFieldPoint { fn double(&self, aPlus2Over4: &PrimeFieldElement) -> ProjectivePrimeFieldPoint {
let xP = *self; let xP = *self;
// Algorithm 2 of Costello-Smith // Algorithm 2 of Costello-Smith
@ -747,11 +792,13 @@ impl ProjectivePrimeFieldPoint {
// = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ ) // = ((X+Z)^2(X-Z)^2 : (4XZ((a + 2)/4) + (X-Z)^2)4XZ )
ProjectivePrimeFieldPoint{ X: x, Z: z } ProjectivePrimeFieldPoint{ X: x, Z: z }
} }
/// Calculates the x-coordinate of `2P` and `P+Q` from the x-coordinate of `P, Q` and `P-Q`. /// Calculates the x-coordinate of `2P` and `P+Q` from the x-coordinate of `P, Q` and `P-Q`.
// Assumptions: // Assumptions:
// aPlus2Over2 = (A+2)/4. // aPlus2Over2 = (A+2)/4.
// z(P-Q) = 1, the Z-coordinate of P-Q is equal to 1. // z(P-Q) = 1, the Z-coordinate of P-Q is equal to 1.
// Cost: 6M+4S+8A in Fp // Cost: 6M+4S+8A in Fp
#[allow(non_snake_case)]
fn dbl_add(&self, xQ: &ProjectivePrimeFieldPoint, xPmQ: &ProjectivePrimeFieldPoint, aPlus2Over4: &PrimeFieldElement) -> fn dbl_add(&self, xQ: &ProjectivePrimeFieldPoint, xPmQ: &ProjectivePrimeFieldPoint, aPlus2Over4: &PrimeFieldElement) ->
(ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint)
{ {
@ -785,7 +832,8 @@ impl ProjectivePrimeFieldPoint {
(x2P, xPaddQ) (x2P, xPaddQ)
} }
/// Given `x(P)` and a scalar m in little-endian bytes, compute `x([m]P), x([m+1]P)`
/// Given `x(P)` and a scalar m in little-endian bytes, compute `x([m]P), x([m+1]P)`
/// using the Montgomery ladder. This is described in Algorithm 8 of Costello-Smith. /// using the Montgomery ladder. This is described in Algorithm 8 of Costello-Smith.
/// ///
/// The extra value `x([m+1]P)` is returned to allow y-coordinate recovery, otherwise, /// The extra value `x([m+1]P)` is returned to allow y-coordinate recovery, otherwise,
@ -794,6 +842,7 @@ impl ProjectivePrimeFieldPoint {
/// This function's execution time is dependent only on the byte-length of the input /// This function's execution time is dependent only on the byte-length of the input
/// scalar. All scalars of the same input length execute in uniform time. /// scalar. All scalars of the same input length execute in uniform time.
/// The scalar can be padded with zero bytes to ensure a uniform length. /// The scalar can be padded with zero bytes to ensure a uniform length.
#[allow(non_snake_case)]
fn scalar_mul_prime_field(&self, aPlus2Over4: &PrimeFieldElement, scalar: &[u8]) -> (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) fn scalar_mul_prime_field(&self, aPlus2Over4: &PrimeFieldElement, scalar: &[u8]) -> (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint)
{ {
let xP = *self; let xP = *self;
@ -831,6 +880,7 @@ impl ProjectivePrimeFieldPoint {
// sage: X3, Y3, Z3 = 3*P // sage: X3, Y3, Z3 = 3*P
// sage: m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 // sage: m = 96550223052359874398280314003345143371473380422728857598463622014420884224892
// //
#[allow(non_snake_case)]
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;

View file

@ -9,11 +9,12 @@
//! This module contains finite field arithmetic functionality for SIDH, //! This module contains finite field arithmetic functionality for SIDH,
//! which is not part of the public API. //! which is not part of the public API.
use crate::sidh::fp::*; use crate::sidhp751::fp::*;
use std::fmt::Debug; use std::fmt::Debug;
use std::cmp::{Eq, PartialEq}; use std::cmp::{Eq, PartialEq};
use std::mem::zeroed;
use std::ops::*; use std::ops::*;
use subtle::ConditionallySelectable; use subtle::ConditionallySelectable;
@ -28,6 +29,7 @@ use quickcheck::{Arbitrary, Gen};
//-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------//
/// Represents an element of the extension field `F_{p^2}`. /// Represents an element of the extension field `F_{p^2}`.
#[allow(non_snake_case)]
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct ExtensionFieldElement { pub struct ExtensionFieldElement {
/// This field element is in Montgomery form, so that the value `A` is /// This field element is in Montgomery form, so that the value `A` is
@ -177,11 +179,13 @@ impl Arbitrary for ExtensionFieldElement {
impl ExtensionFieldElement { impl ExtensionFieldElement {
/// Construct a zero `ExtensionFieldElement`. /// Construct a zero `ExtensionFieldElement`.
pub fn zero() -> ExtensionFieldElement { pub fn zero() -> ExtensionFieldElement {
ExtensionFieldElement{ unsafe { zeroed() }
A: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), //ExtensionFieldElement{
B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), // A: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
} // B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
//}
} }
/// Construct a one `ExtensionFieldElement`. /// Construct a one `ExtensionFieldElement`.
pub fn one() -> ExtensionFieldElement { pub fn one() -> ExtensionFieldElement {
ExtensionFieldElement{ ExtensionFieldElement{
@ -189,6 +193,7 @@ impl ExtensionFieldElement {
B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), B: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
} }
} }
/// Set output to `1/x`. /// Set output to `1/x`.
pub fn inv(&self) -> ExtensionFieldElement { pub fn inv(&self) -> ExtensionFieldElement {
let a = &self.A; let a = &self.A;
@ -227,6 +232,7 @@ impl ExtensionFieldElement {
B: _b B: _b
} }
} }
// Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3). // Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3).
// //
// All xi, yi must be distinct. // All xi, yi must be distinct.
@ -244,6 +250,7 @@ impl ExtensionFieldElement {
(_y1, _y2, _y3) (_y1, _y2, _y3)
} }
/// Set the output to `x^2`. /// Set the output to `x^2`.
pub fn square(&self) -> ExtensionFieldElement { pub fn square(&self) -> ExtensionFieldElement {
let a = &self.A; let a = &self.A;
@ -268,11 +275,13 @@ impl ExtensionFieldElement {
B: _b B: _b
} }
} }
/// Returns true if both sides are equal. Takes variable time. /// Returns true if both sides are equal. Takes variable time.
#[inline(always)] #[inline(always)]
pub fn vartime_eq(&self, _rhs: &ExtensionFieldElement) -> bool { pub fn vartime_eq(&self, _rhs: &ExtensionFieldElement) -> bool {
(&self.A == &_rhs.A) && (&self.B == &_rhs.B) (&self.A == &_rhs.A) && (&self.B == &_rhs.B)
} }
/// Convert the input to wire format. /// Convert the input to wire format.
pub fn to_bytes(&self) -> [u8; 188] { pub fn to_bytes(&self) -> [u8; 188] {
let mut bytes = [0u8; 188]; let mut bytes = [0u8; 188];
@ -280,6 +289,7 @@ impl ExtensionFieldElement {
bytes[94..188].clone_from_slice(&self.B.to_bytes()); bytes[94..188].clone_from_slice(&self.B.to_bytes());
bytes bytes
} }
/// Read 188 bytes into the given `ExtensionFieldElement`. /// Read 188 bytes into the given `ExtensionFieldElement`.
pub fn from_bytes(bytes: &[u8]) -> ExtensionFieldElement { pub fn from_bytes(bytes: &[u8]) -> ExtensionFieldElement {
assert!(bytes.len() >= 188, "Too short input to ExtensionFieldElement from_bytes, expected 188 bytes"); assert!(bytes.len() >= 188, "Too short input to ExtensionFieldElement from_bytes, expected 188 bytes");
@ -294,6 +304,7 @@ impl ExtensionFieldElement {
//-----------------------------------------------------------------------------// //-----------------------------------------------------------------------------//
/// Represents an element of the prime field `F_p`. /// Represents an element of the prime field `F_p`.
#[allow(non_snake_case)]
#[derive(Copy, Clone, PartialEq)] #[derive(Copy, Clone, PartialEq)]
pub struct PrimeFieldElement { pub struct PrimeFieldElement {
/// This field element is in Montgomery form, so that the value `A` is /// This field element is in Montgomery form, so that the value `A` is
@ -396,16 +407,19 @@ impl Arbitrary for PrimeFieldElement {
impl PrimeFieldElement { impl PrimeFieldElement {
/// Construct a zero `PrimeFieldElement`. /// Construct a zero `PrimeFieldElement`.
pub fn zero() -> PrimeFieldElement { pub fn zero() -> PrimeFieldElement {
PrimeFieldElement{ unsafe { zeroed() }
A: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]), //PrimeFieldElement{
} // A: Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]),
//}
} }
/// Construct a one `PrimeFieldElement`. /// Construct a one `PrimeFieldElement`.
pub fn one() -> PrimeFieldElement { pub fn one() -> PrimeFieldElement {
PrimeFieldElement{ PrimeFieldElement{
A: Fp751Element([0x249ad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83100000, 0x375c6c66, 0x5527b1e4, 0x3f4f24d0, 0x697797bf, 0xac5c4e2e, 0xc89db7b2, 0xd2076956, 0x4ca4b439, 0x7512c7e9, 0x10f7926c, 0x24bce5e2, 0x2d5b]), A: Fp751Element([0x249ad, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x83100000, 0x375c6c66, 0x5527b1e4, 0x3f4f24d0, 0x697797bf, 0xac5c4e2e, 0xc89db7b2, 0xd2076956, 0x4ca4b439, 0x7512c7e9, 0x10f7926c, 0x24bce5e2, 0x2d5b]),
} }
} }
/// Set the output to `x^2`. /// Set the output to `x^2`.
#[inline(always)] #[inline(always)]
pub fn square(&self) -> PrimeFieldElement { pub fn square(&self) -> PrimeFieldElement {
@ -416,12 +430,14 @@ impl PrimeFieldElement {
PrimeFieldElement{ A: _a } PrimeFieldElement{ A: _a }
} }
/// Raise self to `2^(2^k)`-th power, for `k >= 1`, by repeated squarings. /// Raise self to `2^(2^k)`-th power, for `k >= 1`, by repeated squarings.
fn pow2k(&self, k: u8) -> PrimeFieldElement { fn pow2k(&self, k: u8) -> PrimeFieldElement {
let mut result = self.square(); let mut result = self.square();
for _ in 1..k { result = result.square(); } for _ in 1..k { result = result.square(); }
result result
} }
/// Set output to `x^((p-3)/4)`. If `x` is square, this is `1/sqrt(x)`. /// Set output to `x^((p-3)/4)`. If `x` is square, this is `1/sqrt(x)`.
fn p34(&self) -> PrimeFieldElement { fn p34(&self) -> PrimeFieldElement {
// Sliding-window strategy computed with Sage, awk, sed, and tr. // Sliding-window strategy computed with Sage, awk, sed, and tr.
@ -455,6 +471,7 @@ impl PrimeFieldElement {
} }
result result
} }
/// Set output to `sqrt(x)`, if x is a square. If `x` is nonsquare output is undefined. /// Set output to `sqrt(x)`, if x is a square. If `x` is nonsquare output is undefined.
fn sqrt(&self) -> PrimeFieldElement { fn sqrt(&self) -> PrimeFieldElement {
let mut result = self.p34(); // result = (y^2)^((p-3)/4) = y^((p-3)/2) let mut result = self.p34(); // result = (y^2)^((p-3)/4) = y^((p-3)/2)
@ -462,6 +479,7 @@ impl PrimeFieldElement {
// Now result^2 = y^(p+1) = y^2 = x, so result = sqrt(x). // Now result^2 = y^(p+1) = y^2 = x, so result = sqrt(x).
result result
} }
/// Set output to `1/x`. /// Set output to `1/x`.
pub fn inv(&self) -> PrimeFieldElement { pub fn inv(&self) -> PrimeFieldElement {
let mut result = self.square(); // result = x^2 let mut result = self.square(); // result = x^2
@ -470,7 +488,9 @@ impl PrimeFieldElement {
result = &result * self; // result = x^(p-2) result = &result * self; // result = x^(p-2)
result result
} }
/// Returns true if both sides are equal. Takes variable time. /// Returns true if both sides are equal. Takes variable time.
#[inline(always)]
pub fn vartime_eq(&self, _rhs: &PrimeFieldElement) -> bool { pub fn vartime_eq(&self, _rhs: &PrimeFieldElement) -> bool {
&self.A == &_rhs.A &self.A == &_rhs.A
} }
@ -652,12 +672,12 @@ impl Fp751X2 {
#[inline(always)] #[inline(always)]
pub fn checklt238(scalar: &[u8; 48], result: &mut u32) { pub fn checklt238(scalar: &[u8; 48], result: &mut u32) {
crate::sidh::fp::checklt238(scalar, result); crate::sidhp751::fp::checklt238(scalar, result);
} }
#[inline(always)] #[inline(always)]
pub fn mulby3(scalar: &mut [u8; 48]) { pub fn mulby3(scalar: &mut [u8; 48]) {
crate::sidh::fp::mulby3(scalar); crate::sidhp751::fp::mulby3(scalar);
} }
#[cfg(test)] #[cfg(test)]

View file

@ -8,7 +8,7 @@
use crate::random::SecureRandom; use crate::random::SecureRandom;
use std::mem::size_of; use std::mem::{size_of, MaybeUninit, zeroed};
use std::fmt::Debug; use std::fmt::Debug;
use std::ops::Neg; use std::ops::Neg;
@ -40,28 +40,13 @@ const P751: [u32; FP751_NUM_WORDS] = [4294967295, 4294967295, 4294967295, 429496
const P751P1: [u32; FP751_NUM_WORDS] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4004511744, 1241020584, 3823933061, 335006838, 3667237658, 3605784694, 139368551, 1555191624, 2237838596, 2545605734, 236097695, 3577870108, 28645]; const P751P1: [u32; FP751_NUM_WORDS] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4004511744, 1241020584, 3823933061, 335006838, 3667237658, 3605784694, 139368551, 1555191624, 2237838596, 2545605734, 236097695, 3577870108, 28645];
const P751X2: [u32; FP751_NUM_WORDS] = [4294967294, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 3714056191, 2482041169, 3352898826, 670013677, 3039508020, 2916602093, 278737103, 3110383248, 180709896, 796244173, 472195391, 2860772920, 57291]; const P751X2: [u32; FP751_NUM_WORDS] = [4294967294, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 3714056191, 2482041169, 3352898826, 670013677, 3039508020, 2916602093, 278737103, 3110383248, 180709896, 796244173, 472195391, 2860772920, 57291];
// Return 1 if x != 0, and 0 otherwise. fn digit_x_digit(a: u32, b: u32, c: &mut [u32; 2]) {
#[inline(always)] #[allow(non_upper_case_globals)]
fn is_digit_nonzero_ct(x: &u32) -> u32 { const sizeof_u32: u32 = size_of::<u32>() as u32;
((x | ((0 as u32).wrapping_sub(*x))) >> (RADIX-1)) as u32 #[allow(non_upper_case_globals)]
} const mask_low: u32 = <u32>::MAX >> (sizeof_u32 * 4);
#[allow(non_upper_case_globals)]
// Return 1 if x = 0, and 0 otherwise. const mask_high: u32 = <u32>::MAX << (sizeof_u32 * 4);
#[inline(always)]
fn is_digit_zero_ct(x: &u32) -> u32 {
(1 ^ is_digit_nonzero_ct(x)) as u32
}
// Return 1 if x < y, and 0 otherwise.
#[inline(always)]
fn is_digit_lessthan_ct(x: &u32, y: &u32) -> u32 {
((x ^ ((x ^ y) | ((x.wrapping_sub(*y)) ^ y))) >> (RADIX-1)) as u32
}
fn digit_x_digit(a: &u32, b: &u32, c: &mut [u32]) {
let sizeof_u32 = size_of::<u32>() as u32;
let mask_low = <u32>::max_value() >> (sizeof_u32 * 4);
let mask_high = <u32>::max_value() << (sizeof_u32 * 4);
let al = a & mask_low; let al = a & mask_low;
let ah = a >> (sizeof_u32 * 4); let ah = a >> (sizeof_u32 * 4);
@ -72,78 +57,73 @@ fn digit_x_digit(a: &u32, b: &u32, c: &mut [u32]) {
let albh = al * bh; let albh = al * bh;
let ahbl = ah * bl; let ahbl = ah * bl;
let ahbh = ah * bh; let ahbh = ah * bh;
c[0] = albl & mask_low; let c0 = albl & mask_low;
let mut res1 = albl >> (sizeof_u32 * 4); let mut res1 = albl >> (sizeof_u32 * 4);
let mut res2 = ahbl & mask_low; let mut res2 = ahbl & mask_low;
let mut res3 = albh & mask_low; let mut res3 = albh & mask_low;
let mut temp = res1 + res2 + res3; let mut temp = res1 + res2 + res3;
let mut carry = temp >> (sizeof_u32 * 4); let mut carry = temp >> (sizeof_u32 * 4);
c[0] ^= temp << (sizeof_u32 * 4); c[0] = c0 ^ (temp << (sizeof_u32 * 4));
res1 = ahbl >> (sizeof_u32 * 4); res1 = ahbl >> (sizeof_u32 * 4);
res2 = albh >> (sizeof_u32 * 4); res2 = albh >> (sizeof_u32 * 4);
res3 = ahbh & mask_low; res3 = ahbh & mask_low;
temp = res1 + res2 + res3 + carry; temp = res1 + res2 + res3 + carry;
c[1] = temp & mask_low; let c1 = temp & mask_low;
carry = temp & mask_high; carry = temp & mask_high;
c[1] ^= (ahbh & mask_high) + carry; c[1] = c1 ^ ((ahbh & mask_high) + carry);
} }
#[inline(always)] #[inline(always)]
fn mul(multiplier: &u32, multiplicant: &u32, uv: &mut [u32]) { fn mul(multiplier: u32, multiplicant: u32, uv: &mut [u32; 2]) {
digit_x_digit(multiplier, multiplicant, uv); digit_x_digit(multiplier, multiplicant, uv);
} }
#[inline(always)] #[inline(always)]
fn addc(carry_in: &u32, addend1: &u32, addend2: &u32) -> (u32, u32) { fn addc(carry_in: u32, addend1: u32, addend2: u32) -> (u32, u32) {
let temp = addend1.wrapping_add(*carry_in); //let sum = (addend1 as u64) + (addend2 as u64) + (carry_in as u64);
//((sum > u32::MAX as u64) as u32, sum as u32)
let temp = addend1.wrapping_add(carry_in);
let sum = addend2.wrapping_add(temp); let sum = addend2.wrapping_add(temp);
let carry_out = (is_digit_lessthan_ct(&temp, carry_in)) | is_digit_lessthan_ct(&sum, &temp); ((temp < carry_in) as u32 | (sum < temp) as u32, sum)
(carry_out, sum)
} }
#[inline(always)] #[inline(always)]
fn subc(borrow_in: &u32, minuend: &u32, subtrahend: &u32) -> (u32, u32) { fn subc(borrow_in: u32, minuend: u32, subtrahend: u32) -> (u32, u32) {
let temp = minuend.wrapping_sub(*subtrahend); let temp = minuend.wrapping_sub(subtrahend);
let borrow = (is_digit_lessthan_ct(minuend, subtrahend)) | (borrow_in & is_digit_zero_ct(&temp)); let borrow = ((minuend < subtrahend) as u32) | (borrow_in & (temp == 0) as u32);
let difference = temp.wrapping_sub(*borrow_in); let difference = temp.wrapping_sub(borrow_in);
let borrow_out = borrow; (borrow, difference)
(borrow_out, difference)
} }
#[inline(always)] #[inline(always)]
pub fn fpadd751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { pub fn fpadd751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) {
let mut carry: u32 = 0; let mut carry: u32 = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
assign!{(carry, z.0[i]) = addc(&carry, &x.0[i], &y.0[i])}; assign!{(carry, z.0[i]) = addc(carry, x.0[i], y.0[i])};
} }
carry = 0; carry = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
assign!{(carry, z.0[i]) = subc(&carry, &z.0[i], &P751X2[i])}; assign!{(carry, z.0[i]) = subc(carry, z.0[i], P751X2[i])};
} }
let mask = (0 as u32).wrapping_sub(carry); let mask = (0 as u32).wrapping_sub(carry);
carry = 0; carry = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
assign!{(carry, z.0[i]) = addc(&carry, &z.0[i], &(P751X2[i] & mask))}; assign!{(carry, z.0[i]) = addc(carry, z.0[i], (P751X2[i] & mask))};
} }
} }
#[inline(always)] #[inline(always)]
pub fn fpsub751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { pub fn fpsub751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) {
let mut borrow: u32 = 0; let mut borrow: u32 = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
assign!{(borrow, z.0[i]) = subc(&borrow, &x.0[i], &y.0[i])}; assign!{(borrow, z.0[i]) = subc(borrow, x.0[i], y.0[i])};
} }
let mask = (0 as u32).wrapping_sub(borrow); let mask = (0 as u32).wrapping_sub(borrow);
borrow = 0; borrow = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
assign!{(borrow, z.0[i]) = addc(&borrow, &z.0[i], &(P751X2[i] & mask))}; assign!{(borrow, z.0[i]) = addc(borrow, z.0[i], (P751X2[i] & mask))};
} }
} }
@ -151,14 +131,15 @@ pub fn mul751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751X2) {
let mut t: u32 = 0; let mut t: u32 = 0;
let mut u: u32 = 0; let mut u: u32 = 0;
let mut v: u32 = 0; let mut v: u32 = 0;
#[allow(non_snake_case)]
let mut UV = [0u32; 2]; let mut UV = [0u32; 2];
let mut carry: u32 = 0; let mut carry: u32 = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
for j in 0..(i+1) { for j in 0..(i+1) {
mul(&x.0[j], &y.0[i-j], &mut UV[..]); mul(x.0[j], y.0[i - j], &mut UV);
assign!{(carry, v) = addc(&0, &UV[0], &v)}; assign! {(carry, v) = addc(0, UV[0], v)};
assign!{(carry, u) = addc(&carry, &UV[1], &u)}; assign! {(carry, u) = addc(carry, UV[1], u)};
t += carry; t += carry;
} }
z.0[i] = v; z.0[i] = v;
@ -169,9 +150,9 @@ pub fn mul751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751X2) {
for i in FP751_NUM_WORDS..(2*FP751_NUM_WORDS-1) { for i in FP751_NUM_WORDS..(2*FP751_NUM_WORDS-1) {
for j in (i-FP751_NUM_WORDS+1)..FP751_NUM_WORDS { for j in (i-FP751_NUM_WORDS+1)..FP751_NUM_WORDS {
mul(&x.0[j], &y.0[i-j], &mut UV[..]); mul(x.0[j], y.0[i-j], &mut UV);
assign!{(carry, v) = addc(&0, &UV[0], &v)}; assign!{(carry, v) = addc(0, UV[0], v)};
assign!{(carry, u) = addc(&carry, &UV[1], &u)}; assign!{(carry, u) = addc(carry, UV[1], u)};
t += carry; t += carry;
} }
z.0[i] = v; z.0[i] = v;
@ -186,6 +167,7 @@ pub fn rdc751(x: &Fp751X2, z: &mut Fp751Element) {
let mut t: u32 = 0; let mut t: u32 = 0;
let mut u: u32 = 0; let mut u: u32 = 0;
let mut v: u32 = 0; let mut v: u32 = 0;
#[allow(non_snake_case)]
let mut UV = [0u32; 2]; let mut UV = [0u32; 2];
let mut carry: u32 = 0; let mut carry: u32 = 0;
let mut count = P751_ZERO_WORDS; let mut count = P751_ZERO_WORDS;
@ -197,14 +179,14 @@ pub fn rdc751(x: &Fp751X2, z: &mut Fp751Element) {
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
for j in 0..i { for j in 0..i {
if j < (((i+1) as u32).wrapping_sub(P751_ZERO_WORDS as u32) as usize) { if j < (((i+1) as u32).wrapping_sub(P751_ZERO_WORDS as u32) as usize) {
mul(&z.0[j], &P751P1[i-j], &mut UV[..]); mul(z.0[j], P751P1[i-j], &mut UV);
assign!{(carry, v) = addc(&0, &UV[0], &v)}; assign!{(carry, v) = addc(0, UV[0], v)};
assign!{(carry, u) = addc(&carry, &UV[1], &u)}; assign!{(carry, u) = addc(carry, UV[1], u)};
t += carry; t += carry;
} }
} }
assign!{(carry, v) = addc(&0, &v, &x.0[i])}; assign!{(carry, v) = addc(0, v, x.0[i])};
assign!{(carry, u) = addc(&carry, &u, &0)}; assign!{(carry, u) = addc(carry, u, 0)};
t += carry; t += carry;
z.0[i] = v; z.0[i] = v;
@ -219,14 +201,14 @@ pub fn rdc751(x: &Fp751X2, z: &mut Fp751Element) {
} }
for j in (i-FP751_NUM_WORDS+1)..FP751_NUM_WORDS { for j in (i-FP751_NUM_WORDS+1)..FP751_NUM_WORDS {
if j < (FP751_NUM_WORDS-count) { if j < (FP751_NUM_WORDS-count) {
mul(&z.0[j], &P751P1[i-j], &mut UV[..]); mul(z.0[j], P751P1[i-j], &mut UV);
assign!{(carry, v) = addc(&0, &UV[0], &v)}; assign!{(carry, v) = addc(0, UV[0], v)};
assign!{(carry, u) = addc(&carry, &UV[1], &u)}; assign!{(carry, u) = addc(carry, UV[1], u)};
t += carry; t += carry;
} }
} }
assign!{(carry, v) = addc(&0, &v, &x.0[i])}; assign!{(carry, v) = addc(0, v, x.0[i])};
assign!{(carry, u) = addc(&carry, &u, &0)}; assign!{(carry, u) = addc(carry, u, 0)};
t += carry; t += carry;
z.0[i-FP751_NUM_WORDS] = v; z.0[i-FP751_NUM_WORDS] = v;
@ -234,55 +216,49 @@ pub fn rdc751(x: &Fp751X2, z: &mut Fp751Element) {
u = t; u = t;
t = 0; t = 0;
} }
assign!{(carry, v) = addc(&0, &v, &x.0[2*FP751_NUM_WORDS-1])}; assign!{(carry, v) = addc(0, v, x.0[2*FP751_NUM_WORDS-1])};
z.0[FP751_NUM_WORDS-1] = v; z.0[FP751_NUM_WORDS-1] = v;
} }
#[inline(always)] #[inline(always)]
pub fn srdc751(x: &mut Fp751Element) { pub fn srdc751(x: &mut Fp751Element) {
let mut borrow: u32 = 0; let mut borrow: u32 = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
assign!{(borrow, x.0[i]) = subc(&borrow, &x.0[i], &P751[i])}; assign!{(borrow, x.0[i]) = subc(borrow, x.0[i], P751[i])};
} }
let mask = (0 as u32).wrapping_sub(borrow); let mask = (0 as u32).wrapping_sub(borrow);
borrow = 0; borrow = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
assign!{(borrow, x.0[i]) = addc(&borrow, &x.0[i], &(P751[i] & mask))}; assign!{(borrow, x.0[i]) = addc(borrow, x.0[i], (P751[i] & mask))};
} }
} }
#[inline(always)] #[inline(always)]
pub fn mp_add751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { pub fn mp_add751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) {
let mut carry: u32 = 0; let mut carry: u32 = 0;
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
assign!{(carry, z.0[i]) = addc(&carry, &x.0[i], &y.0[i])}; assign!{(carry, z.0[i]) = addc(carry, x.0[i], y.0[i])};
} }
} }
#[inline(always)] #[inline(always)]
pub fn mp_add751x2(x: &Fp751X2, y: &Fp751X2, z: &mut Fp751X2) { pub fn mp_add751x2(x: &Fp751X2, y: &Fp751X2, z: &mut Fp751X2) {
let mut carry: u32 = 0; let mut carry: u32 = 0;
for i in 0..(FP751_NUM_WORDS*2) { for i in 0..(FP751_NUM_WORDS*2) {
assign!{(carry, z.0[i]) = addc(&carry, &x.0[i], &y.0[i])}; assign!{(carry, z.0[i]) = addc(carry, x.0[i], y.0[i])};
} }
} }
#[inline(always)] #[inline(always)]
pub fn mp_sub751x2(x: &Fp751X2, y: &Fp751X2, z: &mut Fp751X2) { pub fn mp_sub751x2(x: &Fp751X2, y: &Fp751X2, z: &mut Fp751X2) {
let mut borrow: u32 = 0; let mut borrow: u32 = 0;
for i in 0..(FP751_NUM_WORDS*2) { for i in 0..(FP751_NUM_WORDS*2) {
assign!{(borrow, z.0[i]) = subc(&borrow, &x.0[i], &y.0[i])}; assign!{(borrow, z.0[i]) = subc(borrow, x.0[i], y.0[i])};
} }
let mask = (0 as u32).wrapping_sub(borrow); let mask = (0 as u32).wrapping_sub(borrow);
borrow = 0; borrow = 0;
for i in FP751_NUM_WORDS..(FP751_NUM_WORDS*2) { for i in FP751_NUM_WORDS..(FP751_NUM_WORDS*2) {
assign!{(borrow, z.0[i]) = addc(&borrow, &z.0[i], &(P751[i-FP751_NUM_WORDS] & mask))}; assign!{(borrow, z.0[i]) = addc(borrow, z.0[i], (P751[i-FP751_NUM_WORDS] & mask))};
} }
} }
@ -303,7 +279,7 @@ pub fn checklt238(scalar: &[u8; 48], result: &mut u32) {
let mut borrow: u32 = 0; let mut borrow: u32 = 0;
for i in 0..12 { for i in 0..12 {
assign!{(borrow, ignored) = subc(&borrow, &three238[i], &scalar_u32[i])}; assign!{(borrow, ignored) = subc(borrow, three238[i], scalar_u32[i])};
} }
let mask = (0 as u32).wrapping_sub(borrow); let mask = (0 as u32).wrapping_sub(borrow);
*result = mask; *result = mask;
@ -324,10 +300,10 @@ pub fn mulby3(scalar: &mut [u8; 48]) {
let mut carry: u32 = 0; let mut carry: u32 = 0;
let temp = scalar_u32; let temp = scalar_u32;
for i in 0..12 { for i in 0..12 {
assign!{(carry, scalar_u32[i]) = addc(&carry, &scalar_u32[i], &temp[i])}; assign!{(carry, scalar_u32[i]) = addc(carry, scalar_u32[i], temp[i])};
} }
for i in 0..12 { for i in 0..12 {
assign!{(carry, scalar_u32[i]) = addc(&carry, &scalar_u32[i], &temp[i])}; assign!{(carry, scalar_u32[i]) = addc(carry, scalar_u32[i], temp[i])};
} }
for i in 0..48 { for i in 0..48 {
@ -346,11 +322,11 @@ pub struct Fp751ElementDist;
impl ConditionallySelectable for Fp751Element { impl ConditionallySelectable for Fp751Element {
#[inline(always)] #[inline(always)]
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
let mut bytes = [0_u32; FP751_NUM_WORDS]; let mut bytes = unsafe { MaybeUninit::<Fp751Element>::uninit().assume_init() };
for i in 0..FP751_NUM_WORDS { for i in 0..FP751_NUM_WORDS {
bytes[i] = u32::conditional_select(&a.0[i], &b.0[i], choice); bytes.0[i] = u32::conditional_select(&a.0[i], &b.0[i], choice);
} }
Fp751Element(bytes) bytes
} }
#[inline(always)] #[inline(always)]
@ -398,14 +374,17 @@ impl Arbitrary for Fp751Element {
impl Fp751Element { impl Fp751Element {
/// Construct a new zero `Fp751Element`. /// Construct a new zero `Fp751Element`.
#[inline(always)]
pub fn zero() -> Fp751Element { pub fn zero() -> Fp751Element {
Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) unsafe { zeroed() }
//Fp751Element([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0])
} }
/// Given an `Fp751Element` in Montgomery form, convert to little-endian bytes. /// Given an `Fp751Element` in Montgomery form, convert to little-endian bytes.
pub fn to_bytes(&self) -> [u8; 94] { pub fn to_bytes(&self) -> [u8; 94] {
let mut bytes = [0u8; 94]; let mut bytes = [0u8; 94];
let mut a = Fp751Element::zero(); let mut a = Fp751Element::zero();
#[allow(non_snake_case)]
let mut aR = Fp751X2::zero(); let mut aR = Fp751X2::zero();
//let one = Fp751Element([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]); //let one = Fp751Element([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
@ -440,6 +419,7 @@ impl Fp751Element {
a.0[j as usize] |= (bytes[i as usize] as u32) << (8 * k); a.0[j as usize] |= (bytes[i as usize] as u32) << (8 * k);
} }
#[allow(non_snake_case)]
let aRR = &a * &MONTGOMERY_RSQ; // = a*R*R let aRR = &a * &MONTGOMERY_RSQ; // = a*R*R
let output = aRR.reduce(); // = a*R mod p let output = aRR.reduce(); // = a*R mod p
output output
@ -457,9 +437,11 @@ impl Debug for Fp751X2 {
impl Fp751X2 { impl Fp751X2 {
// Construct a zero `Fp751X2`. // Construct a zero `Fp751X2`.
#[inline(always)]
pub fn zero() -> Fp751X2 { pub fn zero() -> Fp751X2 {
Fp751X2([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, unsafe { zeroed() }
0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) //Fp751X2([0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
// 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0])
} }
} }

View file

@ -9,16 +9,18 @@
//! This module contains internal isogeny representation and operations //! This module contains internal isogeny representation and operations
//! for SIDH, which is not part of the public API. //! for SIDH, which is not part of the public API.
use crate::sidh::field::ExtensionFieldElement; use crate::sidhp751::field::ExtensionFieldElement;
use crate::sidh::curve::{ProjectiveCurveParameters, ProjectivePoint}; use crate::sidhp751::curve::{ProjectiveCurveParameters, ProjectivePoint};
/// Represents a 3-isogeny phi, holding the data necessary to evaluate phi. /// Represents a 3-isogeny phi, holding the data necessary to evaluate phi.
#[allow(non_snake_case)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct ThreeIsogeny { pub struct ThreeIsogeny {
pub X: ExtensionFieldElement, pub X: ExtensionFieldElement,
pub Z: ExtensionFieldElement, pub Z: ExtensionFieldElement,
} }
#[allow(non_snake_case)]
impl ThreeIsogeny { impl ThreeIsogeny {
/// Given a three-torsion point `x3 = x(P_3)` on the curve `E_(A:C)`, construct the /// Given a three-torsion point `x3 = x(P_3)` on the curve `E_(A:C)`, construct the
/// three-isogeny `phi : E_(A:C) -> E_(A:C)/<P_3> = E_(A':C')`. /// three-isogeny `phi : E_(A:C) -> E_(A:C)/<P_3> = E_(A':C')`.
@ -45,6 +47,7 @@ impl ThreeIsogeny {
(codomain, isogeny) (codomain, isogeny)
} }
/// Given a 3-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate /// Given a 3-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate
/// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`. /// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`.
/// ///
@ -72,6 +75,7 @@ impl ThreeIsogeny {
/// Represents a 4-isogeny phi, holding the data necessary to evaluate phi. /// Represents a 4-isogeny phi, holding the data necessary to evaluate phi.
// //
// See compute_four_isogeny for more details. // See compute_four_isogeny for more details.
#[allow(non_snake_case)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct FourIsogeny { pub struct FourIsogeny {
pub Xsq_plus_Zsq : ExtensionFieldElement, pub Xsq_plus_Zsq : ExtensionFieldElement,
@ -81,6 +85,7 @@ pub struct FourIsogeny {
pub Zpow4 : ExtensionFieldElement, pub Zpow4 : ExtensionFieldElement,
} }
#[allow(non_snake_case)]
impl FourIsogeny { impl FourIsogeny {
/// Given a four-torsion point `x4 = x(P_4)` on the curve `E_(A:C)`, compute the /// Given a four-torsion point `x4 = x(P_4)` on the curve `E_(A:C)`, compute the
/// coefficients of the codomain `E_(A':C')` of the four-isogeny `phi : E_(A:C) -> /// coefficients of the codomain `E_(A':C')` of the four-isogeny `phi : E_(A:C) ->
@ -118,6 +123,7 @@ impl FourIsogeny {
(codomain, isogeny) (codomain, isogeny)
} }
/// Given a 4-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate /// Given a 4-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate
/// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`. /// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`.
// //
@ -160,12 +166,14 @@ impl FourIsogeny {
/// Represents a 4-isogeny phi. /// Represents a 4-isogeny phi.
// //
// See compute_four_isogeny for details. // See compute_four_isogeny for details.
#[allow(non_snake_case)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct FirstFourIsogeny { pub struct FirstFourIsogeny {
pub A: ExtensionFieldElement, pub A: ExtensionFieldElement,
pub C: ExtensionFieldElement, pub C: ExtensionFieldElement,
} }
#[allow(non_snake_case)]
impl FirstFourIsogeny { impl FirstFourIsogeny {
/// Compute the "first" four-isogeny from the given curve. /// Compute the "first" four-isogeny from the given curve.
// //
@ -183,6 +191,7 @@ impl FirstFourIsogeny {
(codomain, isogeny) (codomain, isogeny)
} }
/// Given a 4-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate /// Given a 4-isogeny phi and a point `xP = x(P)`, compute `x(Q)`, the x-coordinate
/// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`. /// of the image `Q = phi(P)` of `P` under `phi : E_(A:C) -> E_(A':C')`.
// //
@ -210,10 +219,11 @@ impl FirstFourIsogeny {
} }
} }
#[allow(non_snake_case)]
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use super::*; use super::*;
use crate::sidh::fp::Fp751Element; use crate::sidhp751::fp::Fp751Element;
// Test the first four-isogeny from the base curve E_0(F_{p^2}). // Test the first four-isogeny from the base curve E_0(F_{p^2}).
#[test] #[test]

View file

@ -23,4 +23,6 @@ pub(crate) mod fp;
pub mod constants; pub mod constants;
#[allow(unused_assignments)] #[allow(unused_assignments)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub mod sidh; mod sidh;
pub use sidh::*;

View file

@ -6,11 +6,11 @@
// - Erkan Tairi <erkan.tairi@gmail.com> // - Erkan Tairi <erkan.tairi@gmail.com>
// //
use crate::sidh::field::ExtensionFieldElement; use crate::sidhp751::field::ExtensionFieldElement;
use crate::sidh::curve::{ProjectiveCurveParameters, ProjectivePoint}; use crate::sidhp751::curve::{ProjectiveCurveParameters, ProjectivePoint};
use crate::sidh::isogeny::*; use crate::sidhp751::isogeny::*;
use crate::sidh::constants::*; use crate::sidhp751::constants::*;
use crate::sidh::fp::*; use crate::sidhp751::fp::*;
#[allow(unused_imports)] #[allow(unused_imports)]
use crate::random::SecureRandom; use crate::random::SecureRandom;
@ -34,7 +34,6 @@ pub const SHARED_SECRET_SIZE: usize = 188;
const MAX_INT_POINTS_ALICE: usize = 8; const MAX_INT_POINTS_ALICE: usize = 8;
const MAX_INT_POINTS_BOB: usize = 10; const MAX_INT_POINTS_BOB: usize = 10;
const MAX_ALICE: usize = 185; const MAX_ALICE: usize = 185;
/// Alice's isogeny strategy. /// Alice's isogeny strategy.
pub const ALICE_ISOGENY_STRATEGY: [u8; MAX_ALICE] = [0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5, pub const ALICE_ISOGENY_STRATEGY: [u8; MAX_ALICE] = [0, 1, 1, 2, 2, 2, 3, 4, 4, 4, 4, 5, 5,
@ -66,6 +65,7 @@ pub const BOB_ISOGENY_STRATEGY: [u8; MAX_BOB] = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4
97, 97]; 97, 97];
/// Alice's public key. /// Alice's public key.
#[allow(non_snake_case)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct SIDHPublicKeyAlice { pub struct SIDHPublicKeyAlice {
pub affine_xP : ExtensionFieldElement, pub affine_xP : ExtensionFieldElement,
@ -75,6 +75,7 @@ pub struct SIDHPublicKeyAlice {
impl SIDHPublicKeyAlice { impl SIDHPublicKeyAlice {
/// Read a public key from a byte slice. The input must be at least 564 bytes long. /// Read a public key from a byte slice. The input must be at least 564 bytes long.
#[allow(non_snake_case)]
pub fn from_bytes(bytes: &[u8]) -> SIDHPublicKeyAlice { pub fn from_bytes(bytes: &[u8]) -> SIDHPublicKeyAlice {
assert!(bytes.len() >= 564, "Too short input to SIDH public key from_bytes, expected 564 bytes"); assert!(bytes.len() >= 564, "Too short input to SIDH public key from_bytes, expected 564 bytes");
let affine_xP = ExtensionFieldElement::from_bytes(&bytes[0..188]); let affine_xP = ExtensionFieldElement::from_bytes(&bytes[0..188]);
@ -93,6 +94,7 @@ impl SIDHPublicKeyAlice {
} }
/// Bob's public key. /// Bob's public key.
#[allow(non_snake_case)]
#[derive(Copy, Clone)] #[derive(Copy, Clone)]
pub struct SIDHPublicKeyBob { pub struct SIDHPublicKeyBob {
pub affine_xP : ExtensionFieldElement, pub affine_xP : ExtensionFieldElement,
@ -102,6 +104,7 @@ pub struct SIDHPublicKeyBob {
impl SIDHPublicKeyBob { impl SIDHPublicKeyBob {
/// Read a public key from a byte slice. The input must be at least 564 bytes long. /// Read a public key from a byte slice. The input must be at least 564 bytes long.
#[allow(non_snake_case)]
pub fn from_bytes(bytes: &[u8]) -> SIDHPublicKeyBob { pub fn from_bytes(bytes: &[u8]) -> SIDHPublicKeyBob {
assert!(bytes.len() >= 564, "Too short input to SIDH public key from_bytes, expected 564 bytes"); assert!(bytes.len() >= 564, "Too short input to SIDH public key from_bytes, expected 564 bytes");
let affine_xP = ExtensionFieldElement::from_bytes(&bytes[0..188]); let affine_xP = ExtensionFieldElement::from_bytes(&bytes[0..188]);
@ -142,6 +145,7 @@ impl Arbitrary for SIDHSecretKeyAlice {
impl SIDHSecretKeyAlice { impl SIDHSecretKeyAlice {
/// Compute the corresponding public key for the given secret key. /// Compute the corresponding public key for the given secret key.
#[allow(non_snake_case)]
pub fn public_key(&self) -> SIDHPublicKeyAlice { pub fn public_key(&self) -> SIDHPublicKeyAlice {
let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // = ( x_P : 1) = x(P_B) let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // = ( x_P : 1) = x(P_B)
let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); //
@ -199,7 +203,9 @@ impl SIDHSecretKeyAlice {
SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP } SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP }
} }
/// Compute (Alice's view of) a shared secret using Alice's secret key and Bob's public key. /// Compute (Alice's view of) a shared secret using Alice's secret key and Bob's public key.
#[allow(non_snake_case)]
pub fn shared_secret(&self, bob_public: &SIDHPublicKeyBob) -> [u8; SHARED_SECRET_SIZE] { pub fn shared_secret(&self, bob_public: &SIDHPublicKeyBob) -> [u8; SHARED_SECRET_SIZE] {
let current_curve = ProjectiveCurveParameters::recover_curve_parameters(&bob_public.affine_xP, &bob_public.affine_xQ, &bob_public.affine_xQmP); let current_curve = ProjectiveCurveParameters::recover_curve_parameters(&bob_public.affine_xP, &bob_public.affine_xQ, &bob_public.affine_xQmP);
let xP = ProjectivePoint::from_affine(&bob_public.affine_xP); let xP = ProjectivePoint::from_affine(&bob_public.affine_xP);
@ -264,6 +270,7 @@ impl Arbitrary for SIDHSecretKeyBob {
impl SIDHSecretKeyBob { impl SIDHSecretKeyBob {
/// Compute the public key corresponding to the secret key. /// Compute the public key corresponding to the secret key.
#[allow(non_snake_case)]
pub fn public_key(&self) -> SIDHPublicKeyBob { pub fn public_key(&self) -> SIDHPublicKeyBob {
let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // = ( x_P : 1) = x(P_A) let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // = ( x_P : 1) = x(P_A)
let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); //
@ -315,7 +322,9 @@ impl SIDHSecretKeyBob {
SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP } SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP }
} }
/// Compute (Bob's view of) a shared secret using Bob's secret key and Alice's public key. /// Compute (Bob's view of) a shared secret using Bob's secret key and Alice's public key.
#[allow(non_snake_case)]
pub fn shared_secret(&self, alice_public: &SIDHPublicKeyAlice) -> [u8; SHARED_SECRET_SIZE] { pub fn shared_secret(&self, alice_public: &SIDHPublicKeyAlice) -> [u8; SHARED_SECRET_SIZE] {
let mut current_curve = ProjectiveCurveParameters::recover_curve_parameters(&alice_public.affine_xP, &alice_public.affine_xQ, &alice_public.affine_xQmP); let mut current_curve = ProjectiveCurveParameters::recover_curve_parameters(&alice_public.affine_xP, &alice_public.affine_xQ, &alice_public.affine_xQmP);
let xP = ProjectivePoint::from_affine(&alice_public.affine_xP); let xP = ProjectivePoint::from_affine(&alice_public.affine_xP);
@ -416,6 +425,7 @@ mod test {
// Perform Alice's (2-isogeny) key generation, using the slow but simple multiplication-based strategy. // Perform Alice's (2-isogeny) key generation, using the slow but simple multiplication-based strategy.
// //
// This function just exists to ensure that the fast isogeny-tree strategy works correctly. // This function just exists to ensure that the fast isogeny-tree strategy works correctly.
#[allow(non_snake_case)]
pub fn alice_keygen_slow(secret_key: &SIDHSecretKeyAlice) -> SIDHPublicKeyAlice { pub fn alice_keygen_slow(secret_key: &SIDHSecretKeyAlice) -> SIDHPublicKeyAlice {
let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // = ( x_P : 1) = x(P_B) let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // = ( x_P : 1) = x(P_B)
let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); //
@ -452,9 +462,11 @@ mod test {
SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP } SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP }
} }
// Perform Bob's (3-isogeny) key generation, using the slow but simple multiplication-based strategy. // Perform Bob's (3-isogeny) key generation, using the slow but simple multiplication-based strategy.
// //
// This function just exists to ensure that the fast isogeny-tree strategy works correctly. // This function just exists to ensure that the fast isogeny-tree strategy works correctly.
#[allow(non_snake_case)]
pub fn bob_keygen_slow(secret_key: &SIDHSecretKeyBob) -> SIDHPublicKeyBob { pub fn bob_keygen_slow(secret_key: &SIDHSecretKeyBob) -> SIDHPublicKeyBob {
let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // = ( x_P : 1) = x(P_A) let mut xP = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // = ( x_P : 1) = x(P_A)
let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // let mut xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); //
@ -484,9 +496,11 @@ mod test {
SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP } SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP }
} }
// Perform Alice's key agreement, using the slow but simple multiplication-based strategy. // Perform Alice's key agreement, using the slow but simple multiplication-based strategy.
// //
// This function just exists to ensure that the fast isogeny-tree strategy works correctly. // This function just exists to ensure that the fast isogeny-tree strategy works correctly.
#[allow(non_snake_case)]
pub fn alice_shared_secret_slow(bob_public: &SIDHPublicKeyBob, alice_secret: &SIDHSecretKeyAlice) -> [u8; SHARED_SECRET_SIZE] { pub fn alice_shared_secret_slow(bob_public: &SIDHPublicKeyBob, alice_secret: &SIDHSecretKeyAlice) -> [u8; SHARED_SECRET_SIZE] {
let current_curve = ProjectiveCurveParameters::recover_curve_parameters(&bob_public.affine_xP, &bob_public.affine_xQ, &bob_public.affine_xQmP); let current_curve = ProjectiveCurveParameters::recover_curve_parameters(&bob_public.affine_xP, &bob_public.affine_xQ, &bob_public.affine_xQmP);
let xP = ProjectivePoint::from_affine(&bob_public.affine_xP); let xP = ProjectivePoint::from_affine(&bob_public.affine_xP);
@ -513,9 +527,11 @@ mod test {
let shared_secret = j_inv.to_bytes(); let shared_secret = j_inv.to_bytes();
shared_secret shared_secret
} }
// Perform Bob's key agreement, using the slow but simple multiplication-based strategy. // Perform Bob's key agreement, using the slow but simple multiplication-based strategy.
// //
// This function just exists to ensure that the fast isogeny-tree strategy works correctly. // This function just exists to ensure that the fast isogeny-tree strategy works correctly.
#[allow(non_snake_case)]
pub fn bob_shared_secret_slow(alice_public: &SIDHPublicKeyAlice, bob_secret: &SIDHSecretKeyBob) -> [u8; SHARED_SECRET_SIZE] { pub fn bob_shared_secret_slow(alice_public: &SIDHPublicKeyAlice, bob_secret: &SIDHSecretKeyBob) -> [u8; SHARED_SECRET_SIZE] {
let mut current_curve = ProjectiveCurveParameters::recover_curve_parameters(&alice_public.affine_xP, &alice_public.affine_xQ, &alice_public.affine_xQmP); let mut current_curve = ProjectiveCurveParameters::recover_curve_parameters(&alice_public.affine_xP, &alice_public.affine_xQ, &alice_public.affine_xQmP);
let xP = ProjectivePoint::from_affine(&alice_public.affine_xP); let xP = ProjectivePoint::from_affine(&alice_public.affine_xP);
@ -593,6 +609,7 @@ mod test {
QuickCheck::new().max_tests(8).quickcheck(shared_secrets_match as fn(SIDHSecretKeyAlice, SIDHSecretKeyBob) -> bool); QuickCheck::new().max_tests(8).quickcheck(shared_secrets_match as fn(SIDHSecretKeyAlice, SIDHSecretKeyBob) -> bool);
} }
#[allow(non_snake_case)]
#[test] #[test]
fn alice_keygen_fast_vs_slow() { fn alice_keygen_fast_vs_slow() {
// m_A = 2*randint(0,2^371) // m_A = 2*randint(0,2^371)
@ -610,6 +627,7 @@ mod test {
"\nExpected affine_xQmP = {:?}\nfound {:?}", fast_pubkey.affine_xQmP, slow_pubkey.affine_xQmP); "\nExpected affine_xQmP = {:?}\nfound {:?}", fast_pubkey.affine_xQmP, slow_pubkey.affine_xQmP);
} }
#[allow(non_snake_case)]
#[test] #[test]
fn bob_keygen_fast_vs_slow() { fn bob_keygen_fast_vs_slow() {
// m_B = 3*randint(0,3^238) // m_B = 3*randint(0,3^238)
@ -627,6 +645,7 @@ mod test {
"\nExpected affine_xQmP = {:?}\nfound {:?}", fast_pubkey.affine_xQmP, slow_pubkey.affine_xQmP); "\nExpected affine_xQmP = {:?}\nfound {:?}", fast_pubkey.affine_xQmP, slow_pubkey.affine_xQmP);
} }
#[allow(non_snake_case)]
#[test] #[test]
fn shared_secret() { fn shared_secret() {
// m_A = 2*randint(0,2^371) // m_A = 2*randint(0,2^371)
@ -653,6 +672,7 @@ mod test {
"\nShared secret mismatch: Alice (slow) has {:?}\nBob (fast) has {:?}", &alice_shared_secret_slow[..], &bob_shared_secret_fast[..]); "\nShared secret mismatch: Alice (slow) has {:?}\nBob (fast) has {:?}", &alice_shared_secret_slow[..], &bob_shared_secret_fast[..]);
} }
#[allow(non_snake_case)]
#[test] #[test]
fn secret_point() { fn secret_point() {
// m_A = 2*randint(0,2^371) // m_A = 2*randint(0,2^371)

View file

@ -0,0 +1,88 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2021 ZeroTier, Inc.
* https://www.zerotier.com/
*/
use std::sync::atomic::AtomicU32;
use zerotier_core_crypto::c25519::C25519KeyPair;
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
use zerotier_core_crypto::p521::P521KeyPair;
use zerotier_core_crypto::random::SecureRandom;
use zerotier_core_crypto::secret::Secret;
use zerotier_core_crypto::sidhp751::{SIDHPublicKeyAlice, SIDHPublicKeyBob, SIDHSecretKeyAlice, SIDHSecretKeyBob};
use crate::vl1::Address;
use crate::vl1::protocol::EPHEMERAL_SECRET_SIDH_TTL;
use crate::vl1::symmetricsecret::SymmetricSecret;
const EPHEMERAL_CIPHER_C25519: u8 = 1;
const EPHEMERAL_CIPHER_P521: u8 = 2;
const EPHEMERAL_CIPHER_SIDH_P751: u8 = 3;
#[derive(Copy, Clone)]
enum SIDHSecretKey {
Alice(SIDHPublicKeyAlice, SIDHSecretKeyAlice),
Bob(SIDHPublicKeyBob, SIDHSecretKeyBob)
}
impl SIDHSecretKey {
/// Generate a SIDH key pair.
///
/// SIDH is weird. A key exchange must involve one participant taking a role
/// canonically called Alice and the other wearing the Bob hat, because math.
///
/// If our local address is less than the remote address, we take the Alice role.
/// Otherwise if it's greater or equal we take the Bob role.
///
/// Everything works as long as the two sides take opposite roles. There is no
/// security implication in one side always taking one role.
pub fn generate(local_address: Address, remote_address: Address) -> SIDHSecretKey {
let mut rng = SecureRandom::get();
if local_address < remote_address {
let (p, s) = zerotier_core_crypto::sidh::sidh::generate_alice_keypair(&mut rng);
SIDHSecretKey::Alice(p, s)
} else {
let (p, s) = zerotier_core_crypto::sidh::sidh::generate_bob_keypair(&mut rng);
SIDHSecretKey::Bob(p, s)
}
}
}
/// An ephemeral secret key negotiated to implement forward secrecy.
pub struct EphemeralSecret {
timestamp_ticks: i64,
c25519: C25519KeyPair,
p521: P521KeyPair,
sidhp751: SIDHSecretKey,
}
impl EphemeralSecret {
/// Create a new ephemeral secret key.
///
/// This contains key pairs for the asymmetric key agreement algorithms used and a
/// timestamp used to enforce TTL.
pub fn new(time_ticks: i64, local_address: Address, remote_address: Address) -> Self {
EphemeralSecret {
timestamp_ticks: time_ticks,
c25519: C25519KeyPair::generate(true),
p521: P521KeyPair::generate(true).expect("NIST P-521 key pair generation failed"),
sidhp751: SIDHSecretKey::generate(local_address, remote_address),
}
}
/// Create a public version of this ephemeral secret to share with our counterparty.
pub fn public(&self) -> Vec<u8> {
todo!()
}
}
pub struct EphemeralSymmetricSecret {
secret: SymmetricSecret,
agreement_timestamp_ticks: i64,
local_secret_timestamp_ticks: i64,
encrypt_uses: AtomicU32,
}

View file

@ -23,6 +23,7 @@ pub(crate) mod mac;
pub(crate) mod fragmentedpacket; pub(crate) mod fragmentedpacket;
pub(crate) mod whoisqueue; pub(crate) mod whoisqueue;
pub(crate) mod ephemeral; pub(crate) mod ephemeral;
pub(crate) mod symmetricsecret;
pub use address::Address; pub use address::Address;
pub use mac::MAC; pub use mac::MAC;

View file

@ -49,6 +49,18 @@ pub const KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1: u8 = b'1';
/// KBKDF usage label for acknowledgement of a shared secret. /// KBKDF usage label for acknowledgement of a shared secret.
pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_ACK: u8 = b'A'; pub const KBKDF_KEY_USAGE_LABEL_EPHEMERAL_ACK: u8 = b'A';
/// Try to re-key ephemeral keys after this time.
pub const EPHEMERAL_SECRET_REKEY_AFTER_TIME: i64 = 1000 * 60 * 60; // 1 hour
/// Maximum number of times to use an ephemeral secret before trying to replace it.
pub const EPHEMERAL_SECRET_REKEY_AFTER_USES: u32 = 536870912; // 1/4 the NIST security limit
/// Ephemeral secret reject after time.
pub const EPHEMERAL_SECRET_REJECT_AFTER_TIME: i64 = EPHEMERAL_SECRET_REKEY_AFTER_TIME * 2;
/// Ephemeral secret reject after uses.
pub const EPHEMERAL_SECRET_REJECT_AFTER_USES: u32 = 2147483648; // NIST security limit
/// Length of an address in bytes. /// Length of an address in bytes.
pub const ADDRESS_SIZE: usize = 5; pub const ADDRESS_SIZE: usize = 5;

View file

@ -0,0 +1,53 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*
* (c)2021 ZeroTier, Inc.
* https://www.zerotier.com/
*/
use parking_lot::Mutex;
use zerotier_core_crypto::aes_gmac_siv::{AesCtr, AesGmacSiv};
use zerotier_core_crypto::hash::SHA384_HASH_SIZE;
use zerotier_core_crypto::kbkdf::zt_kbkdf_hmac_sha384;
use zerotier_core_crypto::secret::Secret;
use crate::util::pool::{Pool, PoolFactory};
use crate::vl1::protocol::{KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, KBKDF_KEY_USAGE_LABEL_HELLO_DICTIONARY_ENCRYPT, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC};
pub struct AesGmacSivPoolFactory(Secret<SHA384_HASH_SIZE>, Secret<SHA384_HASH_SIZE>);
impl PoolFactory<AesGmacSiv> for AesGmacSivPoolFactory {
#[inline(always)]
fn create(&self) -> AesGmacSiv { AesGmacSiv::new(&self.0.0[0..32], &self.1.0[0..32]) }
#[inline(always)]
fn reset(&self, obj: &mut AesGmacSiv) { obj.reset(); }
}
/// A symmetric secret key negotiated between peers.
///
/// This contains the key and several sub-keys and ciphers keyed with sub-keys.
pub struct SymmetricSecret {
pub key: Secret<SHA384_HASH_SIZE>,
pub packet_hmac_key: Secret<SHA384_HASH_SIZE>,
pub hello_dictionary_keyed_cipher: Mutex<AesCtr>,
pub aes_gmac_siv: Pool<AesGmacSiv, AesGmacSivPoolFactory>,
}
impl SymmetricSecret {
pub fn new(base_key: Secret<SHA384_HASH_SIZE>) -> SymmetricSecret {
let usage_packet_hmac = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_PACKET_HMAC, 0, 0);
let usage_hello_dictionary_key = zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_HELLO_DICTIONARY_ENCRYPT, 0, 0);
let aes_factory = AesGmacSivPoolFactory(
zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K0, 0, 0),
zt_kbkdf_hmac_sha384(&base_key.0, KBKDF_KEY_USAGE_LABEL_AES_GMAC_SIV_K1, 0, 0));
SymmetricSecret {
key: base_key,
packet_hmac_key: usage_packet_hmac,
hello_dictionary_keyed_cipher: Mutex::new(AesCtr::new(&usage_hello_dictionary_key.0[0..32])),
aes_gmac_siv: Pool::new(2, aes_factory),
}
}
}