diff --git a/zerotier-core-crypto/src/lib.rs b/zerotier-core-crypto/src/lib.rs index cf2a245a1..1c46f79bd 100644 --- a/zerotier-core-crypto/src/lib.rs +++ b/zerotier-core-crypto/src/lib.rs @@ -17,7 +17,7 @@ pub mod random; pub mod secret; pub mod hex; pub mod varint; -pub mod sidh; +pub mod sidhp751; pub use aes_gmac_siv; pub use rand_core; diff --git a/zerotier-core-crypto/src/sidh/LICENSE b/zerotier-core-crypto/src/sidhp751/LICENSE similarity index 100% rename from zerotier-core-crypto/src/sidh/LICENSE rename to zerotier-core-crypto/src/sidhp751/LICENSE diff --git a/zerotier-core-crypto/src/sidh/ORIGINAL-README.md b/zerotier-core-crypto/src/sidhp751/ORIGINAL-README.md similarity index 100% rename from zerotier-core-crypto/src/sidh/ORIGINAL-README.md rename to zerotier-core-crypto/src/sidhp751/ORIGINAL-README.md diff --git a/zerotier-core-crypto/src/sidh/constants.rs b/zerotier-core-crypto/src/sidhp751/constants.rs similarity index 96% rename from zerotier-core-crypto/src/sidh/constants.rs rename to zerotier-core-crypto/src/sidhp751/constants.rs index 10f5aef92..7507838d0 100644 --- a/zerotier-core-crypto/src/sidh/constants.rs +++ b/zerotier-core-crypto/src/sidhp751/constants.rs @@ -6,8 +6,8 @@ // - Erkan Tairi // -use crate::sidh::field::PrimeFieldElement; -use crate::sidh::fp::Fp751Element; +use crate::sidhp751::field::PrimeFieldElement; +use crate::sidhp751::fp::Fp751Element; /// 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]) }; diff --git a/zerotier-core-crypto/src/sidh/curve.rs b/zerotier-core-crypto/src/sidhp751/curve.rs similarity index 97% rename from zerotier-core-crypto/src/sidh/curve.rs rename to zerotier-core-crypto/src/sidhp751/curve.rs index 75eeb74e2..f4c75e4ac 100644 --- a/zerotier-core-crypto/src/sidh/curve.rs +++ b/zerotier-core-crypto/src/sidhp751/curve.rs @@ -9,9 +9,9 @@ //! This module contains internal curve representation and operations //! for SIDH, which is not part of the public API. -use crate::sidh::fp::Fp751Element; -use crate::sidh::field::{PrimeFieldElement, ExtensionFieldElement}; -use crate::sidh::constants::*; +use crate::sidhp751::fp::Fp751Element; +use crate::sidhp751::field::{PrimeFieldElement, ExtensionFieldElement}; +use crate::sidhp751::constants::*; use std::fmt::Debug; use std::ops::Neg; @@ -19,6 +19,7 @@ use subtle::{ConditionallySelectable, Choice}; #[cfg(test)] use quickcheck::{Gen, Arbitrary}; +use std::mem::zeroed; // Macro to assign tuples, as Rust does not allow tuples as lvalue. macro_rules! assign{ @@ -41,17 +42,20 @@ const CONST_256: ExtensionFieldElement = ExtensionFieldElement { /// A point on the projective line `P^1(F_{p^2})`. /// /// This is used to work projectively with the curve coefficients. +#[allow(non_snake_case)] #[derive(Copy, Clone, PartialEq)] pub struct ProjectiveCurveParameters { pub A: ExtensionFieldElement, pub C: ExtensionFieldElement, } +#[allow(non_snake_case)] struct CachedCurveParameters { Aplus2C: ExtensionFieldElement, C4: ExtensionFieldElement, } +#[allow(non_snake_case)] struct CachedTripleCurveParameters { Aminus2C: ExtensionFieldElement, C2: ExtensionFieldElement, @@ -79,8 +83,10 @@ impl ProjectiveCurveParameters { C: ExtensionFieldElement::one() } } + /// 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 { let mut t0 = ExtensionFieldElement::one(); // = 1 @@ -101,6 +107,7 @@ impl ProjectiveCurveParameters { ProjectiveCurveParameters{ A: a, C: c } } + /// Compute the j-invariant of the given curve. pub fn j_invariant(&self) -> ExtensionFieldElement { let a = &self.A; @@ -122,7 +129,9 @@ impl ProjectiveCurveParameters { v0 } + /// Compute cached parameters `A + 2C, 4C`. + #[allow(non_snake_case)] fn cached_params(&self) -> CachedCurveParameters { let mut Aplus2C = &self.C + &self.C; // = 2*C let C4 = &Aplus2C + &Aplus2C; // = 4*C @@ -130,7 +139,9 @@ impl ProjectiveCurveParameters { CachedCurveParameters{ Aplus2C, C4 } } + /// Compute cached parameters `A - 2C, 2C`. + #[allow(non_snake_case)] fn cached_triple_params(&self) -> CachedTripleCurveParameters { let C2 = &self.C + &self.C; // = 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 /// curve is specified by a ProjectiveCurveParameters struct. +#[allow(non_snake_case)] #[derive(Copy, Clone, PartialEq)] pub struct ProjectivePoint { pub X: ExtensionFieldElement, @@ -181,9 +193,11 @@ impl Arbitrary for ProjectivePoint { impl ProjectivePoint { /// Creates a new zero `ProejctivePoint`. 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 { let _X = ExtensionFieldElement{ A: x.A, B: ExtensionFieldElement::zero().B }; ProjectivePoint{ @@ -203,13 +217,16 @@ impl ProjectivePoint { let affine_x = &self.Z.inv() * &self.X; affine_x } + /// Returns true if both sides are equal. Takes variable time. pub fn vartime_eq(&self, _rhs: &ProjectivePoint) -> bool { let t0 = &self.X * &_rhs.Z; let t1 = &self.Z * &_rhs.X; t0.vartime_eq(&t1) } + /// 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 { let xP = *self; // Algorithm 1 of Costello-Smith. @@ -225,7 +242,9 @@ impl ProjectivePoint { 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)`. + #[allow(non_snake_case)] fn double(&self, curve: &CachedCurveParameters) -> ProjectivePoint { let xP = *self; // 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 ) ProjectivePoint{ X: x, Z: z } } + /// 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)` // Cost: 8M+4S+8A in `Fp2` + #[allow(non_snake_case)] fn dbl_add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, params: &CachedCurveParameters) -> (ProjectivePoint, ProjectivePoint) { @@ -281,17 +302,21 @@ impl ProjectivePoint { (x2P, xPaddQ) } + /// 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 { let cached_params = curve.cached_params(); let mut xQ = *self; for _ in 0..k { xQ = xQ.double(&cached_params); } xQ } + // Uses the efficient Montgomery tripling formulas from 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)`. /// Returns `xQ` to allow chaining. + #[allow(non_snake_case)] fn triple(&self, curve: &CachedTripleCurveParameters) -> ProjectivePoint { let xP = *self; let (x1, z1) = (&xP.X, &xP.Z); @@ -319,19 +344,23 @@ impl ProjectivePoint { ProjectivePoint{ X: x, Z: z } } + /// 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 { let cached_params = curve.cached_triple_params(); let mut xQ = *self; for _ in 0..k { xQ = xQ.triple(&cached_params); } xQ } + /// 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. /// /// 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. /// 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 { let xP = *self; let cached_params = curve.cached_params(); @@ -357,12 +386,14 @@ impl ProjectivePoint { let xQ = x0; xQ } + /// 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 /// `E_0(F_p)`, use the Okeya-Sakurai coordinate recovery strategy to recover `Q = /// (X_Q : Y_Q : Z_Q)`. /// /// 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, xQ: &ProjectivePrimeFieldPoint, xR: &ProjectivePrimeFieldPoint) -> (PrimeFieldElement, PrimeFieldElement, PrimeFieldElement) @@ -387,6 +418,7 @@ impl ProjectivePoint { (X_Q, Y_Q, Z_Q) } + /// 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. /// @@ -427,7 +459,8 @@ impl ProjectivePoint { // // 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 { let cached_params = curve.cached_params(); @@ -457,8 +490,10 @@ impl ProjectivePoint { let xR = x2; xR } + /// 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`. + #[allow(non_snake_case)] pub fn right_to_left_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint { @@ -482,8 +517,10 @@ impl ProjectivePoint { let xR = R1; xR } + /// Given the affine x-coordinate `affine_xP` of `P`, compute the x-coordinate /// `x(\tau(P)-P) of \tau(P)-P`. + #[allow(non_snake_case)] pub fn distort_and_difference(affine_xP: &PrimeFieldElement) -> ProjectivePoint { let mut t0 = affine_xP.square(); // = x_P^2 let t1 = &PrimeFieldElement::one() + &t0; // = x_P^2 + 1 @@ -496,6 +533,7 @@ impl ProjectivePoint { let xR = ProjectivePoint{ X: x, Z: z }; xR } + /// 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)`, /// 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 // and computed more efficiently, but efficiency isn't the biggest concern // 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 { let mut xQ = ProjectivePrimeFieldPoint::from_affine(affine_xP); 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 /// the base curve `E_0(F_p)`, defined by `E_0 : y^2 = x^3 + x`. +#[allow(non_snake_case)] #[derive(Copy, Clone, PartialEq)] struct ProjectivePrimeFieldPoint { X: PrimeFieldElement, @@ -691,7 +731,8 @@ impl Arbitrary for ProjectivePrimeFieldPoint { impl ProjectivePrimeFieldPoint { /// Creates a new zero `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 { @@ -702,17 +743,19 @@ impl ProjectivePrimeFieldPoint { } pub fn to_affine(&self) -> PrimeFieldElement { - let affine_x = &self.Z.inv() * &self.X; - affine_x + &self.Z.inv() * &self.X } + /// Returns true if both sides are equal. Takes variable time. pub fn vartime_eq(&self, _rhs: &ProjectivePrimeFieldPoint) -> bool { let t0 = &self.X * &_rhs.Z; let t1 = &self.Z * &_rhs.X; t0.vartime_eq(&t1) } + /// 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 { let xP = *self; @@ -729,10 +772,12 @@ impl ProjectivePrimeFieldPoint { ProjectivePrimeFieldPoint{ X: x, Z: z } } + /// 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 // ever use a fixed curve (in our case, the base curve E_0). + #[allow(non_snake_case)] fn double(&self, aPlus2Over4: &PrimeFieldElement) -> ProjectivePrimeFieldPoint { let xP = *self; // 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 ) ProjectivePrimeFieldPoint{ X: x, Z: z } } + /// Calculates the x-coordinate of `2P` and `P+Q` from the x-coordinate of `P, Q` and `P-Q`. // Assumptions: // aPlus2Over2 = (A+2)/4. // z(P-Q) = 1, the Z-coordinate of P-Q is equal to 1. // Cost: 6M+4S+8A in Fp + #[allow(non_snake_case)] fn dbl_add(&self, xQ: &ProjectivePrimeFieldPoint, xPmQ: &ProjectivePrimeFieldPoint, aPlus2Over4: &PrimeFieldElement) -> (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) { @@ -785,7 +832,8 @@ impl ProjectivePrimeFieldPoint { (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. /// /// 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 /// 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. + #[allow(non_snake_case)] fn scalar_mul_prime_field(&self, aPlus2Over4: &PrimeFieldElement, scalar: &[u8]) -> (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) { let xP = *self; @@ -831,6 +880,7 @@ impl ProjectivePrimeFieldPoint { // sage: X3, Y3, Z3 = 3*P // sage: m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 // +#[allow(non_snake_case)] #[cfg(test)] mod test { use super::*; diff --git a/zerotier-core-crypto/src/sidh/field.rs b/zerotier-core-crypto/src/sidhp751/field.rs similarity index 97% rename from zerotier-core-crypto/src/sidh/field.rs rename to zerotier-core-crypto/src/sidhp751/field.rs index 7a39cf624..cc1121681 100644 --- a/zerotier-core-crypto/src/sidh/field.rs +++ b/zerotier-core-crypto/src/sidhp751/field.rs @@ -9,11 +9,12 @@ //! This module contains finite field arithmetic functionality for SIDH, //! which is not part of the public API. -use crate::sidh::fp::*; +use crate::sidhp751::fp::*; use std::fmt::Debug; use std::cmp::{Eq, PartialEq}; +use std::mem::zeroed; use std::ops::*; use subtle::ConditionallySelectable; @@ -28,6 +29,7 @@ use quickcheck::{Arbitrary, Gen}; //-----------------------------------------------------------------------------// /// Represents an element of the extension field `F_{p^2}`. +#[allow(non_snake_case)] #[derive(Copy, Clone, PartialEq)] pub struct ExtensionFieldElement { /// This field element is in Montgomery form, so that the value `A` is @@ -177,11 +179,13 @@ impl Arbitrary for ExtensionFieldElement { impl ExtensionFieldElement { /// Construct a zero `ExtensionFieldElement`. pub fn zero() -> ExtensionFieldElement { - ExtensionFieldElement{ - 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]), - } + unsafe { zeroed() } + //ExtensionFieldElement{ + // 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`. pub fn one() -> 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]), } } + /// Set output to `1/x`. pub fn inv(&self) -> ExtensionFieldElement { let a = &self.A; @@ -227,6 +232,7 @@ impl ExtensionFieldElement { B: _b } } + // Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3). // // All xi, yi must be distinct. @@ -244,6 +250,7 @@ impl ExtensionFieldElement { (_y1, _y2, _y3) } + /// Set the output to `x^2`. pub fn square(&self) -> ExtensionFieldElement { let a = &self.A; @@ -268,11 +275,13 @@ impl ExtensionFieldElement { B: _b } } + /// Returns true if both sides are equal. Takes variable time. #[inline(always)] pub fn vartime_eq(&self, _rhs: &ExtensionFieldElement) -> bool { (&self.A == &_rhs.A) && (&self.B == &_rhs.B) } + /// Convert the input to wire format. pub fn to_bytes(&self) -> [u8; 188] { let mut bytes = [0u8; 188]; @@ -280,6 +289,7 @@ impl ExtensionFieldElement { bytes[94..188].clone_from_slice(&self.B.to_bytes()); bytes } + /// Read 188 bytes into the given `ExtensionFieldElement`. pub fn from_bytes(bytes: &[u8]) -> ExtensionFieldElement { 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`. +#[allow(non_snake_case)] #[derive(Copy, Clone, PartialEq)] pub struct PrimeFieldElement { /// This field element is in Montgomery form, so that the value `A` is @@ -396,16 +407,19 @@ impl Arbitrary for PrimeFieldElement { impl PrimeFieldElement { /// Construct a zero `PrimeFieldElement`. pub fn zero() -> PrimeFieldElement { - 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]), - } + unsafe { zeroed() } + //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`. pub fn one() -> 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]), } } + /// Set the output to `x^2`. #[inline(always)] pub fn square(&self) -> PrimeFieldElement { @@ -416,12 +430,14 @@ impl PrimeFieldElement { PrimeFieldElement{ A: _a } } + /// Raise self to `2^(2^k)`-th power, for `k >= 1`, by repeated squarings. fn pow2k(&self, k: u8) -> PrimeFieldElement { let mut result = self.square(); for _ in 1..k { result = result.square(); } result } + /// Set output to `x^((p-3)/4)`. If `x` is square, this is `1/sqrt(x)`. fn p34(&self) -> PrimeFieldElement { // Sliding-window strategy computed with Sage, awk, sed, and tr. @@ -455,6 +471,7 @@ impl PrimeFieldElement { } result } + /// Set output to `sqrt(x)`, if x is a square. If `x` is nonsquare output is undefined. fn sqrt(&self) -> PrimeFieldElement { 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). result } + /// Set output to `1/x`. pub fn inv(&self) -> PrimeFieldElement { let mut result = self.square(); // result = x^2 @@ -470,7 +488,9 @@ impl PrimeFieldElement { result = &result * self; // result = x^(p-2) result } + /// Returns true if both sides are equal. Takes variable time. + #[inline(always)] pub fn vartime_eq(&self, _rhs: &PrimeFieldElement) -> bool { &self.A == &_rhs.A } @@ -652,12 +672,12 @@ impl Fp751X2 { #[inline(always)] pub fn checklt238(scalar: &[u8; 48], result: &mut u32) { - crate::sidh::fp::checklt238(scalar, result); + crate::sidhp751::fp::checklt238(scalar, result); } #[inline(always)] pub fn mulby3(scalar: &mut [u8; 48]) { - crate::sidh::fp::mulby3(scalar); + crate::sidhp751::fp::mulby3(scalar); } #[cfg(test)] diff --git a/zerotier-core-crypto/src/sidh/fp.rs b/zerotier-core-crypto/src/sidhp751/fp.rs similarity index 72% rename from zerotier-core-crypto/src/sidh/fp.rs rename to zerotier-core-crypto/src/sidhp751/fp.rs index f123eb58f..a502403ac 100644 --- a/zerotier-core-crypto/src/sidh/fp.rs +++ b/zerotier-core-crypto/src/sidhp751/fp.rs @@ -8,7 +8,7 @@ use crate::random::SecureRandom; -use std::mem::size_of; +use std::mem::{size_of, MaybeUninit, zeroed}; use std::fmt::Debug; 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 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. -#[inline(always)] -fn is_digit_nonzero_ct(x: &u32) -> u32 { - ((x | ((0 as u32).wrapping_sub(*x))) >> (RADIX-1)) as u32 -} - -// Return 1 if x = 0, and 0 otherwise. -#[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::() as u32; - let mask_low = ::max_value() >> (sizeof_u32 * 4); - let mask_high = ::max_value() << (sizeof_u32 * 4); +fn digit_x_digit(a: u32, b: u32, c: &mut [u32; 2]) { + #[allow(non_upper_case_globals)] + const sizeof_u32: u32 = size_of::() as u32; + #[allow(non_upper_case_globals)] + const mask_low: u32 = ::MAX >> (sizeof_u32 * 4); + #[allow(non_upper_case_globals)] + const mask_high: u32 = ::MAX << (sizeof_u32 * 4); let al = a & mask_low; 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 ahbl = ah * bl; let ahbh = ah * bh; - c[0] = albl & mask_low; + let c0 = albl & mask_low; let mut res1 = albl >> (sizeof_u32 * 4); let mut res2 = ahbl & mask_low; let mut res3 = albh & mask_low; let mut temp = res1 + res2 + res3; 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); res2 = albh >> (sizeof_u32 * 4); res3 = ahbh & mask_low; temp = res1 + res2 + res3 + carry; - c[1] = temp & mask_low; + let c1 = temp & mask_low; carry = temp & mask_high; - c[1] ^= (ahbh & mask_high) + carry; + c[1] = c1 ^ ((ahbh & mask_high) + carry); } #[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); } #[inline(always)] -fn addc(carry_in: &u32, addend1: &u32, addend2: &u32) -> (u32, u32) { - let temp = addend1.wrapping_add(*carry_in); +fn addc(carry_in: u32, addend1: u32, addend2: u32) -> (u32, u32) { + //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 carry_out = (is_digit_lessthan_ct(&temp, carry_in)) | is_digit_lessthan_ct(&sum, &temp); - (carry_out, sum) + ((temp < carry_in) as u32 | (sum < temp) as u32, sum) } #[inline(always)] -fn subc(borrow_in: &u32, minuend: &u32, subtrahend: &u32) -> (u32, u32) { - let temp = minuend.wrapping_sub(*subtrahend); - let borrow = (is_digit_lessthan_ct(minuend, subtrahend)) | (borrow_in & is_digit_zero_ct(&temp)); - let difference = temp.wrapping_sub(*borrow_in); - let borrow_out = borrow; - (borrow_out, difference) +fn subc(borrow_in: u32, minuend: u32, subtrahend: u32) -> (u32, u32) { + let temp = minuend.wrapping_sub(subtrahend); + let borrow = ((minuend < subtrahend) as u32) | (borrow_in & (temp == 0) as u32); + let difference = temp.wrapping_sub(borrow_in); + (borrow, difference) } #[inline(always)] pub fn fpadd751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { let mut carry: u32 = 0; - 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; 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); - carry = 0; 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)] pub fn fpsub751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { let mut borrow: u32 = 0; - 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); - borrow = 0; 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 u: u32 = 0; let mut v: u32 = 0; + #[allow(non_snake_case)] let mut UV = [0u32; 2]; let mut carry: u32 = 0; for i in 0..FP751_NUM_WORDS { for j in 0..(i+1) { - mul(&x.0[j], &y.0[i-j], &mut UV[..]); - assign!{(carry, v) = addc(&0, &UV[0], &v)}; - assign!{(carry, u) = addc(&carry, &UV[1], &u)}; + mul(x.0[j], y.0[i - j], &mut UV); + assign! {(carry, v) = addc(0, UV[0], v)}; + assign! {(carry, u) = addc(carry, UV[1], u)}; t += carry; } 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 j in (i-FP751_NUM_WORDS+1)..FP751_NUM_WORDS { - mul(&x.0[j], &y.0[i-j], &mut UV[..]); - assign!{(carry, v) = addc(&0, &UV[0], &v)}; - assign!{(carry, u) = addc(&carry, &UV[1], &u)}; + mul(x.0[j], y.0[i-j], &mut UV); + assign!{(carry, v) = addc(0, UV[0], v)}; + assign!{(carry, u) = addc(carry, UV[1], u)}; t += carry; } z.0[i] = v; @@ -186,6 +167,7 @@ pub fn rdc751(x: &Fp751X2, z: &mut Fp751Element) { let mut t: u32 = 0; let mut u: u32 = 0; let mut v: u32 = 0; + #[allow(non_snake_case)] let mut UV = [0u32; 2]; let mut carry: u32 = 0; 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 j in 0..i { if j < (((i+1) as u32).wrapping_sub(P751_ZERO_WORDS as u32) as usize) { - mul(&z.0[j], &P751P1[i-j], &mut UV[..]); - assign!{(carry, v) = addc(&0, &UV[0], &v)}; - assign!{(carry, u) = addc(&carry, &UV[1], &u)}; + mul(z.0[j], P751P1[i-j], &mut UV); + assign!{(carry, v) = addc(0, UV[0], v)}; + assign!{(carry, u) = addc(carry, UV[1], u)}; t += carry; } } - assign!{(carry, v) = addc(&0, &v, &x.0[i])}; - assign!{(carry, u) = addc(&carry, &u, &0)}; + assign!{(carry, v) = addc(0, v, x.0[i])}; + assign!{(carry, u) = addc(carry, u, 0)}; t += carry; 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 { if j < (FP751_NUM_WORDS-count) { - mul(&z.0[j], &P751P1[i-j], &mut UV[..]); - assign!{(carry, v) = addc(&0, &UV[0], &v)}; - assign!{(carry, u) = addc(&carry, &UV[1], &u)}; + mul(z.0[j], P751P1[i-j], &mut UV); + assign!{(carry, v) = addc(0, UV[0], v)}; + assign!{(carry, u) = addc(carry, UV[1], u)}; t += carry; } } - assign!{(carry, v) = addc(&0, &v, &x.0[i])}; - assign!{(carry, u) = addc(&carry, &u, &0)}; + assign!{(carry, v) = addc(0, v, x.0[i])}; + assign!{(carry, u) = addc(carry, u, 0)}; t += carry; z.0[i-FP751_NUM_WORDS] = v; @@ -234,55 +216,49 @@ pub fn rdc751(x: &Fp751X2, z: &mut Fp751Element) { u = t; 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; } #[inline(always)] pub fn srdc751(x: &mut Fp751Element) { let mut borrow: u32 = 0; - 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); - borrow = 0; 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)] pub fn mp_add751(x: &Fp751Element, y: &Fp751Element, z: &mut Fp751Element) { let mut carry: u32 = 0; - 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)] pub fn mp_add751x2(x: &Fp751X2, y: &Fp751X2, z: &mut Fp751X2) { let mut carry: u32 = 0; - 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)] pub fn mp_sub751x2(x: &Fp751X2, y: &Fp751X2, z: &mut Fp751X2) { let mut borrow: u32 = 0; - 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); - borrow = 0; 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; 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); *result = mask; @@ -324,10 +300,10 @@ pub fn mulby3(scalar: &mut [u8; 48]) { let mut carry: u32 = 0; let temp = scalar_u32; 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 { - 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 { @@ -346,11 +322,11 @@ pub struct Fp751ElementDist; impl ConditionallySelectable for Fp751Element { #[inline(always)] fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { - let mut bytes = [0_u32; FP751_NUM_WORDS]; + let mut bytes = unsafe { MaybeUninit::::uninit().assume_init() }; 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)] @@ -398,14 +374,17 @@ impl Arbitrary for Fp751Element { impl Fp751Element { /// Construct a new zero `Fp751Element`. + #[inline(always)] 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. pub fn to_bytes(&self) -> [u8; 94] { let mut bytes = [0u8; 94]; let mut a = Fp751Element::zero(); + #[allow(non_snake_case)] 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]); @@ -440,6 +419,7 @@ impl Fp751Element { 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 output = aRR.reduce(); // = a*R mod p output @@ -457,9 +437,11 @@ impl Debug for Fp751X2 { impl Fp751X2 { // Construct a zero `Fp751X2`. + #[inline(always)] 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, - 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() } + //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]) } } diff --git a/zerotier-core-crypto/src/sidh/isogeny.rs b/zerotier-core-crypto/src/sidhp751/isogeny.rs similarity index 98% rename from zerotier-core-crypto/src/sidh/isogeny.rs rename to zerotier-core-crypto/src/sidhp751/isogeny.rs index e80db36ce..28b1b1f0b 100644 --- a/zerotier-core-crypto/src/sidh/isogeny.rs +++ b/zerotier-core-crypto/src/sidhp751/isogeny.rs @@ -9,16 +9,18 @@ //! This module contains internal isogeny representation and operations //! for SIDH, which is not part of the public API. -use crate::sidh::field::ExtensionFieldElement; -use crate::sidh::curve::{ProjectiveCurveParameters, ProjectivePoint}; +use crate::sidhp751::field::ExtensionFieldElement; +use crate::sidhp751::curve::{ProjectiveCurveParameters, ProjectivePoint}; /// Represents a 3-isogeny phi, holding the data necessary to evaluate phi. +#[allow(non_snake_case)] #[derive(Copy, Clone)] pub struct ThreeIsogeny { pub X: ExtensionFieldElement, pub Z: ExtensionFieldElement, } +#[allow(non_snake_case)] impl ThreeIsogeny { /// 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)/ = E_(A':C')`. @@ -45,6 +47,7 @@ impl ThreeIsogeny { (codomain, isogeny) } + /// 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')`. /// @@ -72,6 +75,7 @@ impl ThreeIsogeny { /// Represents a 4-isogeny phi, holding the data necessary to evaluate phi. // // See compute_four_isogeny for more details. +#[allow(non_snake_case)] #[derive(Copy, Clone)] pub struct FourIsogeny { pub Xsq_plus_Zsq : ExtensionFieldElement, @@ -81,6 +85,7 @@ pub struct FourIsogeny { pub Zpow4 : ExtensionFieldElement, } +#[allow(non_snake_case)] impl FourIsogeny { /// 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) -> @@ -118,6 +123,7 @@ impl FourIsogeny { (codomain, isogeny) } + /// 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')`. // @@ -160,12 +166,14 @@ impl FourIsogeny { /// Represents a 4-isogeny phi. // // See compute_four_isogeny for details. +#[allow(non_snake_case)] #[derive(Copy, Clone)] pub struct FirstFourIsogeny { pub A: ExtensionFieldElement, pub C: ExtensionFieldElement, } +#[allow(non_snake_case)] impl FirstFourIsogeny { /// Compute the "first" four-isogeny from the given curve. // @@ -183,6 +191,7 @@ impl FirstFourIsogeny { (codomain, isogeny) } + /// 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')`. // @@ -210,10 +219,11 @@ impl FirstFourIsogeny { } } +#[allow(non_snake_case)] #[cfg(test)] mod test { 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] diff --git a/zerotier-core-crypto/src/sidh/mod.rs b/zerotier-core-crypto/src/sidhp751/mod.rs similarity index 94% rename from zerotier-core-crypto/src/sidh/mod.rs rename to zerotier-core-crypto/src/sidhp751/mod.rs index 0f170cfe2..780dfd1d0 100644 --- a/zerotier-core-crypto/src/sidh/mod.rs +++ b/zerotier-core-crypto/src/sidhp751/mod.rs @@ -23,4 +23,6 @@ pub(crate) mod fp; pub mod constants; #[allow(unused_assignments)] #[allow(non_snake_case)] -pub mod sidh; +mod sidh; + +pub use sidh::*; diff --git a/zerotier-core-crypto/src/sidh/sidh.rs b/zerotier-core-crypto/src/sidhp751/sidh.rs similarity index 98% rename from zerotier-core-crypto/src/sidh/sidh.rs rename to zerotier-core-crypto/src/sidhp751/sidh.rs index b03781864..3010040ae 100644 --- a/zerotier-core-crypto/src/sidh/sidh.rs +++ b/zerotier-core-crypto/src/sidhp751/sidh.rs @@ -6,11 +6,11 @@ // - Erkan Tairi // -use crate::sidh::field::ExtensionFieldElement; -use crate::sidh::curve::{ProjectiveCurveParameters, ProjectivePoint}; -use crate::sidh::isogeny::*; -use crate::sidh::constants::*; -use crate::sidh::fp::*; +use crate::sidhp751::field::ExtensionFieldElement; +use crate::sidhp751::curve::{ProjectiveCurveParameters, ProjectivePoint}; +use crate::sidhp751::isogeny::*; +use crate::sidhp751::constants::*; +use crate::sidhp751::fp::*; #[allow(unused_imports)] 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_BOB: usize = 10; - const MAX_ALICE: usize = 185; /// 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, @@ -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]; /// Alice's public key. +#[allow(non_snake_case)] #[derive(Copy, Clone)] pub struct SIDHPublicKeyAlice { pub affine_xP : ExtensionFieldElement, @@ -75,6 +75,7 @@ pub struct SIDHPublicKeyAlice { impl SIDHPublicKeyAlice { /// 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 { 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]); @@ -93,6 +94,7 @@ impl SIDHPublicKeyAlice { } /// Bob's public key. +#[allow(non_snake_case)] #[derive(Copy, Clone)] pub struct SIDHPublicKeyBob { pub affine_xP : ExtensionFieldElement, @@ -102,6 +104,7 @@ pub struct SIDHPublicKeyBob { impl SIDHPublicKeyBob { /// 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 { 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]); @@ -142,6 +145,7 @@ impl Arbitrary for SIDHSecretKeyAlice { impl SIDHSecretKeyAlice { /// Compute the corresponding public key for the given secret key. + #[allow(non_snake_case)] 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 xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PB); // @@ -199,7 +203,9 @@ impl SIDHSecretKeyAlice { 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. + #[allow(non_snake_case)] 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 xP = ProjectivePoint::from_affine(&bob_public.affine_xP); @@ -264,6 +270,7 @@ impl Arbitrary for SIDHSecretKeyBob { impl SIDHSecretKeyBob { /// Compute the public key corresponding to the secret key. + #[allow(non_snake_case)] 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 xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // @@ -315,7 +322,9 @@ impl SIDHSecretKeyBob { 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. + #[allow(non_snake_case)] 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 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. // // 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 { 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); // @@ -452,9 +462,11 @@ mod test { SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP } } + // 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. + #[allow(non_snake_case)] 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 xQ = ProjectivePoint::from_affine_prime_field(&AFFINE_X_PA); // @@ -484,9 +496,11 @@ mod test { SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP } } + // 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. + #[allow(non_snake_case)] 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 xP = ProjectivePoint::from_affine(&bob_public.affine_xP); @@ -513,9 +527,11 @@ mod test { let shared_secret = j_inv.to_bytes(); shared_secret } + // 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. + #[allow(non_snake_case)] 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 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); } + #[allow(non_snake_case)] #[test] fn alice_keygen_fast_vs_slow() { // 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); } + #[allow(non_snake_case)] #[test] fn bob_keygen_fast_vs_slow() { // 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); } + #[allow(non_snake_case)] #[test] fn shared_secret() { // 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[..]); } + #[allow(non_snake_case)] #[test] fn secret_point() { // m_A = 2*randint(0,2^371) diff --git a/zerotier-network-hypervisor/src/vl1/ephemeral.rs b/zerotier-network-hypervisor/src/vl1/ephemeral.rs index e69de29bb..485045fce 100644 --- a/zerotier-network-hypervisor/src/vl1/ephemeral.rs +++ b/zerotier-network-hypervisor/src/vl1/ephemeral.rs @@ -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 { + todo!() + } +} + +pub struct EphemeralSymmetricSecret { + secret: SymmetricSecret, + agreement_timestamp_ticks: i64, + local_secret_timestamp_ticks: i64, + encrypt_uses: AtomicU32, +} diff --git a/zerotier-network-hypervisor/src/vl1/mod.rs b/zerotier-network-hypervisor/src/vl1/mod.rs index 1dda609aa..c45d51733 100644 --- a/zerotier-network-hypervisor/src/vl1/mod.rs +++ b/zerotier-network-hypervisor/src/vl1/mod.rs @@ -23,6 +23,7 @@ pub(crate) mod mac; pub(crate) mod fragmentedpacket; pub(crate) mod whoisqueue; pub(crate) mod ephemeral; +pub(crate) mod symmetricsecret; pub use address::Address; pub use mac::MAC; diff --git a/zerotier-network-hypervisor/src/vl1/protocol.rs b/zerotier-network-hypervisor/src/vl1/protocol.rs index 8eda057a6..46aeddf6d 100644 --- a/zerotier-network-hypervisor/src/vl1/protocol.rs +++ b/zerotier-network-hypervisor/src/vl1/protocol.rs @@ -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. 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. pub const ADDRESS_SIZE: usize = 5; diff --git a/zerotier-network-hypervisor/src/vl1/symmetricsecret.rs b/zerotier-network-hypervisor/src/vl1/symmetricsecret.rs new file mode 100644 index 000000000..3018c745a --- /dev/null +++ b/zerotier-network-hypervisor/src/vl1/symmetricsecret.rs @@ -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, Secret); + +impl PoolFactory 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, + pub packet_hmac_key: Secret, + pub hello_dictionary_keyed_cipher: Mutex, + pub aes_gmac_siv: Pool, +} + +impl SymmetricSecret { + pub fn new(base_key: Secret) -> 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), + } + } +}