diff --git a/zerotier-core-crypto/Cargo.toml b/zerotier-core-crypto/Cargo.toml index b8ffb602c..19032a1ed 100644 --- a/zerotier-core-crypto/Cargo.toml +++ b/zerotier-core-crypto/Cargo.toml @@ -5,11 +5,14 @@ edition = "2018" license = "MPL-2.0" authors = ["ZeroTier, Inc. ", "Adam Ierymenko "] -[patch.crates-io] - [dependencies] rand_core = "0.5.0" aes-gmac-siv = { path = "../aes-gmac-siv" } gcrypt = "0.7.0" x25519-dalek = { version = "1.2.0", features = ["u64_backend"] } ed25519-dalek = { version = "1.0.1", features = ["u64_backend"] } +heapless = "0.7.7" +subtle = "^2" + +[dev-dependencies] +quickcheck = "^1" diff --git a/zerotier-core-crypto/src/balloon.rs b/zerotier-core-crypto/src/balloon.rs index ed9497f4a..50acf9945 100644 --- a/zerotier-core-crypto/src/balloon.rs +++ b/zerotier-core-crypto/src/balloon.rs @@ -46,7 +46,7 @@ pub fn hash /* Expand (use AES as PRNG in this version as it's much faster on most hardware) */ let mut expand_aes = gcrypt::cipher::Cipher::new(gcrypt::cipher::Algorithm::Aes, gcrypt::cipher::Mode::Ecb).unwrap(); - expand_aes.set_key(&buf[0..32]); + let _ = expand_aes.set_key(&buf[0..32]); let mut s: usize = 64; while s < SPACE_COST { let ss = s + 16; diff --git a/zerotier-core-crypto/src/lib.rs b/zerotier-core-crypto/src/lib.rs index b22671e9a..cf2a245a1 100644 --- a/zerotier-core-crypto/src/lib.rs +++ b/zerotier-core-crypto/src/lib.rs @@ -17,6 +17,7 @@ pub mod random; pub mod secret; pub mod hex; pub mod varint; +pub mod sidh; pub use aes_gmac_siv; pub use rand_core; diff --git a/zerotier-core-crypto/src/sidh/constants.rs b/zerotier-core-crypto/src/sidh/constants.rs new file mode 100644 index 000000000..10f5aef92 --- /dev/null +++ b/zerotier-core-crypto/src/sidh/constants.rs @@ -0,0 +1,25 @@ +// This file is part of sidh-rs. +// Copyright (c) 2017 Erkan Tairi +// See LICENSE for licensing information. +// +// Author: +// - Erkan Tairi +// + +use crate::sidh::field::PrimeFieldElement; +use crate::sidh::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]) }; + +/// The y-coordinate of `P_A = [3^239](11, oddsqrt(11^3 + 11))` on `E_0(F_p)`. +pub const AFFINE_Y_PA: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0xbe3d7739, 0x332bd16f, 0x2319e3db, 0x7e5e20ff, 0xaefbd81b, 0xea856234, 0x6d071283, 0xe016df7d, 0xf73cd34f, 0x8ae42796, 0xa4774575, 0x6364b408, 0x7ce99497, 0xa71c97f1, 0xaa0cbe71, 0xda03cdd9, 0x195bd56f, 0xe52b4fda, 0x1fce0a46, 0xdac41f81, 0x0ee84a61, 0x9333720f, 0xf006e578, 0x1399]) }; + +/// The x-coordinate of `P_B = [2^372](6, oddsqrt(6^3 + 6))` on `E_0(F_p)`. +pub const AFFINE_X_PB: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x7b96c4ab, 0xf1a8c9ed, 0x5178486e, 0x299429da, 0x0cd5c2f4, 0xef4926f2, 0x58b4716a, 0x683b2e28, 0x3cac3eeb, 0xdda2fbcc, 0x3a600460, 0xec055f9f, 0x58c3848b, 0xd5a5a17a, 0xf42eaed5, 0x4652d836, 0x78b3a3b3, 0x2f2e71ed, 0x180add1d, 0xa771c057, 0xd835f512, 0xc780a5d2, 0xa3b55ac1, 0x114e]) }; + +/// The y-coordinate of `P_B = [2^372](6, oddsqrt(6^3 + 6))` on `E_0(F_p)`. +pub const AFFINE_Y_PB: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x73e3736b, 0xd1e14712, 0x4da241fe, 0xf9301ba9, 0x7fef0a85, 0xe14ab3c1, 0x037e9e62, 0xb4ddd26a, 0xb2afeb69, 0x66142df, 0x649d6c9e, 0xe297cb70, 0x8b1a0912, 0x214dfc6e, 0xb01cf859, 0x9f5ba818, 0x07c12828, 0x87d15b49, 0x3a880dbf, 0xa4da70c5, 0x72c8f253, 0xac5df62a, 0xa42ec617, 0x2e26]) }; + +/// The value of `(a+2)/4` for the starting curve `E_0` with `a=0`: this is `1/2`. +pub const E0_A_PLUS2_OVER4: PrimeFieldElement = PrimeFieldElement{ A: Fp751Element([0x124d6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb8e00000, 0xc0aa7287, 0x9c8a2434, 0xa9a378a3, 0xa206996c, 0x41a41b52, 0x6876280d, 0x175ce04f, 0xe903b49f, 0x0666d227, 0xf851186, 0x7cff6e7f, 0x4ea0]) }; diff --git a/zerotier-core-crypto/src/sidh/curve.rs b/zerotier-core-crypto/src/sidh/curve.rs new file mode 100644 index 000000000..75eeb74e2 --- /dev/null +++ b/zerotier-core-crypto/src/sidh/curve.rs @@ -0,0 +1,1057 @@ +// This file is part of sidh-rs. +// Copyright (c) 2017 Erkan Tairi +// See LICENSE for licensing information. +// +// Author: +// - Erkan Tairi +// + +//! 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 std::fmt::Debug; +use std::ops::Neg; +use subtle::{ConditionallySelectable, Choice}; + +#[cfg(test)] +use quickcheck::{Gen, Arbitrary}; + +// Macro to assign tuples, as Rust does not allow tuples as lvalue. +macro_rules! assign{ + {($v1:ident, $v2:ident) = $e:expr} => + { + { + let (v1, v2) = $e; + $v1 = v1; + $v2 = v2; + } + }; +} + +// = 256 +const CONST_256: ExtensionFieldElement = ExtensionFieldElement { + A: Fp751Element([0x249ad67, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x7300000, 0x9973da8b, 0x73815496, 0x46718c7f, 0x856657c1, 0xe363a697, 0x461860e4,0xbba838cd, 0xf9fd6510,0x06993c0c, 0x4e1a3c3f, 0xef5b75c7, 0x55ab]), + 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 point on the projective line `P^1(F_{p^2})`. +/// +/// This is used to work projectively with the curve coefficients. +#[derive(Copy, Clone, PartialEq)] +pub struct ProjectiveCurveParameters { + pub A: ExtensionFieldElement, + pub C: ExtensionFieldElement, +} + +struct CachedCurveParameters { + Aplus2C: ExtensionFieldElement, + C4: ExtensionFieldElement, +} + +struct CachedTripleCurveParameters { + Aminus2C: ExtensionFieldElement, + C2: ExtensionFieldElement, +} + +impl Debug for ProjectiveCurveParameters { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "ProjectiveCurveParameters(A: {:?}\nC: {:?})", &self.A, &self.C) + } +} + +#[cfg(test)] +impl Arbitrary for ProjectiveCurveParameters { + fn arbitrary(g: &mut Gen) -> ProjectiveCurveParameters { + let a = ExtensionFieldElement::arbitrary(g); + let c = ExtensionFieldElement::arbitrary(g); + ProjectiveCurveParameters{ A: a, C: c } + } +} + +impl ProjectiveCurveParameters { + pub fn from_affine(a: &ExtensionFieldElement) -> ProjectiveCurveParameters { + ProjectiveCurveParameters{ + A: *a, + 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) -> + ProjectiveCurveParameters + { + let mut t0 = ExtensionFieldElement::one(); // = 1 + let mut t1 = affine_xP * affine_xQ; // = x_P * x_Q + t0 = &t0 - &t1; // = 1 - x_P * x_Q + t1 = affine_xP * affine_xQmP; // = x_P * x_{Q-P} + t0 = &t0 - &t1; // = 1 - x_P * x_Q - x_P * x_{Q-P} + t1 = affine_xQ * affine_xQmP; // = x_Q * x_{Q-P} + t0 = &t0 - &t1; // = 1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P} + let mut a = t0.square(); // = (1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P})^2 + t1 = &t1 * affine_xP; // = x_P * x_Q * x_{Q-P} + t1 = &t1 + &t1; // = 2 * x_P * x_Q * x_{Q-P} + let c = &t1 + &t1; // = 4 * x_P * x_Q * x_{Q-P} + t0 = affine_xP + affine_xQ; // = x_P + x_Q + t0 = &t0 + affine_xQmP; // = x_P + x_Q + x_{Q-P} + t1 = &c * &t0; // = 4 * x_P * x_Q * x_{Q-P} * (x_P + x_Q + x_{Q-P}) + a = &a - &t1; // = (1 - x_P * x_Q - x_P * x_{Q-P} - x_Q * x_{Q-P})^2 - 4 * x_P * x_Q * x_{Q-P} * (x_P + x_Q + x_{Q-P}) + + ProjectiveCurveParameters{ A: a, C: c } + } + /// Compute the j-invariant of the given curve. + pub fn j_invariant(&self) -> ExtensionFieldElement { + let a = &self.A; + let c = &self.C; + let mut v0 = c.square(); // C^2 + let mut v1 = a.square(); // A^2 + let mut v2 = &v0 + &v0; // 2C^2 + let mut v3 = &v2 + &v0; // 3C^2 + v2 = &v2 + &v2; // 4C^2 + v2 = &v1 - &v2; // A^2 - 4C^2 + v1 = &v1 - &v3; // A^2 - 3C^2 + v3 = v1.square(); // (A^2 - 3C^2)^2 + v3 = &v3 * &v1; // (A^2 - 3C^2)^3 + v0 = v0.square(); // C^4 + v3 = &v3 * &CONST_256; // 256(A^2 - 3C^2)^3 + v2 = &v2 * &v0; // C^4(A^2 - 4C^2) + v2 = v2.inv(); // 1/C^4(A^2 - 4C^2) + v0 = &v3 * &v2; // 256(A^2 - 3C^2)^3 / C^4(A^2 - 4C^2) + + v0 + } + /// Compute cached parameters `A + 2C, 4C`. + fn cached_params(&self) -> CachedCurveParameters { + let mut Aplus2C = &self.C + &self.C; // = 2*C + let C4 = &Aplus2C + &Aplus2C; // = 4*C + Aplus2C = &Aplus2C + &self.A; // = 2*C + A + + CachedCurveParameters{ Aplus2C, C4 } + } + /// Compute cached parameters `A - 2C, 2C`. + fn cached_triple_params(&self) -> CachedTripleCurveParameters { + let C2 = &self.C + &self.C; // = 2*C + let Aminus2C = &self.A - &C2; // = A -2*C + + CachedTripleCurveParameters{ Aminus2C, C2 } + } +} + +/// A point on the projective line `P^1(F_{p^2})`. +/// +/// This represents a point on the (Kummer line) of a Montgomery curve. The +/// curve is specified by a ProjectiveCurveParameters struct. +#[derive(Copy, Clone, PartialEq)] +pub struct ProjectivePoint { + pub X: ExtensionFieldElement, + pub Z: ExtensionFieldElement, +} + +impl ConditionallySelectable for ProjectivePoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ProjectivePoint{ + X: ExtensionFieldElement::conditional_select(&a.X, &b.X, choice), + Z: ExtensionFieldElement::conditional_select(&a.Z, &b.Z, choice) + } + } + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + ExtensionFieldElement::conditional_swap(&mut a.X, &mut b.X, choice); + ExtensionFieldElement::conditional_swap(&mut a.Z, &mut b.Z, choice); + } +} + +impl Debug for ProjectivePoint { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "ProjectivePoint(X: {:?}\nZ: {:?})", &self.X, &self.Z) + } +} + +#[cfg(test)] +impl Arbitrary for ProjectivePoint { + fn arbitrary(g: &mut Gen) -> ProjectivePoint { + let x = ExtensionFieldElement::arbitrary(g); + let z = ExtensionFieldElement::arbitrary(g); + ProjectivePoint{ X: x, Z: z } + } +} + +impl ProjectivePoint { + /// Creates a new zero `ProejctivePoint`. + pub fn new() -> ProjectivePoint { + ProjectivePoint{ X: ExtensionFieldElement::zero(), Z: ExtensionFieldElement::zero() } + } + + pub fn from_affine_prime_field(x: &PrimeFieldElement) -> ProjectivePoint { + let _X = ExtensionFieldElement{ A: x.A, B: ExtensionFieldElement::zero().B }; + ProjectivePoint{ + X: _X, + Z: ExtensionFieldElement::one() + } + } + + pub fn from_affine(x: &ExtensionFieldElement) -> ProjectivePoint { + ProjectivePoint{ + X: *x, + Z: ExtensionFieldElement::one() + } + } + + pub fn to_affine(&self) -> ExtensionFieldElement { + 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)`. + fn add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint) -> ProjectivePoint { + let xP = *self; + // Algorithm 1 of Costello-Smith. + let mut v0 = &xP.X + &xP.Z; // X_P + Z_P + let v1 = &(&xQ.X - &xQ.Z) * &v0; // (X_Q - Z_Q)(X_P + Z_P) + v0 = &xP.X - &xP.Z; // X_P - Z_P + let v2 = &(&xQ.X + &xQ.Z) * &v0; // (X_Q + Z_Q)(X_P - Z_P) + let v3 = (&v1 + &v2).square(); // 4(X_Q X_P - Z_Q Z_P)^2 + let v4 = (&v1 - &v2).square(); // 4(X_Q Z_P - Z_Q X_P)^2 + v0 = &xPmQ.Z * &v3; // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2 + let z = &xPmQ.X * &v4; // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2 + let x = v0; + + 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)`. + fn double(&self, curve: &CachedCurveParameters) -> ProjectivePoint { + let xP = *self; + // Algorithm 2 of Costello-Smith, amended to work with projective curve coefficients. + let v1 = (&xP.X + &xP.Z).square(); // (X+Z)^2 + let mut v2 = (&xP.X - &xP.Z).square(); // (X-Z)^2 + let xz4 = &v1 - &v2; // 4XZ = (X+Z)^2 - (X-Z)^2 + v2 = &v2 * &curve.C4; // 4C(X-Z)^2 + let x = &v1 * &v2; // 4C(X+Z)^2(X-Z)^2 + let mut v3 = &xz4 * &curve.Aplus2C; // 4XZ(A + 2C) + v3 = &v3 + &v2; // 4XZ(A + 2C) + 4C(X-Z)^2 + let z = &v3 * &xz4; // (4XZ(A + 2C) + 4C(X-Z)^2)4XZ + // Now (xQ.x : xQ.z) + // = (4C(X+Z)^2(X-Z)^2 : (4XZ(A + 2C) + 4C(X-Z)^2)4XZ ) + // = ((X+Z)^2(X-Z)^2 : (4XZ((A + 2C)/4C) + (X-Z)^2)4XZ ) + // = ((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` + fn dbl_add(&self, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, params: &CachedCurveParameters) -> + (ProjectivePoint, ProjectivePoint) + { + let xP = *self; + let (x1, z1) = (&xPmQ.X, &xPmQ.Z); + let (x2, z2) = (&xP.X, &xP.Z); + let (x3, z3) = (&xQ.X, &xQ.Z); + + let mut t0 = x2 + z2; // A = x2+z2 + let mut t1 = x2 - z2; // B = x2-z2 + let mut t3 = x3 + z3; // C = x3+z3 + let mut t2 = x3 - z3; // D = x3-z3 + t2 = &t2 * &t0; // DA = D*A + t3 = &t3 * &t1; // CB = C*B + + let mut x = &t2 + &t3; // x5 = DA+CB + let mut z = &t2 - &t3; // z5 = DA-CB + x = x.square(); // x5 = (DA+CB)^2 + z = z.square(); // z5 = (DA-CB)^2 + x = &x * z1; // x5 = z1*(DA+CB)^2 + z = &z * x1; // z5 = x1*(DA-CB)^2 + let xPaddQ = ProjectivePoint{ X: x, Z: z }; + + t0 = t0.square(); // t0 = AA = A^2 + t1 = t1.square(); // t1 = BB = B^2 + t2 = &t0 - &t1; // t2 = E = AA-BB + t3 = &t1 * ¶ms.C4; // t3 = (4C)*BB + z = &t2 * ¶ms.Aplus2C; // z4 = (A+2C)*E + z = &z + &t3; // z4 = (4C)*BB+(A+2C)*E + x = &t0 * &t3; // x4 = AA*(4C)*BB + z = &z * &t2; // z4 = E*((4C)*BB+(A+2C)*E) + let x2P = ProjectivePoint{ X: x, Z: z }; + + (x2P, xPaddQ) + } + /// Given the curve parameters, `xP = x(P)`, and `k >= 0`, compute `xQ = x([2^k]P)`. + 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. + fn triple(&self, curve: &CachedTripleCurveParameters) -> ProjectivePoint { + let xP = *self; + let (x1, z1) = (&xP.X, &xP.Z); + let mut t0 = x1.square(); // t0 = x1^2 + let mut t1 = z1.square(); // t1 = z1^2 + let mut t2 = x1 + z1; // t2 = x1+z1 + t2 = t2.square(); // t2 = t2^2 + let t3 = &t0 + &t1; // t3 = t0+t1 + let mut t4 = &t2 - &t3; // t4 = t2-t3 + let mut t5 = &curve.Aminus2C * &t4; // t5 = (A-2C)*t4 + t2 = &curve.C2 * &t2; // t2 = (2C)*t2 + t5 = &t5 + &t2; // t5 = t2+t5 + t5 = &t5 + &t5; // t5 = t5+t5 + t5 = &t5 + &t5; // t5 = t5+t5 + t0 = &t0 * &t5; // t0 = t0*t5 + t1 = &t1 * &t5; // t1 = t1*t5 + t4 = &t3 - &t4; // t4 = t3-t4 + t2 = &t2 * &t4; // t2 = t2*t4 + t0 = &t2 - &t0; // t0 = t2-t0 + t1 = &t2 - &t1; // t1 = t2-t1 + t0 = t0.square(); // t0 = t0^2 + t1 = t1.square(); // t1 = t1^2 + let x = x1 * &t1; // x3 = x1*t1 + let z = z1 * &t0; // z3 = z1*t0 + + ProjectivePoint{ X: x, Z: z } + } + /// Given the curve parameters, `xP = x(P)`, and `k >= 0`, compute `xQ = x([3^k]P)`. + 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. + fn scalar_mul(&self, curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint { + let xP = *self; + let cached_params = curve.cached_params(); + let mut x0 = ProjectivePoint{ X: ExtensionFieldElement::one(), Z: ExtensionFieldElement::zero() }; + let mut x1 = xP; + let mut tmp: ProjectivePoint; + + // Iterate over the bits of the scalar, top to bottom. + let mut prev_bit: u8 = 0; + for i in (0..scalar.len()).rev() { + let scalar_byte = scalar[i]; + for j in (0..8).rev() { + let bit = (scalar_byte >> (j as u32)) & 0x1; + ProjectivePoint::conditional_swap(&mut x0, &mut x1, (bit ^ prev_bit).into()); + tmp = x0.double(&cached_params); + x1 = x0.add(&x1, &xP); + x0 = tmp; + prev_bit = bit; + } + } + // Now prev_bit is the lowest bit of the scalar. + ProjectivePoint::conditional_swap(&mut x0, &mut x1, prev_bit.into()); + 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. + fn okeya_sakurai_coordinate_recovery(affine_xP: &PrimeFieldElement, affine_yP: &PrimeFieldElement, + xQ: &ProjectivePrimeFieldPoint, xR: &ProjectivePrimeFieldPoint) -> + (PrimeFieldElement, PrimeFieldElement, PrimeFieldElement) + { + let mut v1 = affine_xP * &xQ.Z; // = x_P*Z_Q + let mut v2 = &xQ.X + &v1; // = X_Q + x_P*Z_Q + let mut v3 = (&xQ.X - &v1).square(); // = (X_Q - x_P*Z_Q)^2 + v3 = &v3 * &xR.X; // = X_R*(X_Q - x_P*Z_Q)^2 + // Skip setting v1 = 2a*Z_Q (step 6) since we hardcode a = 0. + // Skip adding v1 to v2 (step 7) since v1 is zero. + let mut v4 = affine_xP * &xQ.X; // = x_P*X_Q + v4 = &v4 + &xQ.Z; // = x_P*X_Q + Z_Q + v2 = &v2 * &v4; // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q) + // Skip multiplication by v1 (step 11) since v1 is zero. + // Skip subtracting v1 from v2 (step 12) since v1 is zero. + v2 = &v2 * &xR.Z; // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)*Z_R + let Y_Q = &v2 - &v3; // = (x_P*X_Q + Z_Q)*(X_Q + x_P*Z_Q)*Z_R - X_R*(X_Q - x_P*Z_Q)^2 + v1 = affine_yP + affine_yP; // = 2b*y_P + v1 = &(&v1 * &xQ.Z) * &xR.Z; // = 2b*y_P*Z_Q*Z_R + let X_Q = &v1 * &xQ.X; // = 2b*y_P*Z_Q*Z_R*X_Q + let Z_Q = &v1 * &xQ.Z; // = 2b*y_P*Z_Q^2*Z_R + + (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. + /// + /// 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. + // + // The algorithm, as described in de Feo-Jao-Plut, is as follows: + // + // (x0, x1, x2) <--- (x(O), x(Q), x(P)) + // + // for i = |m| down to 0, indexing the bits of m: + // Invariant: (x0, x1, x2) == (x( [t]Q ), x( [t+1]Q ), x( P + [t]Q )) + // where t = m//2^i is the high bits of m, starting at i + // if m_i == 0: + // (x0, x1, x2) <--- (xDBL(x0), xADD(x1, x0, x(Q)), xADD(x2, x0, x(P))) + // Invariant: (x0, x1, x2) == (x( [2t]Q ), x( [2t+1]Q ), x( P + [2t]Q )) + // == (x( [t']Q ), x( [t'+1]Q ), x( P + [t']Q )) + // where t' = m//2^{i-1} is the high bits of m, starting at i-1 + // if m_i == 1: + // (x0, x1, x2) <--- (xADD(x1, x0, x(Q)), xDBL(x1), xADD(x2, x1, x(P-Q))) + // Invariant: (x0, x1, x2) == (x( [2t+1]Q ), x( [2t+2]Q ), x( P + [2t+1]Q )) + // == (x( [t']Q ), x( [t'+1]Q ), x( P + [t']Q )) + // where t' = m//2^{i-1} is the high bits of m, starting at i-1 + // return x2 + // + // Notice that the roles of (x0,x1) and (x(P), x(P-Q)) swap depending on the + // current bit of the scalar. Instead of swapping which operations we do, we + // can swap variable names, producing the following uniform algorithm: + // + // (x0, x1, x2) <--- (x(O), x(Q), x(P)) + // (y0, y1) <--- (x(P), x(P-Q)) + // + // for i = |m| down to 0, indexing the bits of m: + // (x0, x1) <--- SWAP( m_{i+1} xor m_i, (x0,x1) ) + // (y0, y1) <--- SWAP( m_{i+1} xor m_i, (y0,y1) ) + // (x0, x1, x2) <--- ( xDBL(x0), xADD(x1,x0,x(Q)), xADD(x2, x0, y0) ) + // + // return x2 + // + pub fn three_point_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, + curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint + { + let cached_params = curve.cached_params(); + + // (x0, x1, x2) <--- (x(O), x(Q), x(P)) + let mut x0 = ProjectivePoint{ X: ExtensionFieldElement::one(), Z: ExtensionFieldElement::zero() }; + let mut x1 = *xQ; + let mut x2 = *xP; + // (y0, y1) <--- (x(P), x(P-Q)) + let mut y0 = *xP; + let mut y1 = *xPmQ; + + // Iterate over the bits of the scalar, top to bottom. + let mut prev_bit: u8 = 0; + for i in (0..scalar.len()).rev() { + let scalar_byte = scalar[i]; + for j in (0..8).rev() { + let bit = (scalar_byte >> (j as u32)) & 0x1; + ProjectivePoint::conditional_swap(&mut x0, &mut x1, (bit ^ prev_bit).into()); + ProjectivePoint::conditional_swap(&mut y0, &mut y1, (bit ^ prev_bit).into()); + x1 = x1.add(&x0, xQ); // = xADD(x1, x0, x(Q)) + assign!{(x0, x2) = x0.dbl_add(&x2, &y0, &cached_params)}; + prev_bit = bit; + } + } + + 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`. + pub fn right_to_left_ladder(xP: &ProjectivePoint, xQ: &ProjectivePoint, xPmQ: &ProjectivePoint, + curve: &ProjectiveCurveParameters, scalar: &[u8]) -> ProjectivePoint + { + let cached_params = curve.cached_params(); + let mut R1 = *xP; + let mut R2 = *xPmQ; + let mut R0 = *xQ; + + // Iterate over the bits of the scalar, bottom to top. + let mut prev_bit: u8 = 0; + for i in 0..scalar.len() { + let scalar_byte = scalar[i]; + for j in 0..8 { + let bit = (scalar_byte >> (j as u32)) & 0x1; + ProjectivePoint::conditional_swap(&mut R1, &mut R2, (bit ^ prev_bit).into()); + assign!{(R0, R2) = R0.dbl_add(&R2, &R1, &cached_params)}; + prev_bit = bit; + } + } + ProjectivePoint::conditional_swap(&mut R1, &mut R2, prev_bit.into()); + 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`. + 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 + let b = t1.A; // = 0 + (x_P^2 + 1)*i + t0 = affine_xP + affine_xP; // = 2*x_P + let a = t0.A; // = 2*x_P + 0*i + + let x = ExtensionFieldElement{ A: Fp751Element::zero(), B: b }; + let z = ExtensionFieldElement{ A: a, B: Fp751Element::zero() }; + 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. + // + // The computation uses basically the same strategy as the + // Costello-Longa-Naehrig implementation: + // + // 1. Use the standard Montgomery ladder to compute x([m]Q), x([m+1]Q) + // + // 2. Use Okeya-Sakurai coordinate recovery to recover [m]Q from Q, x([m]Q), + // x([m+1]Q) + // + // 3. Use P and [m]Q to compute x(P + [m]Q) + // + // The distortion map \tau is defined as + // + // \tau : E_0(F_{p^2}) ---> E_0(F_{p^2}) + // + // \tau : (x,y) |---> (-x, iy). + // + // The image of the distortion map is the _trace-zero_ subgroup of E_0(F_{p^2}) + // defined by Tr(P) = P + \pi_p(P) = id, where \pi_p((x,y)) = (x^p, y^p) is the + // p-power Frobenius map. To see this, take P = (x,y) \in E_0(F_{p^2}). Then + // Tr(P) = id if and only if \pi_p(P) = -P, so that + // + // -P = (x, -y) = (x^p, y^p) = \pi_p(P); + // + // we have x^p = x if and only if x \in F_p, while y^p = -y if and only if y = + // i*y' for y' \in F_p. + // + // Thus (excepting the identity) every point in the trace-zero subgroup is of + // the form \tau((x,y)) = (-x,i*y) for (x,y) \in E_0(F_p). + // + // Since the Montgomery ladder only uses the x-coordinate, and the x-coordinate + // is always in the prime subfield, we can compute x([m]Q), x([m+1]Q) entirely + // in the prime subfield. + // + // The affine form of the relation for Okeya-Sakurai coordinate recovery is + // given on p. 13 of Costello-Smith: + // + // y_Q = ((x_P*x_Q + 1)*(x_P + x_Q + 2*a) - 2*a - x_R*(x_P - x_Q)^2)/(2*b*y_P), + // + // where R = Q + P and a,b are the Montgomery parameters. In our setting + // (a,b)=(0,1) and our points are P=Q, Q=[m]Q, P+Q=[m+1]Q, so this becomes + // + // y_{mQ} = ((x_Q*x_{mQ} + 1)*(x_Q + x_{mQ}) - x_{m1Q}*(x_Q - x_{mQ})^2)/(2*y_Q) + // + // y_{mQ} = ((1 - x_P*x_{mQ})*(x_{mQ} - x_P) - x_{m1Q}*(x_P + x_{mQ})^2)/(2*y_P*i) + // + // y_{mQ} = i*((1 - x_P*x_{mQ})*(x_{mQ} - x_P) - x_{m1Q}*(x_P + x_{mQ})^2)/(-2*y_P) + // + // since (x_Q, y_Q) = (-x_P, y_P*i). In projective coordinates this is + // + // Y_{mQ}' = ((Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q} + // - X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2) + // + // with denominator + // + // Z_{mQ}' = (-2*y_P*Z_{mQ}*Z_{m1Q})*Z_{mQ}. + // + // Setting + // + // X_{mQ}' = (-2*y_P*Z_{mQ}*Z_{m1Q})*X_{mQ} + // + // gives [m]Q = (X_{mQ}' : i*Y_{mQ}' : Z_{mQ}') with X,Y,Z all in F_p. (Here + // the ' just denotes that we've added extra terms to the denominators during + // the computation of Y) + // + // To compute the x-coordinate x(P+[m]Q) from P and [m]Q, we use the affine + // addition formulas of section 2.2 of Costello-Smith. We're only interested + // in the x-coordinate, giving + // + // X_R = Z_{mQ}*(i*Y_{mQ} - y_P*Z_{mQ})^2 - (x_P*Z_{mQ} + X_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2 + // + // Z_R = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2. + // + // Notice that although X_R \in F_{p^2}, we can split the computation into + // coordinates X_R = X_{R,a} + X_{R,b}*i as + // + // (i*Y_{mQ} - y_P*Z_{mQ})^2 = (y_P*Z_{mQ})^2 - Y_{mQ}^2 - 2*y_P*Z_{mQ}*Y_{mQ}*i, + // + // giving + // + // X_{R,a} = Z_{mQ}*((y_P*Z_{mQ})^2 - Y_{mQ}^2) + // - (x_P*Z_{mQ} + X_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2 + // + // X_{R,b} = -2*y_P*Y_{mQ}*Z_{mQ}^2 + // + // Z_R = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2. + // + // 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. + 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(); + + // Compute x([m]Q) = (X_{mQ} : Z_{mQ}), x([m+1]Q) = (X_{m1Q} : Z_{m1Q}). + let (xmQ, xm1Q) = xQ.scalar_mul_prime_field(&E0_A_PLUS2_OVER4, scalar); + + // Now perform coordinate recovery: + // [m]Q = (X_{mQ} : Y_{mQ}*i : Z_{mQ}) + + // Y_{mQ} = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q} + // - X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2 + let mut t0 = affine_xP * &xmQ.X; // = x_P*X_{mQ} + let mut YmQ = &xmQ.Z - &t0; // = Z_{mQ} - x_P*X_{mQ} + let mut t1 = affine_xP * &xmQ.Z; // = x_P*Z_{mQ} + t0 = &xmQ.X - &t1; // = X_{mQ} - x_P*Z_{mQ} + YmQ = &YmQ * &t0; // = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ}) + YmQ = &YmQ * &xm1Q.Z; // = (Z_{mQ} - x_P*X_{mQ})*(X_{mQ} - x_P*Z_{mQ})*Z_{m1Q} + t1 = (&t1 + &xmQ.X).square(); // = (X_{mQ} + x_P*Z_{mQ})^2 + t1 = &t1 * &xm1Q.X; // = X_{m1Q}*(X_{mQ} + x_P*Z_{mQ})^2 + YmQ = &YmQ - &t1; // = Y_{mQ} + + // Z_{mQ} = -2*(Z_{mQ}^2 * Z_{m1Q} * y_P) + t0 = &(&xmQ.Z * &xm1Q.Z) * affine_yP; // = Z_{mQ} * Z_{m1Q} * y_P + t0 = (&t0).neg(); // = -1*(Z_{mQ} * Z_{m1Q} * y_P) + t0 = &t0 + &t0; // = -2*(Z_{mQ} * Z_{m1Q} * y_P) + let ZmQ = &xmQ.Z * &t0; // = -2*(Z_{mQ}^2 * Z_{m1Q} * y_P) + + // We added terms to the denominator Z_{mQ}, so multiply them to X_{mQ}. + // X_{mQ} = -2*X_{mQ}*Z_{mQ}*Z_{m1Q}*y_P + let XmQ = &xmQ.X * &t0; + + // Now compute x(P + [m]Q) = (X_Ra + i*X_Rb : Z_R) + let mut XRb = &ZmQ.square() * &YmQ; // = Y_{mQ} * Z_{mQ}^2 + XRb = &XRb * affine_yP; // = Y_{mQ} * y_P * Z_{mQ}^2 + XRb = &XRb + &XRb; // = 2 * Y_{mQ} * y_P * Z_{mQ}^2 + XRb = (&XRb).neg(); // = -2 * Y_{mQ} * y_P * Z_{mQ}^2 + + t0 = (affine_yP * &ZmQ).square(); // = (y_P * Z_{mQ})^2 + t1 = YmQ.square(); // = Y_{mQ}^2 + let mut XRa = &t0 - &t1; // = (y_P * Z_{mQ})^2 - Y_{mQ}^2 + XRa = &XRa * &ZmQ; // = Z_{mQ}*((y_P * Z_{mQ})^2 - Y_{mQ}^2) + t0 = affine_xP * &ZmQ; // = x_P * Z_{mQ} + t1 = &XmQ + &t0; // = X_{mQ} + x_P*Z_{mQ} + t0 = &XmQ - &t0; // = X_{mQ} - x_P*Z_{mQ} + t0 = t0.square(); // = (X_{mQ} - x_P*Z_{mQ})^2 + t1 = &t1 * &t0; // = (X_{mQ} + x_P*Z_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2 + XRa = &XRa - &t1; // = Z_{mQ}*((y_P*Z_{mQ})^2 - Y_{mQ}^2) - (X_{mQ} + x_P*Z_{mQ})*(X_{mQ} - x_P*Z_{mQ})^2 + + let ZR = &ZmQ * &t0; // = Z_{mQ}*(X_{mQ} - x_P*Z_{mQ})^2 + + let mut xR = ProjectivePoint{ X: ExtensionFieldElement::zero(), Z: ExtensionFieldElement::zero() }; + xR.X.A = XRa.A; + xR.X.B = XRb.A; + xR.Z.A = ZR.A; + + xR + } +} + +/// A point on the projective line `P^1(F_p)`. +/// +/// 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`. +#[derive(Copy, Clone, PartialEq)] +struct ProjectivePrimeFieldPoint { + X: PrimeFieldElement, + Z: PrimeFieldElement, +} + +impl ConditionallySelectable for ProjectivePrimeFieldPoint { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ProjectivePrimeFieldPoint{ + X: PrimeFieldElement::conditional_select(&a.X, &b.X, choice), + Z: PrimeFieldElement::conditional_select(&a.Z, &b.Z, choice) + } + } + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + PrimeFieldElement::conditional_swap(&mut a.X, &mut b.X, choice); + PrimeFieldElement::conditional_swap(&mut a.Z, &mut b.Z, choice); + } +} + +impl Debug for ProjectivePrimeFieldPoint { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "ProjectivePrimeFieldPoint(X: {:?}\nZ: {:?})", &self.X, &self.Z) + } +} + +#[cfg(test)] +impl Arbitrary for ProjectivePrimeFieldPoint { + fn arbitrary(g: &mut Gen) -> ProjectivePrimeFieldPoint { + let x = PrimeFieldElement::arbitrary(g); + let z = PrimeFieldElement::arbitrary(g); + ProjectivePrimeFieldPoint{ X: x, Z: z } + } +} + +impl ProjectivePrimeFieldPoint { + /// Creates a new zero `ProjectivePrimeFieldPoint`. + pub fn new() -> ProjectivePrimeFieldPoint { + ProjectivePrimeFieldPoint{ X: PrimeFieldElement::zero(), Z: PrimeFieldElement::zero() } + } + + pub fn from_affine(x: &PrimeFieldElement) -> ProjectivePrimeFieldPoint { + ProjectivePrimeFieldPoint{ + X: *x, + Z: PrimeFieldElement::one() + } + } + + pub fn to_affine(&self) -> PrimeFieldElement { + 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: &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) -> + ProjectivePrimeFieldPoint + { + let xP = *self; + // Algorithm 1 of Costello-Smith. + let mut v0 = &xP.X + &xP.Z; // X_P + Z_P + let v1 = &(&xQ.X - &xQ.Z) * &v0; // (X_Q - Z_Q)(X_P + Z_P) + v0 = &xP.X - &xP.Z; // X_P - Z_P + let v2 = &(&xQ.X + &xQ.Z) * &v0; // (X_Q + Z_Q)(X_P - Z_P) + let v3 = (&v1 + &v2).square(); // 4(X_Q X_P - Z_Q Z_P)^2 + let v4 = (&v1 - &v2).square(); // 4(X_Q Z_P - Z_Q X_P)^2 + v0 = &xPmQ.Z * &v3; // 4X_{P-Q}(X_Q X_P - Z_Q Z_P)^2 + let z = &xPmQ.X * &v4; // 4Z_{P-Q}(X_Q Z_P - Z_Q X_P)^2 + let x = v0; + + 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). + fn double(&self, aPlus2Over4: &PrimeFieldElement) -> ProjectivePrimeFieldPoint { + let xP = *self; + // Algorithm 2 of Costello-Smith + let v1 = (&xP.X + &xP.Z).square(); // (X+Z)^2 + let v2 = (&xP.X - &xP.Z).square(); // (X-Z)^2 + let xz4 = &v1 - &v2; // 4XZ = (X+Z)^2 - (X-Z)^2 + let x = &v1 * &v2; // (X+Z)^2(X-Z)^2 + let mut v3 = &xz4 * aPlus2Over4; // 4XZ((a+2)/4) + v3 = &v3 + &v2; // 4XZ((a+2)/4) + (X-Z)^2 + let z = &v3 * &xz4; // (4XZ((a+2)/4) + (X-Z)^2)4XZ + // Now (xQ.x : xQ.z) + // = ((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 + fn dbl_add(&self, xQ: &ProjectivePrimeFieldPoint, xPmQ: &ProjectivePrimeFieldPoint, aPlus2Over4: &PrimeFieldElement) -> + (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) + { + let xP = *self; + let x1 = &xPmQ.X; + let (x2, z2) = (&xP.X, &xP.Z); + let (x3, z3) = (&xQ.X, &xQ.Z); + + let mut t0 = x2 + z2; // A = x2+z2 + let mut t1 = x2 - z2; // B = x2-z2 + let mut t3 = x3 + z3; // C = x3+z3 + let mut t2 = x3 - z3; // D = x3-z3 + t2 = &t2 * &t0; // DA = D*A + t3 = &t3 * &t1; // CB = C*B + + let mut x = &t2 + &t3; // x5 = DA+CB + let mut z = &t2 - &t3; // z5 = DA-CB + x = x.square(); // x5 = (DA+CB)^2 + z = z.square(); // z5 = (DA-CB)^2 + z = &z * x1; // z5 = x1*(DA-CB)^2 + let xPaddQ = ProjectivePrimeFieldPoint{ X: x, Z: z }; + + t0 = t0.square(); // t0 = AA = A^2 + t1 = t1.square(); // t1 = BB = B^2 + x = &t0 * &t1; // x4 = AA*BB + t0 = &t0 - &t1; // t2 = E = AA-BB + z = &t0 * aPlus2Over4; // z4 = ((A+2C)/4)*E + z = &z + &t1; // z4 = BB+((A+2C)/4)*E + z = &z * &t0; // z4 = E*(BB+((A+2C)/4)*E) + let x2P = ProjectivePrimeFieldPoint{ X: x, Z: z }; + + (x2P, xPaddQ) + } + /// 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, + /// it can be ignored. + /// + /// 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. + fn scalar_mul_prime_field(&self, aPlus2Over4: &PrimeFieldElement, scalar: &[u8]) -> (ProjectivePrimeFieldPoint, ProjectivePrimeFieldPoint) + { + let xP = *self; + let mut x0 = ProjectivePrimeFieldPoint{ X: PrimeFieldElement::one(), Z: PrimeFieldElement::zero() }; + let mut x1 = xP; + + // Iterate over the bits of the scalar, top to bottom. + let mut prev_bit: u8 = 0; + for i in (0..scalar.len()).rev() { + let scalar_byte = scalar[i]; + for j in (0..8).rev() { + let bit = (scalar_byte >> (j as u32)) & 0x1; + ProjectivePrimeFieldPoint::conditional_swap(&mut x0, &mut x1, (bit ^ prev_bit).into()); + assign!{(x0, x1) = x0.dbl_add(&x1, &xP, aPlus2Over4)}; + prev_bit = bit; + } + } + // Now prev_bit is the lowest bit of the scalar. + ProjectivePrimeFieldPoint::conditional_swap(&mut x0, &mut x1, prev_bit.into()); + (x0, x1) + } +} + +// Sage script for generating test vectors: +// sage: p = 2^372 * 3^239 - 1; Fp = GF(p) +// sage: R. = Fp[] +// sage: Fp2 = Fp.extension(x^2 + 1, 'i') +// sage: i = Fp2.gen() +// sage: A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760 +// sage: C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307 +// sage: E = EllipticCurve(Fp2, [0,A/C,0,1,0]) +// sage: X, Y, Z = (8172151271761071554796221948801462094972242987811852753144865524899433583596839357223411088919388342364651632180452081960511516040935428737829624206426287774255114241789158000915683252363913079335550843837650671094705509470594*i + 9326574858039944121604015439381720195556183422719505497448541073272720545047742235526963773359004021838961919129020087515274115525812121436661025030481584576474033630899768377131534320053412545346268645085054880212827284581557, 2381174772709336084066332457520782192315178511983342038392622832616744048226360647551642232950959910067260611740876401494529727990031260499974773548012283808741733925525689114517493995359390158666069816204787133942283380884077*i + 5378956232034228335189697969144556552783858755832284194802470922976054645696324118966333158267442767138528227968841257817537239745277092206433048875637709652271370008564179304718555812947398374153513738054572355903547642836171, 1) +// sage: P = E((X,Y,Z)) +// sage: X2, Y2, Z2 = 2*P +// sage: X3, Y3, Z3 = 3*P +// sage: m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 +// +#[cfg(test)] +mod test { + use super::*; + use quickcheck::QuickCheck; + + // A = 4385300808024233870220415655826946795549183378139271271040522089756750951667981765872679172832050962894122367066234419550072004266298327417513857609747116903999863022476533671840646615759860564818837299058134292387429068536219*i + 1408083354499944307008104531475821995920666351413327060806684084512082259107262519686546161682384352696826343970108773343853651664489352092568012759783386151707999371397181344707721407830640876552312524779901115054295865393760 + const CURVE_A: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0xca2c435e, 0x8319eb18, 0x72cd0267, 0x3a93beae, 0x72fd5a84, 0x5e465e1f, 0x50aa7272, 0x8617fa41, 0x99d62a13, 0x887da247, 0x3c7667fe, 0xb079b31b, 0x0fa14f2e, 0xc4661b15, 0x7bc6efd6, 0xd4d2b296, 0xb7239003, 0x854215a8, 0xcba656c2, 0x61c5302c, 0x7d6f97a2, 0xf93194a2, 0x532bca75, 0x1ed9]), + B: Fp751Element([0x0e8c7db6, 0xb6f54104, 0x65342e15, 0x99403e73, 0x7c29cced, 0x457e9cee, 0x073b1d67, 0x8ece72dc, 0x7ad28d28, 0x6e73cef1, 0xca317472, 0x7aed836, 0x54263b54, 0x89e1de94, 0x7aa0071b, 0x74532927, 0x3bc86b9b, 0xf623dfc7, 0xa9245882, 0xb8e3c1d8, 0x17770bec, 0x6ad0b3d3, 0x6e8d502b, 0x5b40]) }; + // C = 933177602672972392833143808100058748100491911694554386487433154761658932801917030685312352302083870852688835968069519091048283111836766101703759957146191882367397129269726925521881467635358356591977198680477382414690421049768*i + 9088894745865170214288643088620446862479558967886622582768682946704447519087179261631044546285104919696820250567182021319063155067584445633834024992188567423889559216759336548208016316396859149888322907914724065641454773776307 + const CURVE_C: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0xbf723107, 0x4fb2358b, 0xac79e240, 0x3a791521, 0x7c4c922f, 0x283e24ef, 0x205e33cc, 0xc89baa1, 0xcff6fee1, 0x3031be81, 0x2f6a95c4, 0xaf7a494a, 0xaac83a1d, 0x248d251e, 0xe2550c88, 0xc122fca1, 0x1b6cfd3d, 0xbc0451b1, 0xb046222c, 0x9c0a114a, 0x2f21f6ea, 0x43b957b3, 0x87fa61de, 0x5b9c]), + B: Fp751Element([0xaac15ec6, 0xacf142af, 0x04a071d5, 0xfd1322a5, 0x10f6c5c6, 0x56bb205e, 0x9a97b9bd, 0xe204d284, 0x02fe7f2e, 0x40b01222, 0xafacf2cb, 0xecf72c6f, 0xf869f60a, 0x45dfc681, 0xff4af66c, 0x11814c9a, 0xeea54fe7, 0x9278b0c4, 0xaf7f2e2e, 0x9a633d5b, 0xf1a05112, 0x69a329e6, 0x4ace23e4, 0x1d87]) }; + + const CURVE: ProjectiveCurveParameters = ProjectiveCurveParameters{ A: CURVE_A, C: CURVE_C }; + const AFFINE_XP: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0xaac47247, 0xe8d05f30, 0x55441de7, 0x576ec00c, 0xfe558518, 0xbf1a8ec5, 0x77515881, 0xd77cb17f, 0x7ee73ec4, 0x8e985283, 0xd4f44a6b, 0x8159634a, 0x33a798c5, 0x2e4eb55, 0x4d5bc849, 0x9be8c435, 0x06496b84, 0xf47dc618, 0x295120e0, 0x25d0e130, 0x5f8139e3, 0xdbef5409, 0x4f20862c, 0x5a72]), + B: Fp751Element([0x23602e30, 0x3ca30d76, 0xf45f07b7, 0xfb281edd, 0x901a45bc, 0xd2bf62d5, 0x86306dd2, 0xc67c9baf, 0x93f538ca, 0x4e2bd930, 0xc25b9cbe, 0xcfd92075, 0x095bcbab, 0xceafe9a3, 0x80c85414, 0x7d928ad3, 0x2afdc095, 0x37c5f38b, 0xa7b779f4, 0x75325899, 0x49f20fdd, 0xf1305682, 0x264767d1, 0x178f]) }; + const AFFINE_XP2: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0x576ce979, 0x2a77afa8, 0x9b0aeba0, 0xab1360e6, 0xbffad660, 0xd79e3e3c, 0xa10f106b, 0x5fd0175a, 0xce9fbdbc, 0x1800ebaf, 0x2bdd6166, 0x228fc914, 0x314e34c3, 0x867cf907, 0x4c13c31c, 0xa58d18c9, 0x8b11499f, 0x699a5bc7, 0x01f7ccf1, 0xa29fc29a, 0x347eebce, 0x6c69c0c5, 0xcee0cc57, 0x38e]), + B: Fp751Element([0xf4837da0, 0x43607fd5, 0xe27f8f4a, 0x560bad4c, 0x8495b4dd, 0x2164927f, 0xb831a997, 0x621103fd, 0xea7db2db, 0xad740c4e, 0x205096cd, 0x2cde0442, 0xede8324e, 0x2af51a70, 0x0b9f3466, 0x41a4e68, 0x60b8f476, 0x5481f746, 0x56ff4d18, 0xfcb2f3e6, 0x37171acc, 0x42e3ce08, 0x8c30530c, 0x4423]) }; + const AFFINE_XP3: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0x3feca947, 0x2096e3f2, 0xa4ad8634, 0xf36f635a, 0x983c5e9a, 0xdae3b1c6, 0x62cb74b4, 0xe08df6c2, 0x37452d3d, 0xd2ca4edc, 0x2f500c79, 0xfb5f3fe4, 0xabc2b21f, 0x73740aa3, 0x9f914cca, 0xd535fd86, 0x823fb67f, 0x4a558466, 0xe3bfc715, 0x3e50a7a0, 0x183a132f, 0xf43c6da9, 0xa1e1b8b9, 0x61ac]), + B: Fp751Element([0xea5077bd, 0x1e54ec26, 0xd8769f9a, 0x61380572, 0x84f59818, 0xc6151706, 0x3e84ef6e, 0x6309c3b9, 0x18c3fcd0, 0x33c74b13, 0x835afb14, 0xfe8d7956, 0x423c1ecc, 0x2d5a7b55, 0xdfafea68, 0x869db67e, 0x94f0a628, 0x12926323, 0x25bfd141, 0x10bba482, 0xb408daba, 0x6466c28, 0xcfdb7c43, 0x63ca]) }; + const AFFINE_XAP: ExtensionFieldElement = ExtensionFieldElement{ A: Fp751Element([0xd7f938bb, 0x2112f3c7, 0xa4df08f, 0x704a677f, 0x1fb4ef00, 0x825370e3, 0x7469f902, 0xddbf79b, 0x9ea739fd, 0x27640c89, 0xf244108e, 0xfb7b8b19, 0xdd3baebc, 0x546a6679, 0x98d5265f, 0xe9f0ecf3, 0xe75e461, 0x223d2b35, 0xb6aff016, 0x84b322a0, 0x539f8b39, 0xfabe426f, 0xa0604f50, 0x4507]), + B: Fp751Element([0x5618a5fe, 0xac77737e, 0xc436ca52, 0xf91c0e08, 0xc323533c, 0xd124037b, 0x52c58b63, 0xc9a772bf, 0x8ef6af4d, 0x3b30c8f3, 0xe134f36e, 0xb9eed160, 0x93b25017, 0x24e38363, 0x11baf1d9, 0xc828be1b, 0x5df50e93, 0x7b7dab58, 0x618bd8e0, 0x1ca3852c, 0xb359fa00, 0x4efa73bc, 0xa923c2d4, 0x50b6]) }; + + // m = 96550223052359874398280314003345143371473380422728857598463622014420884224892 + const M_SCALAR_BYTES: [u8; 32] = [124, 123, 149, 250, 180, 117, 108, 72, 140, 23, 85, 180, 73, 245, 30, 163, 11, 49, 240, 164, 166, 129, 173, 148, 81, 17, 231, 245, 91, 125, 117, 213]; + + const EXTENSION_FIELD_ELEMENT_ONE: ExtensionFieldElement = ExtensionFieldElement{ + 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]), + 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]), + }; + + const THREE_POINT_LADDER_INPUTS: [ProjectivePoint; 3] = [ + // x(P) + ProjectivePoint{ + X: ExtensionFieldElement{ A: Fp751Element([0xaac47247, 0xe8d05f30, 0x55441de7, 0x576ec00c, 0xfe558518, 0xbf1a8ec5, 0x77515881, 0xd77cb17f, 0x7ee73ec4, 0x8e985283, 0xd4f44a6b, 0x8159634a, 0x33a798c5, 0x2e4eb55, 0x4d5bc849, 0x9be8c435, 0x6496b84, 0xf47dc618, 0x295120e0, 0x25d0e130, 0x5f8139e3, 0xdbef5409, 0x4f20862c, 0x5a72]), + B: Fp751Element([0x23602e30, 0x3ca30d76, 0xf45f07b7, 0xfb281edd, 0x901a45bc, 0xd2bf62d5, 0x86306dd2, 0xc67c9baf, 0x93f538ca, 0x4e2bd930, 0xc25b9cbe, 0xcfd92075, 0x95bcbab, 0xceafe9a3, 0x80c85414, 0x7d928ad3, 0x2afdc095, 0x37c5f38b, 0xa7b779f4, 0x75325899, 0x49f20fdd, 0xf1305682, 0x264767d1, 0x178f]) }, + Z: EXTENSION_FIELD_ELEMENT_ONE, + }, + // x(Q) + ProjectivePoint{ + X: ExtensionFieldElement{ A: Fp751Element([0x3ad1e10e, 0x2b71a2a9, 0x92cfb333, 0xf0b9842a, 0x15a27f5c, 0xae173736, 0x428330c4, 0x3039239f, 0xed7dcf98, 0xa0c4b735, 0xddf6af6a, 0x6e359771, 0xc4584651, 0xe986e4ca, 0x622d5518, 0x8233a2b, 0xf06b818b, 0xbfd67bf5, 0xf5b966a6, 0xdffe38d0, 0x272ee00a, 0xa86b36a3, 0x2ea4f68f, 0x193e]), + B: Fp751Element([0x59d9d998, 0x5a0f3964, 0xb1b7dda, 0x479f4225, 0x2a15bf75, 0x4016b57e, 0x3fa3749, 0xc59f9152, 0x399cf8da, 0xd5f90257, 0xd86dcef4, 0x1fb2dadf, 0x429021dc, 0x600f20e6, 0x80c57581, 0x17e347d3, 0x8fe3e440, 0xc1b0d5fa, 0x30ac20e8, 0xbcf0353, 0x6a4f03e6, 0x50c2eb5f, 0x6b7c4571, 0x8]) }, + Z: EXTENSION_FIELD_ELEMENT_ONE, + }, + // x(P-Q) + ProjectivePoint{ + X: ExtensionFieldElement{ A: Fp751Element([0x78f7b5ff, 0x4aafa9f3, 0x3aa8eee0, 0x1172a68, 0xbec2c1de, 0xea518d8c, 0x63674557, 0xe191bcbb, 0x7b259011, 0x97bc1963, 0xf4a2e454, 0xdbeae5c9, 0x72a42f95, 0x78f64d1b, 0x7e181e54, 0xe71cb4ea, 0x48543994, 0xe4169d4c, 0x6a98730f, 0x6198c228, 0xbab1afa5, 0xd21d675b, 0x69fce391, 0x2e72]), + B: Fp751Element([0xce1d0450, 0x23355783, 0x4ce3d93f, 0x683164cf, 0x25970fd8, 0xae6d1c4d, 0xb80b48cf, 0x7807007f, 0xc2bbb8a2, 0xa005a62e, 0x16004cb, 0x6b5649bd, 0x1330176b, 0xbb1a13fa, 0x87660461, 0xbf38e510, 0x5dd7b930, 0xe577fddc, 0x56947cd3, 0x5f38116f, 0x98c36fde, 0x3124f30b, 0xb6e6db37, 0x4ca9]) }, + Z: EXTENSION_FIELD_ELEMENT_ONE, + }, + ]; + + #[test] + fn one() { + let tmp = &EXTENSION_FIELD_ELEMENT_ONE * &AFFINE_XP; + assert!(tmp.vartime_eq(&AFFINE_XP), "Not equal 1"); + } + + #[test] + fn jinvariant() { + let j = CURVE.j_invariant(); + // Computed using Sage: + // j = 3674553797500778604587777859668542828244523188705960771798425843588160903687122861541242595678107095655647237100722594066610650373491179241544334443939077738732728884873568393760629500307797547379838602108296735640313894560419*i + 3127495302417548295242630557836520229396092255080675419212556702820583041296798857582303163183558315662015469648040494128968509467224910895884358424271180055990446576645240058960358037224785786494172548090318531038910933793845 + let known_j = ExtensionFieldElement{ + A: Fp751Element([0x1fb23993, 0xc7a8921c, 0x1327620b, 0xa20aea32, 0xd9676fa8, 0xf1caa17e, 0xb1a04037, 0x61b780e6, 0xc24acc7a, 0x47784af4, 0x300b9adf, 0x83926e2e, 0x6fae5b66, 0xcd891d5, 0xbeb733bc, 0x49b66985, 0x473d518f, 0xd4bcd2a, 0x91abe224, 0xe2422399, 0xf98672f8, 0xa8af5b20, 0x4d4e4d98, 0x139e]), + B: Fp751Element([0x1f81f359, 0xb5b52a2, 0x5db6d920, 0x715e3a86, 0x8911978b, 0x9bac2f9d, 0xac4c1e3d, 0xef14acd8, 0xcfb09c8, 0xe81aacd9, 0xde4a09d9, 0xaf898288, 0x8c5c4601, 0xb85a7fb8, 0xdd303387, 0x2c37c3f1, 0xe332367c, 0x7ad3277f, 0x25a8e6f8, 0xd4cbee7f, 0x79eaeffa, 0x36eacbe9, 0x5a13ac33, 0x59eb]), + }; + + assert!(j.vartime_eq(&known_j), "Computed incorrect j-invariant: found\n{:?}\nexpected\n{:?}", j, known_j); + } + + #[test] + fn projective_point_vartime_eq() { + let xP = ProjectivePoint{ X: AFFINE_XP, Z: EXTENSION_FIELD_ELEMENT_ONE }; + let mut xQ = xP; + // Scale xQ, which results in the same projective point. + xQ.X = &xQ.X * &CURVE_A; + xQ.Z = &xQ.Z * &CURVE_A; + + assert!(xQ.vartime_eq(&xP), "Expected the scaled point to be equal to the original"); + } + + #[test] + fn point_double_versus_sage() { + let xP = ProjectivePoint{ X: AFFINE_XP, Z: EXTENSION_FIELD_ELEMENT_ONE }; + let xQ = xP.pow2k(&CURVE, 1); + let affine_xQ = xQ.to_affine(); + + assert!(affine_xQ.vartime_eq(&AFFINE_XP2), "\nExpected\n{:?}\nfound\n{:?}", AFFINE_XP2, affine_xQ); + } + + #[test] + fn point_triple_versus_sage() { + let xP = ProjectivePoint{ X: AFFINE_XP, Z: EXTENSION_FIELD_ELEMENT_ONE }; + let xQ = xP.pow3k(&CURVE, 1); + let affine_xQ = xQ.to_affine(); + + assert!(affine_xQ.vartime_eq(&AFFINE_XP3), "\nExpected\n{:?}\nfound\n{:?}", AFFINE_XP3, affine_xQ); + } + + #[test] + fn point_pow2k_versus_scalar_mul() { + let byte = [32u8; 1]; + let xP = ProjectivePoint{ X: AFFINE_XP, Z: EXTENSION_FIELD_ELEMENT_ONE }; + let xQ = xP.pow2k(&CURVE, 5); // = x([32]P) + let affine_xQ = xQ.to_affine(); + let xR = xP.scalar_mul(&CURVE, &byte[..]); // = x([32]P) + let affine_xR = xR.to_affine(); + + assert!(affine_xQ.vartime_eq(&affine_xR), "\nExpected\n{:?}\nfound\n{:?}", affine_xQ, affine_xR); + } + + #[test] + fn scalar_mul_versus_sage() { + let mut xP = ProjectivePoint{ X: AFFINE_XP, Z: ExtensionFieldElement::one() }; + xP = xP.scalar_mul(&CURVE, &M_SCALAR_BYTES[..]); // = x([m]P) + let affine_xQ = xP.to_affine(); + + assert!(AFFINE_XAP.vartime_eq(&affine_xQ), "\nExpected\n{:?}\nfound\n{:?}", AFFINE_XAP, affine_xQ); + } + + #[test] + fn recover_curve_params() { + // Created using old public key generation code that output the a value: + let a = ExtensionFieldElement{ A: Fp751Element([0xaaf59ea4, 0x9331d9c5, 0xe4046931, 0xb32b702b, 0x12ed4d34, 0xcebb3339, 0xcd29c7a2, 0x5628ce37, 0x48b7f58e, 0xbeac5ed, 0x81d65b07, 0x1fb9d3e2, 0x1e195662, 0x9c0cfacc, 0x6b70f7d9, 0xae4bce0f, 0x43fe71a0, 0x59e4e63d, 0x60cc8615, 0xef7ce575, 0x901e74e8, 0xe44a8fb7, 0x3c8366d1, 0x69d1]), + B: Fp751Element([0x279ab966, 0xf6da1070, 0x7268c762, 0xa78fb0ce, 0x4a57abfa, 0x19b40f04, 0x60c0c233, 0x7ac8ee61, 0x42947072, 0x93d49934, 0xa4e44860, 0x757d2b3f, 0x8c4d5257, 0x73a920f, 0x54734037, 0x2031f1b0, 0x406555cd, 0xdefaa1d2, 0x1496be3d, 0x26f9c70e, 0xa4d0976, 0x5b3f335a, 0x8b2e9c59, 0x1362]) }; + let affine_xP = ExtensionFieldElement{ A: Fp751Element([0x2aebb250, 0xea6b2d1e, 0xdc4f6386, 0x35d0b205, 0xb1830b8d, 0xb198e93c, 0x496ddcc6, 0x3b5b456b, 0x1132c260, 0x5be3f0d4, 0x7516a00, 0xce5f1888, 0x9ea8866d, 0x54f3e746, 0x47f36286, 0x33809ef, 0xeabe1edb, 0x6fa45f83, 0x5d19fd86, 0x1b3391ae, 0x8584af3f, 0x1e66daf4, 0xc14aaa87, 0xb430]), + B: Fp751Element([0x61dcb2ad, 0x97b41ebc, 0xb932f641, 0x80ead31c, 0x9948b642, 0x40a94009, 0xcdc7fe84, 0x2a22fd16, 0x7579667f, 0xaabf35b1, 0x9feb4032, 0x76c1d013, 0x7b1949be, 0x71467e1e, 0xdd0d6d81, 0x678ca8da, 0xa9064c66, 0x14445dae, 0xb4fa4691, 0x92d161ea, 0x6b238d36, 0x8dfbb01b, 0x18434e4e, 0x2e37]) }; + let affine_xQ = ExtensionFieldElement{ A: Fp751Element([0xa1943439, 0xb055cf0c, 0xfa6c69ed, 0xa9ff5de2, 0x34e5730a, 0x4f2761f9, 0x1f94aa4b, 0x61a1dcaa, 0xfd058543, 0xce3c8fad, 0xa6701b8e, 0xeac432aa, 0x93aea8b, 0x8491d523, 0xd92b9b7f, 0xba273f9b, 0x4439bb5a, 0xd8f59fd3, 0x1c1fe600, 0xdc035026, 0xeb151311, 0x99375ab1, 0x75bbdbc5, 0x14d1]), + B: Fp751Element([0x2111a107, 0xffb0ef8c, 0x25991829, 0x55ceca38, 0xc075d34b, 0xdbf8a1cc, 0xd85d8494, 0xb8e9187b, 0xc34a03b0, 0x670aa2d5, 0x2b064953, 0xef9fe2ed, 0x1d645aee, 0xc911f531, 0x9e410507, 0xf4411f40, 0x2d03e1a8, 0x934a0a85, 0xae1ad544, 0xe6274e67, 0xc69a87bc, 0x9f4bc563, 0x6019681e, 0x6f31]) }; + let affine_xQmP = ExtensionFieldElement{ A: Fp751Element([0x6a153779, 0x6ffb4430, 0xf2f918f3, 0xc0ffef21, 0x5d77f778, 0x196c46d3, 0x52edcfe6, 0x4a73f804, 0xce61c67f, 0x9b00836b, 0x8d84219e, 0x38787941, 0xfc1ec5d1, 0x20700cf9, 0xec64155e, 0x1dfe2356, 0x38256b1c, 0xf8b9e330, 0x4bada0f0, 0xd2aaf2e1, 0x79a4e313, 0xb33b226e, 0x76fad4e5, 0x6be5]), + B: Fp751Element([0x8e00de34, 0x7db5dbc8, 0xf8b6e11e, 0x75cc8cb9, 0x4ebc52ac, 0x8c8001c0, 0x1a0b5a94, 0x67ef6c98, 0x73230738, 0xc3654fbe, 0x2983ceca, 0xc6a46ee8, 0x27ef49f0, 0xed1aa61a, 0xb0858fe0, 0x17fe5a13, 0x5a4c6b3c, 0x9ae0ca94, 0x18ad8878, 0x234104a2, 0x66104394, 0xa6196271, 0x1ff2e7e, 0x556a]) }; + + let curve_params = ProjectiveCurveParameters::recover_curve_parameters(&affine_xP, &affine_xQ, &affine_xQmP); + let tmp = &curve_params.C.inv() * &curve_params.A; + + assert!(tmp.vartime_eq(&a), "\nExpected\n{:?}\nfound\n{:?}", a, tmp); + } + + #[test] + fn three_point_ladder_versus_sage() { + let xR = ProjectivePoint::three_point_ladder(&THREE_POINT_LADDER_INPUTS[0], &THREE_POINT_LADDER_INPUTS[1], &THREE_POINT_LADDER_INPUTS[2], &CURVE, &M_SCALAR_BYTES[..]); + let affine_xR = xR.to_affine(); + let sage_affine_xR = ExtensionFieldElement{ A: Fp751Element([0x800d4fd5, 0x729465ba, 0x59e514a1, 0x9398015b, 0xe76c748e, 0x1a59dd6b, 0xb28dd55c, 0x1a7db94e, 0x80b1b8ec, 0x444686e6, 0x2a2454ff, 0xcc3d4ace, 0xec95a419, 0x51d3dab4, 0x94acac6a, 0xc3b0f335, 0x7fd44f8a, 0x9598a74e, 0x8f1c2e37, 0x4fbf8c63, 0x33052f51, 0x844e3470, 0xde3eafcf, 0x6cd6]), + B: Fp751Element([0x12d73430, 0x85da1454, 0x66eb3232, 0xd83c0e3b, 0x53ec1369, 0xd08ff2d4, 0xdb395b13, 0xa64aaacf, 0xa20e806e, 0xe9cba211, 0x5d937cfc, 0xa4f80b17, 0x4b1f7937, 0x556ce5c6, 0x2b3fdf7a, 0xb59b39ea, 0x9a4196b3, 0xc2526b86, 0xa9371750, 0x8dad90bc, 0x9d9147a2, 0xdfb4a30c, 0x2130629b, 0x346d]) }; + + assert!(affine_xR.vartime_eq(&sage_affine_xR), "\nExpected\n{:?}\nfound\n{:?}", sage_affine_xR, affine_xR); + } + + #[test] + fn right_to_left_ladder_versus_sage() { + let xR = ProjectivePoint::right_to_left_ladder(&THREE_POINT_LADDER_INPUTS[0], &THREE_POINT_LADDER_INPUTS[1], &THREE_POINT_LADDER_INPUTS[2], &CURVE, &M_SCALAR_BYTES[..]); + let affine_xR = xR.to_affine(); + let sage_affine_xR = ExtensionFieldElement{ A: Fp751Element([0x800d4fd5, 0x729465ba, 0x59e514a1, 0x9398015b, 0xe76c748e, 0x1a59dd6b, 0xb28dd55c, 0x1a7db94e, 0x80b1b8ec, 0x444686e6, 0x2a2454ff, 0xcc3d4ace, 0xec95a419, 0x51d3dab4, 0x94acac6a, 0xc3b0f335, 0x7fd44f8a, 0x9598a74e, 0x8f1c2e37, 0x4fbf8c63, 0x33052f51, 0x844e3470, 0xde3eafcf, 0x6cd6]), + B: Fp751Element([0x12d73430, 0x85da1454, 0x66eb3232, 0xd83c0e3b, 0x53ec1369, 0xd08ff2d4, 0xdb395b13, 0xa64aaacf, 0xa20e806e, 0xe9cba211, 0x5d937cfc, 0xa4f80b17, 0x4b1f7937, 0x556ce5c6, 0x2b3fdf7a, 0xb59b39ea, 0x9a4196b3, 0xc2526b86, 0xa9371750, 0x8dad90bc, 0x9d9147a2, 0xdfb4a30c, 0x2130629b, 0x346d]) }; + + assert!(affine_xR.vartime_eq(&sage_affine_xR), "\nExpected\n{:?}\nfound\n{:?}", sage_affine_xR, affine_xR); + } + + #[test] + fn point_triple_versus_add_double() { + fn triple_equals_add_double(curve: ProjectiveCurveParameters, P: ProjectivePoint) -> bool { + let cached_params = curve.cached_params(); + let cached_triple_params = curve.cached_triple_params(); + let P2 = P.double(&cached_params); // = x([2]P) + let P3 = P.triple(&cached_triple_params); // = x([3]P) + let P2plusP = P2.add(&P, &P); // = x([2]P + P) + + P3.vartime_eq(&P2plusP) + } + QuickCheck::new().quickcheck(triple_equals_add_double as fn(ProjectiveCurveParameters, ProjectivePoint) -> bool); + } + + #[test] + fn scalar_mul_prime_field_and_coordinate_recovery_versus_sage_generated_torsion_points() { + // x((11,...)) = 11 + let x11 = ProjectivePrimeFieldPoint{ + X: PrimeFieldElement{ A: Fp751Element([0x192a73, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xe6f00000, 0x3916c5c3, 0x19024ab9, 0x68876318, 0x1dcd18cf, 0xc47ba23, 0x7d8c830e, 0x9388299a, 0x3588ea6a, 0xa8e3256c, 0x8259082a, 0x3f160446, 0x3353]) }, + Z: PrimeFieldElement::one(), + }; + // y((11,...)) = oddsqrt(11^3 + 11) + let y11 = PrimeFieldElement{ A: Fp751Element([0xf57f3c8a, 0xd38a264d, 0x5042dcdf, 0x9c0450d2, 0x7bbed0b6, 0xaf1ab7be, 0x42b29630, 0xa307981c, 0xe0fa2ecb, 0x845a7e79, 0x32108f55, 0x7ef77ef7, 0x51081f0d, 0x97b58367, 0xf5275ff4, 0x59e3d115, 0x82284916, 0x9a027362, 0x96540e99, 0xec39f711, 0x8dcc965a, 0xf8b521b2, 0xb9d7f54c, 0x6af0]) }; + + // x((6,...)) = 6 + let x6 = ProjectivePrimeFieldPoint{ + X: PrimeFieldElement{ A: Fp751Element([0xdba10, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x35000000, 0xb8399915, 0x3714fe4e, 0x53eb43f4, 0xc3a25847, 0x5c520428, 0xa3151d60, 0x32c7c978, 0xc116cf52, 0x8efaf6aa, 0x49a84d4b, 0x31e97514, 0x3057]) }, + Z: PrimeFieldElement::one(), + }; + // y((6,...)) = oddsqrt(6^3 + 6) + let y6 = PrimeFieldElement{ A: Fp751Element([0xba55ff3c, 0xe4786c67, 0xc2a148e0, 0x6ffa02bc, 0xdf326e2a, 0xe1c5d019, 0xf712e87, 0x23214891, 0xee99c196, 0x6ade324b, 0x6bb821f3, 0x4372f82c, 0x5d391ec4, 0x91a374a1, 0x110b7c75, 0x6e98998b, 0xd4eeb574, 0x2e093f44, 0x68840958, 0x33cdd146, 0x9e353067, 0xb017cea8, 0x7085d4b7, 0x6f90]) }; + // Little-endian bytes of 3^239 + let three_239_bytes: [u8; 48] = [235, 142, 138, 135, 159, 84, 104, 201, 62, 110, 199, 124, 63, 161, 177, 89, 169, 109, 135, 190, 110, 125, 134, 233, 132, 128, 116, 37, 203, 69, 80, 43, 86, 104, 198, 173, 123, 249, 9, 41, 225, 192, 113, 31, 84, 93, 254, 6]; + // Little-endian bytes of 2^372 + let two_372_bytes: [u8; 47] = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16]; + + // E_0 : y^2 = x^3 + x has a = 0, so (a+2)/4 = 1/2 + let aPlus2Over4 = PrimeFieldElement{ A: Fp751Element([0x124d6, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xb8e00000, 0xc0aa7287, 0x9c8a2434, 0xa9a378a3, 0xa206996c, 0x41a41b52, 0x6876280d, 0x175ce04f, 0xe903b49f, 0x666d227, 0xf851186, 0x7cff6e7f, 0x4ea0]) }; + // Compute x(P_A) = x([3^239](11,...)) and x([3^239 + 1](11,...)) + let (xPA, xPAplus11) = x11.scalar_mul_prime_field(&aPlus2Over4, &three_239_bytes[..]); + // Compute x(P_B) = x([2^372](6,...)) and x([2^372 + 1](6,...)) + let (xPB, xPBplus6) = x6.scalar_mul_prime_field(&aPlus2Over4, &two_372_bytes[..]); + + // Check that the computed x-coordinates are correct: + let test_affine_xPA = xPA.to_affine(); + assert!(test_affine_xPA.vartime_eq(&AFFINE_X_PA), "Recomputed x(P_A) incorrectly: found\n{:?}\nexpected{:?}\n", AFFINE_X_PA, test_affine_xPA); + + let test_affine_xPB = xPB.to_affine(); + assert!(test_affine_xPB.vartime_eq(&AFFINE_X_PB), "Recomputed x(P_B) incorrectly: found\n{:?}\nexpected{:?}\n", AFFINE_X_PB, test_affine_xPB); + + // Recover y-coordinates and check that those are correct: + let (mut X_A, mut Y_A, Z_A) = ProjectivePoint::okeya_sakurai_coordinate_recovery(&x11.X, &y11, &xPA, &xPAplus11); + let invZ_A = Z_A.inv(); + Y_A = &Y_A * &invZ_A; // = Y_A / Z_A + X_A = &X_A * &invZ_A; // = X_A / Z_A + assert!(AFFINE_Y_PA.vartime_eq(&Y_A), "Recovered y(P_A) incorrectly: found\n{:?}\nexpected{:?}\n", Y_A, AFFINE_Y_PA); + assert!(AFFINE_X_PA.vartime_eq(&X_A), "Recovered x(P_A) incorrectly: found\n{:?}\nexpected{:?}\n", X_A, AFFINE_X_PA); + + let (mut X_B, mut Y_B, Z_B) = ProjectivePoint::okeya_sakurai_coordinate_recovery(&x6.X, &y6, &xPB, &xPBplus6); + let invZ_B = Z_B.inv(); + Y_B = &Y_B * &invZ_B; // = Y_B / Z_B + X_B = &X_B * &invZ_B; // = X_B / Z_B + assert!(AFFINE_Y_PB.vartime_eq(&Y_B), "Recovered y(P_B) incorrectly: found\n{:?}\nexpected{:?}\n", Y_B, AFFINE_Y_PB); + assert!(AFFINE_X_PB.vartime_eq(&X_B), "Recovered x(P_B) incorrectly: found\n{:?}\nexpected{:?}\n", X_B, AFFINE_X_PB); + } +} diff --git a/zerotier-core-crypto/src/sidh/field.rs b/zerotier-core-crypto/src/sidh/field.rs new file mode 100644 index 000000000..1a29d5137 --- /dev/null +++ b/zerotier-core-crypto/src/sidh/field.rs @@ -0,0 +1,781 @@ +// This file is part of sidh-rs. +// Copyright (c) 2017 Erkan Tairi +// See LICENSE for licensing information. +// +// Author: +// - Erkan Tairi +// + +//! This module contains finite field arithmetic functionality for SIDH, +//! which is not part of the public API. + +use crate::sidh::fp::*; + +use std::fmt::Debug; + +use std::cmp::{Eq, PartialEq}; +use std::ops::*; + +use subtle::ConditionallySelectable; +use subtle::ConstantTimeEq; +use subtle::Choice; + +#[cfg(test)] +use quickcheck::{Arbitrary, Gen}; + +//-----------------------------------------------------------------------------// +// Extension Field // +//-----------------------------------------------------------------------------// + +/// Represents an element of the extension field `F_{p^2}`. +#[derive(Copy, Clone, PartialEq)] +pub struct ExtensionFieldElement { + /// This field element is in Montgomery form, so that the value `A` is + /// represented by `aR mod p`. + pub A: Fp751Element, + /// This field element is in Montgomery form, so that the value `B` is + /// represented by `bR mod p`. + pub B: Fp751Element, +} + +impl<'b> AddAssign<&'b ExtensionFieldElement> for ExtensionFieldElement { + fn add_assign(&mut self, _rhs: &'b ExtensionFieldElement) { + let result = (self as &ExtensionFieldElement) + _rhs; + self.A = result.A; + self.B = result.B; + } +} + +impl<'a, 'b> Add<&'b ExtensionFieldElement> for &'a ExtensionFieldElement { + type Output = ExtensionFieldElement; + fn add(self, _rhs: &'b ExtensionFieldElement) -> ExtensionFieldElement { + let a = &self.A + &_rhs.A; + let b = &self.B + &_rhs.B; + + ExtensionFieldElement{ + A: a, + B: b + } + } +} + +impl <'b> SubAssign<&'b ExtensionFieldElement> for ExtensionFieldElement { + fn sub_assign(&mut self, _rhs: &'b ExtensionFieldElement) { + let result = (self as &ExtensionFieldElement) - _rhs; + self.A = result.A; + self.B = result.B; + } +} + +impl<'a, 'b> Sub<&'b ExtensionFieldElement> for &'a ExtensionFieldElement { + type Output = ExtensionFieldElement; + fn sub(self, _rhs: &'b ExtensionFieldElement) -> ExtensionFieldElement { + let a = &self.A - &_rhs.A; + let b = &self.B - &_rhs.B; + + ExtensionFieldElement{ + A: a, + B: b + } + } +} + +impl<'b> MulAssign<&'b ExtensionFieldElement> for ExtensionFieldElement { + fn mul_assign(&mut self, _rhs: &'b ExtensionFieldElement) { + let result = (self as &ExtensionFieldElement) * _rhs; + self.A = result.A; + self.B = result.B; + } +} + +impl<'a, 'b> Mul<&'b ExtensionFieldElement> for &'a ExtensionFieldElement { + type Output = ExtensionFieldElement; + fn mul(self, _rhs: &'b ExtensionFieldElement) -> ExtensionFieldElement { + // Alias self, _rhs for more readable formulas. + let a = &self.A; + let b = &self.B; + let c = &_rhs.A; + let d = &_rhs.B; + + // We want to compute + // + // (a + bi)*(c + di) = (a*c - b*d) + (a*d + b*c)i + // + // Use Karatsuba's trick: note that + // + // (b - a)*(c - d) = (b*c + a*d) - a*c - b*d + // + // so (a*d + b*c) = (b-a)*(c-d) + a*c + b*d. + // + let ac = a * c; // = a*c*R*R + let bd = b * d; // = b*d*R*R + let b_minus_a = b - a; // = (b-a)*R + let c_minus_d = c - d; // = (c-d)*R + + let mut ad_plus_bc = &b_minus_a * &c_minus_d; // = (b-a)*(c-d)*R*R + ad_plus_bc += ∾ // = ((b-a)*(c-d) + a*c)*R*R + ad_plus_bc += &bd; // = ((b-a)*(c-d) + a*c + b*d)*R*R + let _b = ad_plus_bc.reduce(); // = (a*d + b*c)*R mod p + + let ac_minus_bd = &ac - &bd; // = (a*c - b*d)*R*R + let _a = ac_minus_bd.reduce(); // = (a*c - b*d)*R mod p + + ExtensionFieldElement{ + A: _a, + B: _b + } + } +} + +impl <'a> Neg for &'a ExtensionFieldElement { + type Output = ExtensionFieldElement; + fn neg(self) -> ExtensionFieldElement { + let zero = ExtensionFieldElement::zero(); + let result = &zero - (self as &ExtensionFieldElement); + result + } +} + +impl ConditionallySelectable for ExtensionFieldElement { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + ExtensionFieldElement{ + A: Fp751Element::conditional_select(&a.A, &b.A, choice), + B: Fp751Element::conditional_select(&a.B, &b.B, choice) + } + } + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + Fp751Element::conditional_swap(&mut a.A, &mut b.A, choice); + Fp751Element::conditional_swap(&mut a.B, &mut b.B, choice); + } +} + +impl Debug for ExtensionFieldElement { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "ExtensionFieldElement(A: {:?}\nB: {:?})", &self.A.0[..], &self.B.0[..]) + } +} + +#[cfg(test)] +pub struct ExtensionFieldElementDist; + +#[cfg(test)] +impl Arbitrary for ExtensionFieldElement { + fn arbitrary(g: &mut Gen) -> ExtensionFieldElement { + let a = Fp751Element::arbitrary(g); + let b = Fp751Element::arbitrary(g); + ExtensionFieldElement{ A: a, B: b } + } +} + +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]), + } + } + /// Construct a one `ExtensionFieldElement`. + pub fn one() -> ExtensionFieldElement { + ExtensionFieldElement{ + 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]), + 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; + let b = &self.B; + + // We want to compute + // + // 1 1 (a - bi) (a - bi) + // -------- = -------- -------- = ----------- + // (a + bi) (a + bi) (a - bi) (a^2 + b^2) + // + // Letting c = 1/(a^2 + b^2), this is + // + // 1/(a+bi) = a*c - b*ci. + // + let mut asq = a * a; // = a*a*R*R + let bsq = b * b; // = b*b*R*R + asq = &asq + &bsq; // = (a^2 + b^2)*R*R + let mut asq_plus_bsq = PrimeFieldElement::zero(); + asq_plus_bsq.A = asq.reduce(); // = (a^2 + b^2)*R mod p + // Now asq_plus_bsq = a^2 + b^2 + + let asq_plus_bsq_inv = asq_plus_bsq.inv(); + let c = &asq_plus_bsq_inv.A; + + let ac = a * c; + let _a = ac.reduce(); + + let mut minus_b = Fp751Element::zero(); + minus_b = &minus_b - &b; + let minus_bc = &minus_b * &c; + let _b = minus_bc.reduce(); + + ExtensionFieldElement{ + A: _a, + B: _b + } + } + // Set (y1, y2, y3) = (1/x1, 1/x2, 1/x3). + // + // All xi, yi must be distinct. + pub fn batch3_inv(x1: &ExtensionFieldElement, x2: &ExtensionFieldElement, x3: &ExtensionFieldElement) -> + (ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement) + { + let x1x2 = x1 * x2; // x1*x2 + let mut t = &x1x2 * x3; + t = t.inv(); // 1/(x1*x2*x3) + let y1 = &t * x2; + let _y1 = &y1 * x3; // 1/x1 + let y2 = &t * x1; + let _y2 = &y2 * x3; // 1/x2 + let _y3 = &t * &x1x2; // 1/x3 + + (_y1, _y2, _y3) + } + /// Set the output to `x^2`. + pub fn square(&self) -> ExtensionFieldElement { + let a = &self.A; + let b = &self.B; + + // We want to compute + // + // (a + bi)*(a + bi) = (a^2 - b^2) + 2abi + // + let a2 = a + a; // = a*R + a*R = 2*a*R + let a_plus_b = a + b; // = a*R + b*R = (a+b)*R + let a_minus_b = a - b; // = a*R - b*R = (a-b)*R + + let asq_minus_bsq = &a_plus_b * &a_minus_b; // = (a+b)*(a-b)*R*R = (a^2 - b^2)*R*R + let ab2 = &a2 * b; // = 2*a*b*R*R + + let _a = asq_minus_bsq.reduce(); // = (a^2 - b^2)*R mod p + let _b = ab2.reduce(); // = 2*a*b*R mod p + + ExtensionFieldElement{ + A: _a, + B: _b + } + } + /// Returns true if both sides are equal. Takes variable time. + 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]; + bytes[0..94].clone_from_slice(&self.A.to_bytes()); + 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"); + let a = Fp751Element::from_bytes(&bytes[0..94]); + let b = Fp751Element::from_bytes(&bytes[94..188]); + ExtensionFieldElement{ A: a, B: b } + } +} + +//-----------------------------------------------------------------------------// +// Prime Field // +//-----------------------------------------------------------------------------// + +/// Represents an element of the prime field `F_p`. +#[derive(Copy, Clone, PartialEq)] +pub struct PrimeFieldElement { + /// This field element is in Montgomery form, so that the value `A` is + /// represented by `aR mod p`. + pub A: Fp751Element +} + +impl<'b> AddAssign<&'b PrimeFieldElement> for PrimeFieldElement { + fn add_assign(&mut self, _rhs: &'b PrimeFieldElement) { + let result = (self as &PrimeFieldElement) + _rhs; + self.A = result.A; + } +} + +impl<'a, 'b> Add<&'b PrimeFieldElement> for &'a PrimeFieldElement { + type Output = PrimeFieldElement; + fn add(self, _rhs: &'b PrimeFieldElement) -> PrimeFieldElement { + let a = &self.A + &_rhs.A; + PrimeFieldElement{ A: a } + } +} + +impl <'b> SubAssign<&'b PrimeFieldElement> for PrimeFieldElement { + fn sub_assign(&mut self, _rhs: &'b PrimeFieldElement) { + let result = (self as &PrimeFieldElement) - _rhs; + self.A = result.A; + } +} + +impl<'a, 'b> Sub<&'b PrimeFieldElement> for &'a PrimeFieldElement { + type Output = PrimeFieldElement; + fn sub(self, _rhs: &'b PrimeFieldElement) -> PrimeFieldElement { + let a = &self.A - &_rhs.A; + PrimeFieldElement{ A: a } + } +} + +impl<'b> MulAssign<&'b PrimeFieldElement> for PrimeFieldElement { + fn mul_assign(&mut self, _rhs: &'b PrimeFieldElement) { + let result = (self as &PrimeFieldElement) * _rhs; + self.A = result.A; + } +} + +impl<'a, 'b> Mul<&'b PrimeFieldElement> for &'a PrimeFieldElement { + type Output = PrimeFieldElement; + fn mul(self, _rhs: &'b PrimeFieldElement) -> PrimeFieldElement { + // Alias self, _rhs for more readable formulas. + let a = &self.A; // = a*R + let b = &_rhs.A; // = b*R + let ab = a * b; // = a*b*R*R + let _a = ab.reduce(); // = a*b*R mod p + + PrimeFieldElement{ A: _a } + } +} + +impl <'a> Neg for &'a PrimeFieldElement { + type Output = PrimeFieldElement; + fn neg(self) -> PrimeFieldElement { + let zero = PrimeFieldElement::zero(); + let result = &zero - (self as &PrimeFieldElement); + result + } +} + +impl ConditionallySelectable for PrimeFieldElement { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + PrimeFieldElement{ + A: Fp751Element::conditional_select(&a.A, &b.A, choice) + } + } + + fn conditional_swap(a: &mut Self, b: &mut Self, choice: Choice) { + Fp751Element::conditional_swap(&mut a.A, &mut b.A, choice); + } +} + +impl Debug for PrimeFieldElement { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "PrimeFieldElement(A: {:?})", &self.A.0[..]) + } +} + +#[cfg(test)] +impl Arbitrary for PrimeFieldElement { + fn arbitrary(g: &mut Gen) -> PrimeFieldElement { + let a = Fp751Element::arbitrary(g); + PrimeFieldElement{ A: a } + } +} + +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]), + } + } + /// 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`. + pub fn square(&self) -> PrimeFieldElement { + let a = &self.A; // = a*R + let b = &self.A; // = b*R + let ab = a * b; // = a*b*R*R + let _a = ab.reduce(); // = a*b*R mod p + + 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. + // + // This performs sum(powStrategy) = 744 squarings and len(mulStrategy) + // = 137 multiplications, in addition to 1 squaring and 15 + // multiplications to build a lookup table. + // + // In total this is 745 squarings, 152 multiplications. Since squaring + // is not implemented for the prime field, this is 897 multiplications + // in total. + let pow_strategy: [u8; 137] = [5, 7, 6, 2, 10, 4, 6, 9, 8, 5, 9, 4, 7, 5, 5, 4, 8, 3, 9, 5, 5, 4, 10, 4, 6, 6, 6, 5, 8, 9, 3, 4, 9, 4, 5, 6, 6, 2, 9, 4, 5, 5, 5, 7, 7, 9, 4, 6, 4, 8, 5, 8, 6, 6, 2, 9, 7, 4, 8, 8, 8, 4, 6, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 2]; + let mul_strategy: [u8; 137] = [31, 23, 21, 1, 31, 7, 7, 7, 9, 9, 19, 15, 23, 23, 11, 7, 25, 5, 21, 17, 11, 5, 17, 7, 11, 9, 23, 9, 1, 19, 5, 3, 25, 15, 11, 29, 31, 1, 29, 11, 13, 9, 11, 27, 13, 19, 15, 31, 3, 29, 23, 31, 25, 11, 1, 21, 19, 15, 15, 21, 29, 13, 23, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 3]; + let initial_mul: u8 = 27; + + // Build a lookup table of odd multiples of x. + let mut lookup = [PrimeFieldElement::zero(); 16]; + let xx: &PrimeFieldElement = &self.square(); // Set xx = x^2 + lookup[0] = *self; + + for i in 1..16 { + lookup[i as usize] = &lookup[(i-1) as usize] * xx; + } + // Now lookup = {x, x^3, x^5, ... } + // so that lookup[i] = x^{2*i + 1} + // so that lookup[k/2] = x^k, for odd k + let mut result = lookup[(initial_mul / 2) as usize]; + for i in 0..137 { + result = result.pow2k(pow_strategy[i]); + result = &result * &lookup[(mul_strategy[i] / 2) as usize]; + } + 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) + result = &result * self; // result = y^2 * y^((p-3)/2) = y^((p+1)/2) + // 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 + result = result.p34(); // result = (x^2)^((p-3)/4) = x^((p-3)/2) + result = result.square(); // result = x^(p-3) + result = &result * self; // result = x^(p-2) + result + } + /// Returns true if both sides are equal. Takes variable time. + pub fn vartime_eq(&self, _rhs: &PrimeFieldElement) -> bool { + &self.A == &_rhs.A + } +} + +//-----------------------------------------------------------------------------// +// Internals // +//-----------------------------------------------------------------------------// + +impl<'b> AddAssign<&'b Fp751Element> for Fp751Element { + fn add_assign(&mut self, _rhs: &'b Fp751Element) { + let result = (self as &Fp751Element) + _rhs; + self.0 = result.0 + } +} + +impl<'a, 'b> Add<&'b Fp751Element> for &'a Fp751Element { + type Output = Fp751Element; + fn add(self, _rhs: &'b Fp751Element) -> Fp751Element { + let mut result = Fp751Element::zero(); + fpadd751(&self, _rhs, &mut result); + result + } +} + +impl <'b> SubAssign<&'b Fp751Element> for Fp751Element { + fn sub_assign(&mut self, _rhs: &'b Fp751Element) { + let result = (self as &Fp751Element) - _rhs; + self.0 = result.0 + } +} + +impl<'a, 'b> Sub<&'b Fp751Element> for &'a Fp751Element { + type Output = Fp751Element; + fn sub(self, _rhs: &'b Fp751Element) -> Fp751Element { + let mut result = Fp751Element::zero(); + fpsub751(&self, _rhs, &mut result); + result + } +} + +impl<'a, 'b> Mul<&'b Fp751Element> for &'a Fp751Element { + type Output = Fp751X2; + fn mul(self, _rhs: &'b Fp751Element) -> Fp751X2 { + let mut result = Fp751X2::zero(); + mul751(&self, _rhs, &mut result); // = a*c*R*R + result + } +} + +impl <'a> Neg for &'a Fp751Element { + type Output = Fp751Element; + fn neg(self) -> Fp751Element { + let zero = Fp751Element::zero(); + let result = &zero - (self as &Fp751Element); + result + } +} + +impl Eq for Fp751Element {} +impl PartialEq for Fp751Element { + /// Test equality between two `Fp751Element`s. + /// + /// # Warning + /// + /// This comparison is *not* constant time. + fn eq(&self, other: &Fp751Element) -> bool { + let mut _self = *self; + let mut _other = *other; + + _self = _self.strong_reduce(); + _other = _other.strong_reduce(); + + let mut eq: bool = true; + for i in 0..FP751_NUM_WORDS { + eq = (_self.0[i] == _other.0[i]) && eq; + } + eq + } +} + +impl ConstantTimeEq for Fp751Element { + /// Test equality between two `Fp751Element`s. + /// + /// # Returns + /// + /// `1u8` if the two `Fp751Element`s are equal, and `0u8` otherwise. + fn ct_eq(&self, other: &Fp751Element) -> Choice { + let self_bytes = self.to_bytes(); + let other_bytes = other.to_bytes(); + let len = self_bytes.len(); + + // Short-circuit on the *lengths* of the slices, not their + // contents. + if len != other_bytes.len() { + return Choice::from(0); + } + + // This loop shouldn't be shortcircuitable, since the compiler + // shouldn't be able to reason about the value of the `u8` + // unwrapped from the `ct_eq` result. + let mut x = 1u8; + for (selfi, otheri) in self_bytes.iter().zip(other_bytes.iter()) { + x &= selfi.ct_eq(otheri).unwrap_u8(); + } + + x.into() + } +} + +// #[cfg(test)] +// impl Arbitrary for Fp751Element { +// fn arbitrary(g: &mut Gen) -> Fp751Element { +// Fp751Element::arbitrary(g); +// } +// } + +impl Fp751Element { + /// Reduce a field element in `[0, 2*p)` to one in `[0,p)`. + pub fn strong_reduce(&self) -> Fp751Element { + let mut _self = *self; + srdc751(&mut _self); + _self + } +} + +impl<'b> AddAssign<&'b Fp751X2> for Fp751X2 { + fn add_assign(&mut self, _rhs: &'b Fp751X2) { + let result = (self as &Fp751X2) + _rhs; + self.0 = result.0 + } +} + +impl<'a, 'b> Add<&'b Fp751X2> for &'a Fp751X2 { + type Output = Fp751X2; + fn add(self, _rhs: &'b Fp751X2) -> Fp751X2 { + let mut result = Fp751X2::zero(); + mp_add751x2(&self, _rhs, &mut result); + result + } +} + +impl <'b> SubAssign<&'b Fp751X2> for Fp751X2 { + fn sub_assign(&mut self, _rhs: &'b Fp751X2) { + let result = (self as &Fp751X2) - _rhs; + self.0 = result.0 + } +} + +impl<'a, 'b> Sub<&'b Fp751X2> for &'a Fp751X2 { + type Output = Fp751X2; + fn sub(self, _rhs: &'b Fp751X2) -> Fp751X2 { + let mut result = Fp751X2::zero(); + mp_sub751x2(&self, _rhs, &mut result); + result + } +} + +impl Fp751X2 { + /// Perform Montgomery reduction, `x R^{-1} (mod p)`. + pub fn reduce(&self) -> Fp751Element { + let mut result = Fp751Element::zero(); + rdc751(self, &mut result); + result + } +} + +#[inline(always)] +pub fn checklt238(scalar: &[u8; 48], result: &mut u32) { + crate::sidh::fp::checklt238(scalar, result); +} + +#[inline(always)] +pub fn mulby3(scalar: &mut [u8; 48]) { + crate::sidh::fp::mulby3(scalar); +} + +#[cfg(test)] +mod test { + use super::*; + use quickcheck::QuickCheck; + + const SCALE_FACTOR: u8 = 3; + const MAX_TESTS: u64 = 1 << (10 + SCALE_FACTOR); + + #[test] + fn one_extension_field_to_byte() { + let one = &ExtensionFieldElement::one(); + let bytes = one.to_bytes(); + + assert_eq!(bytes[0], 1); + + for i in 1..188 { + assert_eq!(bytes[i], 0); + } + } + + #[test] + fn extension_field_element_to_bytes_round_trip() { + fn round_trips(x: ExtensionFieldElement) -> bool { + let bytes = x.to_bytes(); + let x_prime = ExtensionFieldElement::from_bytes(&bytes); + x.vartime_eq(&x_prime) + } + QuickCheck::new().max_tests(MAX_TESTS) + .quickcheck(round_trips as fn(ExtensionFieldElement) -> bool); + } + + #[test] + fn extension_field_element_mul_distributes_over_add() { + fn mul_distributes_over_add(x: ExtensionFieldElement, y: ExtensionFieldElement, z: ExtensionFieldElement) -> bool { + // Compute t1 = (x+y)*z + let t1 = &(&x + &y) * &z; + // Compute t2 = x*z + y*z + let t2 = &(&x * &z) + &(&y * &z); + + t1.vartime_eq(&t2) + } + QuickCheck::new().max_tests(MAX_TESTS) + .quickcheck(mul_distributes_over_add as fn(ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement) -> bool); + } + + #[test] + fn extension_field_element_mul_is_associative() { + fn is_associative(x: ExtensionFieldElement, y: ExtensionFieldElement, z: ExtensionFieldElement) -> bool { + // Compute t1 = (x*y)*z + let t1 = &(&x * &y) * &z; + // Compute t2 = (y*z)*x + let t2 = &(&y * &z) * &x; + + t1.vartime_eq(&t2) + } + QuickCheck::new().max_tests(MAX_TESTS) + .quickcheck(is_associative as fn(ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement) -> bool); + } + + #[test] + fn extension_field_element_square_matches_mul() { + fn square_matches_mul(x: ExtensionFieldElement) -> bool { + // Compute t1 = (x*x) + let t1 = &x * &x; + // Compute t2 = x^2 + let t2 = x.square(); + + t1.vartime_eq(&t2) + } + QuickCheck::new().max_tests(MAX_TESTS) + .quickcheck(square_matches_mul as fn(ExtensionFieldElement) -> bool); + } + + #[test] + fn extension_field_element_inv() { + fn inverse(x: ExtensionFieldElement) -> bool { + let mut z = x.inv(); + // Now z = (1/x), so (z * x) * x == x + z = &(&z * &x) * &x; + + z.vartime_eq(&x) + } + QuickCheck::new().max_tests(MAX_TESTS) + .quickcheck(inverse as fn(ExtensionFieldElement) -> bool); + } + + #[test] + fn extension_field_element_batch3_inv() { + fn batch_inverse(x1: ExtensionFieldElement, x2: ExtensionFieldElement, x3: ExtensionFieldElement) -> bool { + let x1_inv = x1.inv(); + let x2_inv = x2.inv(); + let x3_inv = x3.inv(); + + let (y1, y2, y3) = ExtensionFieldElement::batch3_inv(&x1, &x2, &x3); + + y1.vartime_eq(&x1_inv) && y2.vartime_eq(&x2_inv) && y3.vartime_eq(&x3_inv) + } + QuickCheck::new().max_tests(MAX_TESTS) + .quickcheck(batch_inverse as fn(ExtensionFieldElement, ExtensionFieldElement, ExtensionFieldElement) -> bool); + } + + #[test] + fn prime_field_element_inv() { + fn inverse(x: PrimeFieldElement) -> bool { + let mut z = x.inv(); + // Now z = (1/x), so (z * x) * x == x + z = &(&z * &x) * &x; + + z.vartime_eq(&x) + } + QuickCheck::new().max_tests(MAX_TESTS) + .quickcheck(inverse as fn(PrimeFieldElement) -> bool); + } + + #[test] + fn prime_field_element_sqrt() { + fn square_root(x: PrimeFieldElement) -> bool { + // Construct y = x^2 so we're sure y is square. + let y = x.square(); + let mut z = y.sqrt(); + // Now z = sqrt(y), so z^2 == y + z = z.square(); + + z.vartime_eq(&y) + } + QuickCheck::new().max_tests(MAX_TESTS) + .quickcheck(square_root as fn(PrimeFieldElement) -> bool); + } + + #[test] + fn fp751_element_conditional_swap() { + let one = Fp751Element([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]); + let two = Fp751Element([2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2]); + + let mut x = one; + let mut y = two; + + Fp751Element::conditional_swap(&mut x, &mut y, 0.into()); + assert_eq!(x, one); + assert_eq!(y, two); + + Fp751Element::conditional_swap(&mut x, &mut y, 1.into()); + assert_eq!(x, two); + assert_eq!(y, one); + } +} diff --git a/zerotier-core-crypto/src/sidh/fp.rs b/zerotier-core-crypto/src/sidh/fp.rs new file mode 100644 index 000000000..f6a2ceb5f --- /dev/null +++ b/zerotier-core-crypto/src/sidh/fp.rs @@ -0,0 +1,460 @@ +// This file is part of sidh-rs. +// Copyright (c) 2017 Erkan Tairi +// See LICENSE for licensing information. +// +// Author: +// - Erkan Tairi +// + +use crate::random::SecureRandom; + +use std::mem::size_of; +use std::fmt::Debug; +use std::ops::Neg; + +#[cfg(test)] +use quickcheck::{Arbitrary, Gen}; + +use subtle::{ConditionallySelectable, Choice}; +use rand_core::RngCore; + +// Macro to assign tuples, as Rust does not allow tuples as lvalue. +#[macro_export] +macro_rules! assign{ + {($v1:ident, $v2:expr) = $e:expr} => + { + { + let (v1, v2) = $e; + $v1 = v1; + $v2 = v2; + } + }; +} + +// X86 finite field arithmetic +const RADIX: u32 = 32; +pub const FP751_NUM_WORDS: usize = 24; +const P751_ZERO_WORDS: usize = 11; + +const P751: [u32; FP751_NUM_WORDS] = [4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4294967295, 4004511743, 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]; + +// 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); + + let al = a & mask_low; + let ah = a >> (sizeof_u32 * 4); + let bl = b & mask_low; + let bh = b >> (sizeof_u32 * 4); + + let albl = al * bl; + let albh = al * bh; + let ahbl = ah * bl; + let ahbh = ah * bh; + c[0] = 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); + + res1 = ahbl >> (sizeof_u32 * 4); + res2 = albh >> (sizeof_u32 * 4); + res3 = ahbh & mask_low; + temp = res1 + res2 + res3 + carry; + c[1] = temp & mask_low; + carry = temp & mask_high; + c[1] ^= (ahbh & mask_high) + carry; +} + +fn mul(multiplier: &u32, multiplicant: &u32, uv: &mut [u32]) { + digit_x_digit(multiplier, multiplicant, uv); +} + +fn addc(carry_in: &u32, addend1: &u32, addend2: &u32) -> (u32, 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) +} + +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) +} + +#[inline] +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])}; + } + + carry = 0; + for i in 0..FP751_NUM_WORDS { + 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))}; + } +} + +#[inline] +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])}; + } + 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))}; + } +} + +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; + 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)}; + t += carry; + } + z.0[i] = v; + v = u; + u = t; + t = 0; + } + + 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)}; + t += carry; + } + z.0[i] = v; + v = u; + u = t; + t = 0; + } + z.0[2*FP751_NUM_WORDS-1] = v; +} + +pub fn rdc751(x: &Fp751X2, z: &mut Fp751Element) { + let mut t: u32 = 0; + let mut u: u32 = 0; + let mut v: u32 = 0; + let mut UV = [0u32; 2]; + let mut carry: u32 = 0; + let mut count = P751_ZERO_WORDS; + + for i in 0..FP751_NUM_WORDS { + z.0[i] = 0; + } + + 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)}; + t += carry; + } + } + assign!{(carry, v) = addc(&0, &v, &x.0[i])}; + assign!{(carry, u) = addc(&carry, &u, &0)}; + + t += carry; + z.0[i] = v; + v = u; + u = t; + t = 0; + } + + for i in FP751_NUM_WORDS..(2*FP751_NUM_WORDS-1) { + if count > 0 { + count -= 1; + } + 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)}; + t += carry; + } + } + 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; + v = u; + u = t; + t = 0; + } + assign!{(carry, v) = addc(&0, &v, &x.0[2*FP751_NUM_WORDS-1])}; + z.0[FP751_NUM_WORDS-1] = v; +} + +#[inline] +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])}; + } + 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))}; + } +} + +#[inline] +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])}; + } +} + +#[inline] +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])}; + } +} + +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])}; + } + 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))}; + } +} + +pub fn checklt238(scalar: &[u8; 48], result: &mut u32) { + let three238: [u32; 12] = [0x828384f8, 0xedcd718a, 0xd4427a14, 0x733b35bf, 0x94d7cf38, 0xf88229cf, 0xc7c2ad6, 0x63c56c99, 0x8f4222c7, 0xb858a87e, 0xb525eaf5, 0x254c9c6]; + let mut scalar_u32 = [0u32; 12]; + let mut ignored: u32 = 0; + + let mut j; + let mut k: u32; + for i in 0..48 { + j = i / 4; + k = (i % 4) as u32; + scalar_u32[j as usize] |= (scalar[i] as u32) << (8 * k); + } + + let mut borrow: u32 = 0; + + for i in 0..12 { + assign!{(borrow, ignored) = subc(&borrow, &three238[i], &scalar_u32[i])}; + } + let mask = (0 as u32).wrapping_sub(borrow); + *result = mask; +} + +pub fn mulby3(scalar: &mut [u8; 48]) { + let mut scalar_u32 = [0u32; 12]; + + let mut j; + let mut k: u32; + for i in 0..48 { + j = i / 4; + k = (i % 4) as u32; + scalar_u32[j as usize] |= (scalar[i] as u32) << (8 * k); + } + + 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])}; + } + for i in 0..12 { + assign!{(carry, scalar_u32[i]) = addc(&carry, &scalar_u32[i], &temp[i])}; + } + + for i in 0..48 { + j = i / 4; + k = (i % 4) as u32; + scalar[i as usize] = (scalar_u32[j as usize] >> (8 * k)) as u8; + } +} + +#[derive(Copy, Clone)] +pub struct Fp751Element(pub (crate) [u32; FP751_NUM_WORDS]); + +#[cfg(test)] +pub struct Fp751ElementDist; + +impl ConditionallySelectable for Fp751Element { + fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self { + let mut bytes = [0_u32; FP751_NUM_WORDS]; + for i in 0..FP751_NUM_WORDS { + bytes[i] = u32::conditional_select(&a.0[i], &b.0[i], choice); + } + Fp751Element(bytes) + } + + fn conditional_assign(&mut self, f: &Self, choice: Choice) { + let mask = ((choice.unwrap_u8() as i32).neg()) as u32; + for i in 0..FP751_NUM_WORDS { + self.0[i] ^= mask & (self.0[i] ^ f.0[i]); + } + } +} + +impl Debug for Fp751Element { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Fp751Element({:?})", &self.0[..]) + } +} + +#[cfg(test)] +impl Arbitrary for Fp751Element { + fn arbitrary(g: &mut Gen) -> Fp751Element { + // Generation strategy: low limbs taken from [0,2^64), high limb + // taken from smaller range. + // + // Field elements taken in range [0,2p). Emulate this by capping + // the high limb by the top digit of 2*p-1: + // + // sage: (2*p-1).digits(2^32)[-1] + // 57291 + // + // This still allows generating values >= 2p, but hopefully that + // excess is small. + let mut rng = SecureRandom::get(); + let high_limb = rng.next_u32() % 57291; + + Fp751Element([ + rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), + rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), + rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), + rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), + rng.next_u32(), rng.next_u32(), rng.next_u32(), rng.next_u32(), + rng.next_u32(), rng.next_u32(), rng.next_u32(), high_limb + ]) + } +} + +impl Fp751Element { + /// Construct a new 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]) + } + /// 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(); + 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]); + + aR.0[..FP751_NUM_WORDS].clone_from_slice(&self.0); + //aR = self * &one; + a = aR.reduce(); // = a mod p in [0, 2p) + a = a.strong_reduce(); // = a mod p in [0, p) + + let mut j; + let mut k: u32; + // 4*24 = 96, but we drop the last two bytes since p is 751 < 752=94*8 bits. + for i in 0..94 { + j = i / 4; + k = (i % 4) as u32; + // Rust indexes are of type usize. + bytes[i as usize] = (a.0[j as usize] >> (8 * k)) as u8; + } + bytes + } + /// Read an `Fp751Element` from little-endian bytes and convert to Montgomery form. + pub fn from_bytes(bytes: &[u8]) -> Fp751Element { + assert!(bytes.len() >= 94, "Too short input to Fp751Element from_bytes, expected 94 bytes"); + + let mut a = Fp751Element::zero(); + let mut j; + let mut k: u32; + for i in 0..94 { + j = i / 4; + k = (i % 4) as u32; + // Rust indexes are of type usize. + a.0[j as usize] |= (bytes[i as usize] as u32) << (8 * k); + } + + let aRR = &a * &MONTGOMERY_RSQ; // = a*R*R + let output = aRR.reduce(); // = a*R mod p + output + } +} + +#[derive(Copy, Clone)] +pub struct Fp751X2(pub (crate) [u32; 2*FP751_NUM_WORDS]); + +impl Debug for Fp751X2 { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Fp751X2({:?})", &self.0[..]) + } +} + +impl Fp751X2 { + // Construct a 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, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0]) + } +} + +/// `(2^768) mod p` +//pub const MONTGOMERY_R: Fp751Element = Fp751Element([149933, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2198863872, 928803942, 1428664804, 1062151376, 1769445311, 2891730478, 3365779378, 3523701078, 1285862457, 1964165097, 284660332, 616359394, 11611]); + +/// `(2^768)^2 mod p` +pub const MONTGOMERY_RSQ: Fp751Element = Fp751Element([2645377112, 590366276, 2794865962, 3674276193, 1927544206, 1580635156, 2191714054, 4094426656, 2421131089, 1228065960, 518519937, 527654687, 3238301208, 2723106176, 3451258821, 3043768380, 1935645840, 1142805627, 1785382954, 1450437932, 288500043, 113837350, 2198806325, 16813]); diff --git a/zerotier-core-crypto/src/sidh/isogeny.rs b/zerotier-core-crypto/src/sidh/isogeny.rs new file mode 100644 index 000000000..e80db36ce --- /dev/null +++ b/zerotier-core-crypto/src/sidh/isogeny.rs @@ -0,0 +1,384 @@ +// This file is part of sidh-rs. +// Copyright (c) 2017 Erkan Tairi +// See LICENSE for licensing information. +// +// Author: +// - Erkan Tairi +// + +//! 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}; + +/// Represents a 3-isogeny phi, holding the data necessary to evaluate phi. +#[derive(Copy, Clone)] +pub struct ThreeIsogeny { + pub X: ExtensionFieldElement, + pub Z: ExtensionFieldElement, +} + +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')`. + /// + /// Returns a tuple `(codomain, isogeny) = (E_(A':C'), phi)`. + pub fn compute_three_isogeny(x3: &ProjectivePoint) -> (ProjectiveCurveParameters, ThreeIsogeny) { + let isogeny = ThreeIsogeny{ X: x3.X, Z: x3.Z }; + // We want to compute + // (A':C') = (Z^4 + 18X^2Z^2 - 27X^4 : 4XZ^3) + // To do this, use the identity 18X^2Z^2 - 27X^4 = 9X^2(2Z^2 - 3X^2) + let mut v1 = x3.X.square(); // = X^2 + let mut v0 = &(&v1 + &v1) + &v1; // = 3X^2 + v1 = &(&v0 + &v0) + &v0; // = 9X^2 + let mut v2 = x3.Z.square(); // = Z^2 + let v3 = v2.square(); // = Z^4 + v2 = &v2 + &v2; // = 2Z^2 + v0 = &v2 - &v0; // = 2Z^2 - 3X^2 + v1 = &v1 * &v0; // = 9X^2(2Z^2 - 3X^2) + v0 = &x3.X * &x3.Z; // = XZ + v0 = &v0 + &v0; // = 2XZ + let a = &v3 + &v1; // = Z^4 + 9X^2(2Z^2 - 3X^2) + let c = &v0 * &v2; // = 4XZ^3 + let codomain = ProjectiveCurveParameters{ A: a, C: c }; + + (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')`. + /// + /// The output `xQ = x(Q)` is then a point on the curve `E_(A':C')`; the curve + /// parameters are returned by the compute_three_isogeny function used to construct + /// phi. + pub fn eval(&self, xP: &ProjectivePoint) -> ProjectivePoint { + let phi = *self; + let mut t0 = &phi.X * &xP.X; // = X3*XP + let mut t1 = &phi.Z * &xP.Z; // = Z3*XP + let mut t2 = &t0 - &t1; // = X3*XP - Z3*ZP + t0 = &phi.Z * &xP.X; // = Z3*XP + t1 = &phi.X * &xP.Z; // = X3*ZP + t0 = &t0 - &t1; // = Z3*XP - X3*ZP + t2 = t2.square(); // = (X3*XP - Z3*ZP)^2 + t0 = t0.square(); // = (Z3*XP - X3*ZP)^2 + let x = &t2 * &xP.X; // = XP*(X3*XP - Z3*ZP)^2 + let z = &t0 * &xP.Z; // = ZP*(Z3*XP - X3*ZP)^2 + let xQ = ProjectivePoint{ X: x, Z: z }; + + xQ + } +} + +/// Represents a 4-isogeny phi, holding the data necessary to evaluate phi. +// +// See compute_four_isogeny for more details. +#[derive(Copy, Clone)] +pub struct FourIsogeny { + pub Xsq_plus_Zsq : ExtensionFieldElement, + pub Xsq_minus_Zsq: ExtensionFieldElement, + pub XZ2 : ExtensionFieldElement, + pub Xpow4 : ExtensionFieldElement, + pub Zpow4 : ExtensionFieldElement, +} + +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) -> + /// E_(A:C)/`. + /// + /// Returns a tuple `(codomain, isogeny) = (E_(A':C') : phi)`. + // + // There are two sets of formulas in Costello-Longa-Naehrig for computing + // four-isogenies. One set is for the case where (1,...) lies in the kernel of + // the isogeny (this is the FirstFourIsogeny), and the other (this set) is for + // the case that (1,...) is *not* in the kernel. + pub fn compute_four_isogeny(x4: &ProjectivePoint) -> (ProjectiveCurveParameters, FourIsogeny) { + let mut v0 = x4.X.square(); // = X4^2 + let v1 = x4.Z.square(); // = Z4^2 + let Xsq_plus_Zsq = &v0 + &v1; // = X4^2 + Z4^2 + let Xsq_minus_Zsq = &v0 - &v1; // = X4^2 - Z4^2 + let mut XZ2 = &x4.X + &x4.Z; // = X4 + Z4 + XZ2 = XZ2.square(); // = X4^2 + Z4^2 + 2X4Z4 + XZ2 = &XZ2 - &Xsq_plus_Zsq; // = 2X4Z4 + let Xpow4 = v0.square(); // = X4^4 + let Zpow4 = v1.square(); // = Z4^4 + v0 = &Xpow4 + &Xpow4; // = 2X4^4 + v0 = &v0 - &Zpow4; // = 2X4^4 - Z4^4 + let a = &v0 + &v0; // = 2(2X4^4 - Z4^4) + let c = Zpow4; // = Z4^4 + + let codomain = ProjectiveCurveParameters{ A: a, C: c }; + let isogeny = FourIsogeny{ + Xsq_plus_Zsq, + Xsq_minus_Zsq, + XZ2, + Xpow4, + Zpow4 + }; + + (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')`. + // + // The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve + // parameters are returned by the compute_four_isogeny function used to construct + // phi. + pub fn eval(&self, xP: &ProjectivePoint) -> ProjectivePoint { + let phi = *self; + // We want to compute formula (7) of Costello-Longa-Naehrig, namely + // + // Xprime = (2*X_4*Z*Z_4 - (X_4^2 + Z_4^2)*X)*(X*X_4 - Z*Z_4)^2*X + // Zprime = (2*X*X_4*Z_4 - (X_4^2 + Z_4^2)*Z)*(X_4*Z - X*Z_4)^2*Z + // + // To do this we adapt the method in the MSR implementation, which computes + // + // X_Q = Xprime*( 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 ) + // Z_Q = Zprime*( 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 ) + // + let mut t0 = &xP.X * &phi.XZ2; // = 2*X*X_4*Z_4 + let mut t1 = &xP.Z * &phi.Xsq_plus_Zsq; // = (X_4^2 + Z_4^2)*Z + t0 = &t0 - &t1; // = -X_4^2*Z + 2*X*X_4*Z_4 - Z*Z_4^2 + t1 = &xP.Z * &phi.Xsq_minus_Zsq; // = (X_4^2 - Z_4^2)*Z + let mut t2 = (&t0 - &t1).square(); // = 4*(X_4*Z - X*Z_4)^2*X_4^2 + t0 = &t0 * &t1; + t0 = &t0 + &t0; + t0 = &t0 + &t0; // = 4*(2*X*X_4*Z_4 - (X_4^2 + Z_4^2)*Z)*(X_4^2 - Z_4^2)*Z + t1 = &t0 + &t2; // = 4*(X*X_4 - Z*Z_4)^2*Z_4^2 + t0 = &t0 * &t2; // = Zprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2 + let z = &t0 * &phi.Zpow4; // = Zprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 + t2 = &t2 * &phi.Zpow4; // = 4*(X_4*Z - X*Z_4)^2*X_4^2*Z_4^4 + t0 = &t1 * &phi.Xpow4; // = 4*(X*X_4 - Z*Z_4)^2*X_4^4*Z_4^2 + t0 = &t2 - &t0; // = -4*(X*X_4^2 - 2*X_4*Z*Z_4 + X*Z_4^2)*X*(X_4^2 - Z_4^2)*X_4^2*Z_4^2 + let x = &t1 * &t0; // = Xprime * 16*(X_4 + Z_4)*(X_4 - Z_4)*X_4^2*Z_4^4 + let xQ = ProjectivePoint{ X: x, Z: z }; + + xQ + } +} + +/// Represents a 4-isogeny phi. +// +// See compute_four_isogeny for details. +#[derive(Copy, Clone)] +pub struct FirstFourIsogeny { + pub A: ExtensionFieldElement, + pub C: ExtensionFieldElement, +} + +impl FirstFourIsogeny { + /// Compute the "first" four-isogeny from the given curve. + // + // See also compute_four_isogeny and Costello-Longa-Naehrig for more details. + pub fn compute_first_four_isogeny(domain: &ProjectiveCurveParameters) -> (ProjectiveCurveParameters, FirstFourIsogeny) { + let mut t0 = &domain.C + &domain.C; // = 2*C + let c = &domain.A - &t0; // = A - 2*C + let mut t1 = &t0 + &t0; // = 4*C + t1 = &t1 + &t0; // = 6*C + t0 = &t1 + &domain.A; // = A + 6*C + let a = &t0 + &t0; // = 2*(A + 6*C) + + let codomain = ProjectiveCurveParameters{ A: a, C: c }; + let isogeny = FirstFourIsogeny{ A: domain.A, C: domain.C }; + + (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')`. + // + // The output xQ = x(Q) is then a point on the curve E_(A':C'); the curve + // parameters are returned by the compute_first_four_isogeny function used to + // construct phi. + pub fn eval(&self, xP: &ProjectivePoint) -> ProjectivePoint { + let phi = *self; + let mut t0 = (&xP.X + &xP.Z).square(); // = (X+Z)^2 + let t2 = &xP.X * &xP.Z; // = X*Z + let mut t1 = &t2 + &t2; // = 2*X*Z + t1 = &t0 - &t1; // = X^2 + Z^2 + let mut x = &phi.A * &t2; // = A*X*Z + let t3 = &phi.C * &t1; // = C*(X^2 + Z^2) + x = &x + &t3; // = A*X*Z + C*(X^2 + Z^2) + x = &x * &t0; // = (X+Z)^2 * (A*X*Z + C*(X^2 + Z^2)) + t0 = (&xP.X - &xP.Z).square(); // = (X-Z)^2 + t0 = &t0 * &t2; // = X*Z*(X-Z)^2 + t1 = &phi.C + &phi.C; // = 2*C + t1 = &t1 - &phi.A; // = 2*C - A + let z = &t1 * &t0; // = (2*C - A)*X*Z*(X-Z)^2 + let xQ = ProjectivePoint{ X: x, Z: z }; + + xQ + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::sidh::fp::Fp751Element; + + // Test the first four-isogeny from the base curve E_0(F_{p^2}). + #[test] + fn first_four_isogeny_versus_sage() { + // sage: p = 2^372 * 3^239 - 1; Fp = GF(p) + // sage: R. = Fp[] + // sage: Fp2 = Fp.extension(x^2 + 1, 'i') + // sage: i = Fp2.gen() + // sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) + // sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) + // sage: x_PA = 11 + // sage: y_PA = -Fp(11^3 + 11).sqrt() + // sage: x_PB = 6 + // sage: y_PB = -Fp(6^3 + 6).sqrt() + // sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) + // sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) + // sage: def tau(P): + // ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) + // ....: + // sage: m_B = 3*randint(0,3^238) + // sage: m_A = 2*randint(0,2^371) + // sage: R_A = E0Fp2(P_A) + m_A*tau(P_A) + // sage: def y_recover(x, a): + // ....: return (x**3 + a*x**2 + x).sqrt() + // ....: + // sage: first_4_torsion_point = E0Fp2(1, y_recover(Fp2(1),0)) + // sage: sage_first_4_isogeny = E0Fp2.isogeny(first_4_torsion_point) + // sage: a = Fp2(0) + // sage: sage_isomorphism = sage_first_4_isogeny.codomain().isomorphism_to(EllipticCurve(Fp2, [0,(2*(a+6))/(a-2),0,1,0])) + // sage: isogenized_R_A = sage_isomorphism(sage_first_4_isogeny(R_A)) + // + let xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ + A: Fp751Element([0x2a95fce9, 0xa179cb7e, 0xa0a892c0, 0xbfd6a0f3, 0x250ab3f3, 0x8b2f0aa4, 0x4118732d, 0x2e7aa4dd, 0x93acbc2a, 0x627969e4, 0xc7b8cc83, 0x21a5b852, 0x586324f2, 0x26084278, 0x5aa947c0, 0x383be1aa, 0xb5c0183e, 0xc6558ecb, 0x6a52b035, 0xf1f19208, 0x5b865c1b, 0x4c58b75, 0xceea2d2c, 0x67b4]), + B: Fp751Element([0x797fecbf, 0xfceb02a2, 0x21f95e99, 0x3fee9e1d, 0x6024e166, 0xa1c4ce89, 0x54517358, 0xc09c0242, 0xb17b94e7, 0xf0255994, 0xb41ee894, 0xa4834359, 0xb7ebefbe, 0x9487f7d, 0xa0bf1f24, 0x3bbeeb34, 0x514c6a05, 0xfa7e5533, 0x46450a9a, 0x92b03281, 0xfada4c06, 0xfde71ca3, 0xf995c2bd, 0x3610]) + }); + + let sage_isogenized_xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ + A: Fp751Element([0x78da1e05, 0xff99e76f, 0xbb8d97c4, 0xdaa36bd2, 0xa409daf, 0xb4328cee, 0x80c5da3f, 0xc28b0999, 0xcfebb852, 0xf2d7cd15, 0xded6cdef, 0x1935103d, 0xde1429c3, 0xade81528, 0x90a64319, 0x6775b0fa, 0xee52485d, 0x25f89817, 0x848e697, 0x706e2d0, 0x216d65c0, 0xc4958ec4, 0x9681417f, 0xc51]), + B: Fp751Element([0xe60e1fb9, 0x742fe7dd, 0x466a456b, 0x801a3c78, 0x86f48c35, 0xa9f945b7, 0xb144348f, 0x20ce89e1, 0x7776217e, 0xf633970b, 0xb38976e5, 0x4c6077a9, 0x766c7825, 0x34a513fc, 0x59b9cd65, 0xacccba3, 0xf0fd0125, 0xd0ca8383, 0x7196287a, 0x7735043, 0x6d4ea21, 0x9fe1ad77, 0x129ee42d, 0x4d26]) + }); + + let curve_params = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; + + let (_, phi) = FirstFourIsogeny::compute_first_four_isogeny(&curve_params); + let isogenized_xR = phi.eval(&xR); + + assert!(sage_isogenized_xR.vartime_eq(&isogenized_xR), + "\nExpected\n{:?}\nfound\n{:?}", sage_isogenized_xR.to_affine(), isogenized_xR.to_affine()); + } + + #[test] + fn four_isogeny_versus_sage() { + // sage: p = 2^372 * 3^239 - 1; Fp = GF(p) + // *** Warning: increasing stack size to 2000000. + // *** Warning: increasing stack size to 4000000. + // sage: R. = Fp[] + // sage: Fp2 = Fp.extension(x^2 + 1, 'i') + // sage: i = Fp2.gen() + // sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) + // sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) + // sage: x_PA = 11 + // sage: y_PA = -Fp(11^3 + 11).sqrt() + // sage: x_PB = 6 + // sage: y_PB = -Fp(6^3 + 6).sqrt() + // sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) + // sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) + // sage: def tau(P): + // ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) + // ....: + // sage: m_B = 3*randint(0,3^238) + // sage: m_A = 2*randint(0,2^371) + // sage: R_A = E0Fp2(P_A) + m_A*tau(P_A) + // sage: def y_recover(x, a): + // ....: return (x**3 + a*x**2 + x).sqrt() + // ....: + // sage: first_4_torsion_point = E0Fp2(1, y_recover(Fp2(1),0)) + // sage: sage_first_4_isogeny = E0Fp2.isogeny(first_4_torsion_point) + // sage: a = Fp2(0) + // sage: E1A = EllipticCurve(Fp2, [0,(2*(a+6))/(a-2),0,1,0]) + // sage: sage_isomorphism = sage_first_4_isogeny.codomain().isomorphism_to(E1A) + // sage: isogenized_R_A = sage_isomorphism(sage_first_4_isogeny(R_A)) + // sage: P_4 = (2**(372-4))*isogenized_R_A + // sage: P_4._order = 4 #otherwise falls back to generic group methods for order + // sage: X4, Z4 = P_4.xy()[0], 1 + // sage: phi4 = EllipticCurveIsogeny(E1A, P_4, None, 4) + // sage: E2A_sage = phi4.codomain() # not in monty form + // sage: Aprime, Cprime = 2*(2*X4^4 - Z4^4), Z4^4 + // sage: E2A = EllipticCurve(Fp2, [0,Aprime/Cprime,0,1,0]) + // sage: sage_iso = E2A_sage.isomorphism_to(E2A) + // sage: isogenized2_R_A = sage_iso(phi4(isogenized_R_A)) + // + let xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ + A: Fp751Element([0x78da1e05, 0xff99e76f, 0xbb8d97c4, 0xdaa36bd2, 0xa409daf, 0xb4328cee, 0x80c5da3f, 0xc28b0999, 0xcfebb852, 0xf2d7cd15, 0xded6cdef, 0x1935103d, 0xde1429c3, 0xade81528, 0x90a64319, 0x6775b0fa, 0xee52485d, 0x25f89817, 0x848e697, 0x706e2d0, 0x216d65c0, 0xc4958ec4, 0x9681417f, 0xc51]), + B: Fp751Element([0xe60e1fb9, 0x742fe7dd, 0x466a456b, 0x801a3c78, 0x86f48c35, 0xa9f945b7, 0xb144348f, 0x20ce89e1, 0x7776217e, 0xf633970b, 0xb38976e5, 0x4c6077a9, 0x766c7825, 0x34a513fc, 0x59b9cd65, 0xacccba3, 0xf0fd0125, 0xd0ca8383, 0x7196287a, 0x7735043, 0x6d4ea21, 0x9fe1ad77, 0x129ee42d, 0x4d26]) + }); + + let xP4 = ProjectivePoint::from_affine(&ExtensionFieldElement{ + A: Fp751Element([0x13f3d5e7, 0x2afd75a9, 0x6f88c9ab, 0x2918fba0, 0xcb526f05, 0xa4ac4dc7, 0x1a607300, 0x2d19e939, 0x34091b54, 0x7a79e2b, 0xb42f1792, 0x3ad809dc, 0x8bd6402a, 0xd4617932, 0x1e2c4f3f, 0x1afa7354, 0xce9bdbd8, 0xf602d73a, 0x6bab7004, 0xd77ac58f, 0x6793b3b3, 0x4689d97f, 0xb00e42b7, 0x4f26]), + B: Fp751Element([0xafdcb890, 0x6cdf918d, 0xc29cfae2, 0x666f273c, 0x1ba618e2, 0xad00fcd3, 0xef2f6a33, 0x5fbcf62b, 0x318e5098, 0xf408bb88, 0x9453d175, 0x84ab9784, 0xcfb8e1ac, 0x501bbfcd, 0xe6b5542c, 0xf2370098, 0xf0f6bd32, 0xc7dc73f5, 0x6729d1cf, 0xdd76dcd8, 0x29996e4, 0xca22c905, 0xa9373de3, 0x5cf4]) + }); + + let sage_isogenized_xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ + A: Fp751Element([0xd0b7a01e, 0x111efd8b, 0x3789ca9b, 0x6ab75a4f, 0x8564cac4, 0x939dbe51, 0x601d0434, 0xf9eeaba1, 0x6edac998, 0x8d41f8ba, 0xfe9aa170, 0xfcd2557e, 0x98b7844, 0xb3c3549c, 0x6f81127c, 0x52874fef, 0xaa518bb3, 0xb2b9ac82, 0x30520a86, 0xee708202, 0x5efb184a, 0xd4012b7f, 0x4536329b, 0x573e]), + B: Fp751Element([0x1e932902, 0xa9995228, 0x71f2c7b1, 0x569a89a5, 0x46ba3f6b, 0x61501438, 0x41e91430, 0x11fd2044, 0x55c9b07b, 0x7f469bd, 0xde35b161, 0xb72db8b9, 0xa940512a, 0x455a9a37, 0xabaf906, 0xb0cff767, 0x583375fe, 0x18c785b7, 0x403c9148, 0x603ab9ca, 0x6e6c62c1, 0xab54ba3a, 0xd7d57c4f, 0x2726]) + }); + + let (_, phi) = FourIsogeny::compute_four_isogeny(&xP4); + let isogenized_xR = phi.eval(&xR); + + assert!(sage_isogenized_xR.vartime_eq(&isogenized_xR), + "\nExpected\n{:?}\nfound\n{:?}", sage_isogenized_xR.to_affine(), isogenized_xR.to_affine()); + } + + #[test] + fn three_isogeny_versus_sage() { + // sage: %colors Linux + // sage: p = 2^372 * 3^239 - 1; Fp = GF(p) + // *** Warning: increasing stack size to 2000000. + // *** Warning: increasing stack size to 4000000. + // sage: R. = Fp[] + // sage: Fp2 = Fp.extension(x^2 + 1, 'i') + // sage: i = Fp2.gen() + // sage: E0Fp = EllipticCurve(Fp, [0,0,0,1,0]) + // sage: E0Fp2 = EllipticCurve(Fp2, [0,0,0,1,0]) + // sage: x_PA = 11 + // sage: y_PA = -Fp(11^3 + 11).sqrt() + // sage: x_PB = 6 + // sage: y_PB = -Fp(6^3 + 6).sqrt() + // sage: P_A = 3^239 * E0Fp((x_PA,y_PA)) + // sage: P_B = 2^372 * E0Fp((x_PB,y_PB)) + // sage: def tau(P): + // ....: return E0Fp2( (-P.xy()[0], i*P.xy()[1])) + // ....: + // sage: m_B = 3*randint(0,3^238) + // sage: R_B = E0Fp2(P_B) + m_B*tau(P_B) + // sage: P_3 = (3^238)*R_B + // sage: def three_isog(P_3, P): + // ....: X3, Z3 = P_3.xy()[0], 1 + // ....: XP, ZP = P.xy()[0], 1 + // ....: x = (XP*(X3*XP - Z3*ZP)^2)/(ZP*(Z3*XP - X3*ZP)^2) + // ....: A3, C3 = (Z3^4 + 9*X3^2*(2*Z3^2 - 3*X3^2)), 4*X3*Z3^3 + // ....: cod = EllipticCurve(Fp2, [0,A3/C3,0,1,0]) + // ....: return cod.lift_x(x) + // ....: + // sage: isogenized_R_B = three_isog(P_3, R_B) + // + let xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ + A: Fp751Element([0x5cc9a3d7, 0xbd0737ed, 0x6517c101, 0x45ae6d47, 0x7364fdb2, 0x6f228e9e, 0x225b3dbd, 0xbba4871, 0xe5da1a07, 0x6299ccd2, 0xaf5f2d0e, 0x38488fe4, 0xa86e980c, 0xec23cae5, 0x3f1edffa, 0x26c804ba, 0x32df60e5, 0xfbbed819, 0x82ae9187, 0x7e00e9d1, 0x66d05f4b, 0xc7654abb, 0x567237b, 0x262d]), + B: Fp751Element([0xd0b2ac33, 0x3a3b5b6a, 0x179127d3, 0x246602b5, 0xad65077d, 0x502ae0e9, 0x37e1bf70, 0x10a3a372, 0x4dd05610, 0x4a1ab929, 0x30fe1fa6, 0xb0f3adac, 0x7faf70cb, 0x34199526, 0x39cf4ec1, 0xa14dd94d, 0xd1bf5568, 0xce4b7527, 0xed45c7e4, 0xe0410423, 0xb6425686, 0x38011809, 0x2472ebed, 0x28f5]) + }); + + let xP3 = ProjectivePoint::from_affine(&ExtensionFieldElement{ + A: Fp751Element([0x7b0788dc, 0x7bb7a4a0, 0x607b21b0, 0xdc36a3f6, 0xe74cf2f0, 0x4750e18e, 0xb7ab806, 0x464e319d, 0x4f758ff, 0xc25aa44c, 0xa46e0a68, 0x392e8521, 0x3eff37df, 0xfc4e76b6, 0x92e67dd8, 0x1f3566d8, 0x73295e65, 0xf8d2eb0f, 0xc470bccb, 0x457b13eb, 0xfef5be33, 0xfda1cc9e, 0x3d92cc02, 0x5dbf]), + 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]) + }); + + let sage_isogenized_xR = ProjectivePoint::from_affine(&ExtensionFieldElement{ + A: Fp751Element([0x5913c5b1, 0x286db7d7, 0x50189220, 0xcb2049ad, 0x765fa9f4, 0xccee90ef, 0x730e7d88, 0x65e52ce2, 0xbd0d06e7, 0xa6b6b553, 0x14591590, 0xb561ecec, 0x8c64d959, 0x17b7a66d, 0xcbe1461e, 0x77778ce, 0xc41a57ce, 0x9405c9c0, 0xe8ca7d3d, 0x8f6b4847, 0x7b366937, 0xf625eb98, 0x3590e345, 0x421b]), + B: Fp751Element([0x3e7d8d6, 0x566b8938, 0xd527e696, 0xe8c71a04, 0x7bf5eb51, 0x5a1d8f8, 0xe098724f, 0x42ae08a, 0xaf40ca2e, 0x4ee3d7c7, 0x67bb10a7, 0xd9f9ab90, 0xedd6328c, 0xecd53d69, 0x2dea107d, 0xa581e920, 0x8ecf9257, 0x8bcdfb6c, 0x5cbcf2af, 0xe7cbbc2e, 0x1f0e53e, 0x5f031a87, 0x2d93e3cb, 0x1831]) + }); + + let (_, phi) = ThreeIsogeny::compute_three_isogeny(&xP3); + let isogenized_xR = phi.eval(&xR); + + assert!(sage_isogenized_xR.vartime_eq(&isogenized_xR), + "\nExpected\n{:?}\nfound\n{:?}", sage_isogenized_xR.to_affine(), isogenized_xR.to_affine()); + } +} diff --git a/zerotier-core-crypto/src/sidh/mod.rs b/zerotier-core-crypto/src/sidh/mod.rs new file mode 100644 index 000000000..0f170cfe2 --- /dev/null +++ b/zerotier-core-crypto/src/sidh/mod.rs @@ -0,0 +1,26 @@ +// This file is part of sidh-rs. +// Copyright (c) 2017 Erkan Tairi +// See LICENSE for licensing information. +// +// Author: +// - Erkan Tairi +// + +#[allow(non_snake_case)] +#[allow(unused_variables)] +#[allow(unused)] +pub(crate) mod field; +#[allow(non_snake_case)] +#[allow(unused)] +pub(crate) mod curve; +#[allow(non_snake_case)] +pub(crate) mod isogeny; +#[allow(non_snake_case)] +#[allow(unused)] +#[macro_use] +pub(crate) mod fp; + +pub mod constants; +#[allow(unused_assignments)] +#[allow(non_snake_case)] +pub mod sidh; diff --git a/zerotier-core-crypto/src/sidh/sidh.rs b/zerotier-core-crypto/src/sidh/sidh.rs new file mode 100644 index 000000000..8e4f65a69 --- /dev/null +++ b/zerotier-core-crypto/src/sidh/sidh.rs @@ -0,0 +1,674 @@ +// This file is part of sidh-rs. +// Copyright (c) 2017 Erkan Tairi +// See LICENSE for licensing information. +// +// Author: +// - 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::*; +#[allow(unused_imports)] +use crate::random::SecureRandom; + +use heapless::Vec; + +use std::fmt::Debug; +use std::ops::Neg; + +use rand_core::{RngCore, CryptoRng}; + +#[cfg(test)] +use quickcheck::{Arbitrary, Gen, QuickCheck}; + +/// The secret key size, in bytes. +pub const SECRET_KEY_SIZE: usize = 48; +/// The public key size, in bytes. +pub const PUBLIC_KEY_SIZE: usize = 564; +/// The shared secret size, in bytes. +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, + 6, 7, 8, 8, 9, 9, 9, 9, 9, 9, 9, 12, 11, 12, 12, 13, 14, 15, 16, 16, 16, 16, + 16, 16, 17, 17, 18, 18, 17, 21, 17, 18, 21, 20, 21, 21, 21, 21, 21, 22, 25, 25, + 25, 26, 27, 28, 28, 29, 30, 31, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33, 35, 36, + 36, 33, 36, 35, 36, 36, 35, 36, 36, 37, 38, 38, 39, 40, 41, 42, 38, 39, 40, 41, + 42, 40, 46, 42, 43, 46, 46, 46, 46, 48, 48, 48, 48, 49, 49, 48, 53, 54, 51, 52, + 53, 54, 55, 56, 57, 58, 59, 59, 60, 62, 62, 63, 64, 64, 64, 64, 64, 64, 64, 64, + 65, 65, 65, 65, 65, 66, 67, 65, 66, 67, 66, 69, 70, 66, 67, 66, 69, 70, 69, 70, + 70, 71, 72, 71, 72, 72, 74, 74, 75, 72, 72, 74, 74, 75, 72, 72, 74, 75, 75, 72, + 72, 74, 75, 75, 77, 77, 79, 80, 80, 82]; + + +const MAX_BOB: usize = 239; +/// Bob's isogeny strategy. +pub const BOB_ISOGENY_STRATEGY: [u8; MAX_BOB] = [0, 1, 1, 2, 2, 2, 3, 3, 4, 4, 4, 5, 5, 5, 6, + 7, 8, 8, 8, 8, 9, 9, 9, 9, 9, 10, 12, 12, 12, 12, 12, 12, 13, 14, 14, 15, 16, + 16, 16, 16, 16, 17, 16, 16, 17, 19, 19, 20, 21, 22, 22, 22, 22, 22, 22, 22, 22, + 22, 22, 24, 24, 25, 27, 27, 28, 28, 29, 28, 29, 28, 28, 28, 30, 28, 28, 28, 29, + 30, 33, 33, 33, 33, 34, 35, 37, 37, 37, 37, 38, 38, 37, 38, 38, 38, 38, 38, 39, + 43, 38, 38, 38, 38, 43, 40, 41, 42, 43, 48, 45, 46, 47, 47, 48, 49, 49, 49, 50, + 51, 50, 49, 49, 49, 49, 51, 49, 53, 50, 51, 50, 51, 51, 51, 52, 55, 55, 55, 56, + 56, 56, 56, 56, 58, 58, 61, 61, 61, 63, 63, 63, 64, 65, 65, 65, 65, 66, 66, 65, + 65, 66, 66, 66, 66, 66, 66, 66, 71, 66, 73, 66, 66, 71, 66, 73, 66, 66, 71, 66, + 73, 68, 68, 71, 71, 73, 73, 73, 75, 75, 78, 78, 78, 80, 80, 80, 81, 81, 82, 83, + 84, 85, 86, 86, 86, 86, 86, 87, 86, 88, 86, 86, 86, 86, 88, 86, 88, 86, 86, 86, + 88, 88, 86, 86, 86, 93, 90, 90, 92, 92, 92, 93, 93, 93, 93, 93, 97, 97, 97, 97, + 97, 97]; + +/// Alice's public key. +#[derive(Copy, Clone)] +pub struct SIDHPublicKeyAlice { + pub affine_xP : ExtensionFieldElement, + pub affine_xQ : ExtensionFieldElement, + pub affine_xQmP: ExtensionFieldElement, +} + +impl SIDHPublicKeyAlice { + /// Read a public key from a byte slice. The input must be at least 564 bytes long. + 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]); + let affine_xQ = ExtensionFieldElement::from_bytes(&bytes[188..376]); + let affine_xQmP = ExtensionFieldElement::from_bytes(&bytes[376..564]); + SIDHPublicKeyAlice{ affine_xP, affine_xQ, affine_xQmP } + } + /// Write a public key to a byte slice. The output will be 564 bytes long. + pub fn to_bytes(&self) -> [u8; 564] { + let mut bytes = [0u8; 564]; + bytes[0..188].clone_from_slice(&self.affine_xP.to_bytes()); + bytes[188..376].clone_from_slice(&self.affine_xQ.to_bytes()); + bytes[376..564].clone_from_slice(&self.affine_xQmP.to_bytes()); + bytes + } +} + +/// Bob's public key. +#[derive(Copy, Clone)] +pub struct SIDHPublicKeyBob { + pub affine_xP : ExtensionFieldElement, + pub affine_xQ : ExtensionFieldElement, + pub affine_xQmP: ExtensionFieldElement, +} + +impl SIDHPublicKeyBob { + /// Read a public key from a byte slice. The input must be at least 564 bytes long. + 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]); + let affine_xQ = ExtensionFieldElement::from_bytes(&bytes[188..376]); + let affine_xQmP = ExtensionFieldElement::from_bytes(&bytes[376..564]); + SIDHPublicKeyBob{ affine_xP, affine_xQ, affine_xQmP } + } + /// Write a public key to a byte slice. The output will be 564 bytes long. + pub fn to_bytes(&self) -> [u8; 564] { + let mut bytes = [0u8; 564]; + bytes[0..188].clone_from_slice(&self.affine_xP.to_bytes()); + bytes[188..376].clone_from_slice(&self.affine_xQ.to_bytes()); + bytes[376..564].clone_from_slice(&self.affine_xQmP.to_bytes()); + bytes + } +} + +/// Alice's secret key. +#[derive(Copy, Clone)] +pub struct SIDHSecretKeyAlice { + pub scalar: [u8; SECRET_KEY_SIZE], +} + +impl Debug for SIDHSecretKeyAlice { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "SIDHSecretKeyAlice(scalar: {:?})", &self.scalar[..]) + } +} + +#[cfg(test)] +impl Arbitrary for SIDHSecretKeyAlice { + fn arbitrary(_g: &mut Gen) -> SIDHSecretKeyAlice { + let mut rng = SecureRandom::get(); + let (_, alice_secret_key) = generate_alice_keypair(&mut rng); + alice_secret_key + } +} + +impl SIDHSecretKeyAlice { + /// Compute the corresponding public key for the given secret key. + 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); // + xQ.X = (&xQ.X).neg(); // = (-x_P : 1) = x(Q_B) + let mut xQmP = ProjectivePoint::distort_and_difference(&AFFINE_X_PB); // = x(Q_B - P_B) + + let mut xR = ProjectivePoint::secret_point(&AFFINE_X_PA, &AFFINE_Y_PA, &self.scalar[..]); + + // Starting curve has a = 0, so (A:C) = (0,1). + let current_curve = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; + let (mut current_curve, firstPhi) = FirstFourIsogeny::compute_first_four_isogeny(¤t_curve); + + xP = firstPhi.eval(&xP); + xQ = firstPhi.eval(&xQ); + xQmP = firstPhi.eval(&xQmP); + xR = firstPhi.eval(&xR); + + let mut points: Vec = Vec::new(); + let mut indices: Vec = Vec::new(); + let mut i: usize = 0; + let mut phi: FourIsogeny; + for j in 1..MAX_ALICE { + while i < MAX_ALICE-j { + points.push(xR).unwrap(); + indices.push(i).unwrap(); + let k = ALICE_ISOGENY_STRATEGY[MAX_ALICE-i-j]; + xR = xR.pow2k(¤t_curve, (2*k) as u32); + i = i + k as usize; + } + assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; + + for k in 0..points.len() { + points[k] = phi.eval(&points[k]); + } + + xP = phi.eval(&xP); + xQ = phi.eval(&xQ); + xQmP = phi.eval(&xQmP); + + // Pop xR from pointsm and i from indices. + xR = points.pop().unwrap(); + i = indices.pop().unwrap(); + } + + assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; + + xP = phi.eval(&xP); + xQ = phi.eval(&xQ); + xQmP = phi.eval(&xQmP); + + let (invZP, invZQ, invZQmP) = ExtensionFieldElement::batch3_inv(&xP.Z, &xQ.Z, &xQmP.Z); + let affine_xP = &xP.X * &invZP; + let affine_xQ = &xQ.X * &invZQ; + let affine_xQmP = &xQmP.X * &invZQmP; + + 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. + 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); + let xQ = ProjectivePoint::from_affine(&bob_public.affine_xQ); + let xQmP = ProjectivePoint::from_affine(&bob_public.affine_xQmP); + let mut xR = ProjectivePoint::right_to_left_ladder(&xP, &xQ, &xQmP, ¤t_curve, &self.scalar[..]); + + let (mut current_curve, firstPhi) = FirstFourIsogeny::compute_first_four_isogeny(¤t_curve); + xR = firstPhi.eval(&xR); + + let mut points: Vec = Vec::new(); + let mut indices: Vec = Vec::new(); + let mut i: usize = 0; + let mut phi: FourIsogeny; + for j in 1..MAX_ALICE { + while i < MAX_ALICE-j { + points.push(xR).unwrap(); + indices.push(i).unwrap(); + let k = ALICE_ISOGENY_STRATEGY[MAX_ALICE-i-j]; + xR = xR.pow2k(¤t_curve, (2*k) as u32); + i = i + k as usize; + } + assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; + + for k in 0..points.len() { + points[k] = phi.eval(&points[k]); + } + + // Pop xR from pointsm and i from indices. + xR = points.pop().unwrap(); + i = indices.pop().unwrap(); + } + + assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; + + let j_inv = current_curve.j_invariant(); + let shared_secret = j_inv.to_bytes(); + shared_secret + } +} + +/// Bob's secret key. +#[derive(Copy, Clone)] +pub struct SIDHSecretKeyBob { + pub scalar: [u8; SECRET_KEY_SIZE], +} + +impl Debug for SIDHSecretKeyBob { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "SIDHSecretKeyBob(scalar: {:?})", &self.scalar[..]) + } +} + +#[cfg(test)] +impl Arbitrary for SIDHSecretKeyBob { + fn arbitrary(_g: &mut Gen) -> SIDHSecretKeyBob { + let mut rng = SecureRandom::get(); + let (_, bob_secret_key) = generate_bob_keypair(&mut rng); + bob_secret_key + } +} + +impl SIDHSecretKeyBob { + /// Compute the public key corresponding to the secret key. + 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); // + xQ.X = (&xQ.X).neg(); // = (-x_P : 1) = x(Q_A) + let mut xQmP = ProjectivePoint::distort_and_difference(&AFFINE_X_PA); // = x(Q_B - P_B) + + let mut xR = ProjectivePoint::secret_point(&AFFINE_X_PB, &AFFINE_Y_PB, &self.scalar[..]); + + // Starting curve has a = 0, so (A:C) = (0,1). + let mut current_curve = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; + + let mut points: Vec = Vec::new(); + let mut indices: Vec = Vec::new(); + let mut i: usize = 0; + let mut phi: ThreeIsogeny; + for j in 1..MAX_BOB { + while i < MAX_BOB-j { + points.push(xR).unwrap(); + indices.push(i).unwrap(); + let k = BOB_ISOGENY_STRATEGY[MAX_BOB-i-j]; + xR = xR.pow3k(¤t_curve, k as u32); + i = i + k as usize; + } + assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; + + for k in 0..points.len() { + points[k] = phi.eval(&points[k]); + } + + xP = phi.eval(&xP); + xQ = phi.eval(&xQ); + xQmP = phi.eval(&xQmP); + + // Pop xR from points and i from indices. + xR = points.pop().unwrap(); + i = indices.pop().unwrap(); + } + + assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; + + xP = phi.eval(&xP); + xQ = phi.eval(&xQ); + xQmP = phi.eval(&xQmP); + + let (invZP, invZQ, invZQmP) = ExtensionFieldElement::batch3_inv(&xP.Z, &xQ.Z, &xQmP.Z); + let affine_xP = &xP.X * &invZP; + let affine_xQ = &xQ.X * &invZQ; + let affine_xQmP = &xQmP.X * &invZQmP; + + 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. + 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); + let xQ = ProjectivePoint::from_affine(&alice_public.affine_xQ); + let xQmP = ProjectivePoint::from_affine(&alice_public.affine_xQmP); + let mut xR = ProjectivePoint::right_to_left_ladder(&xP, &xQ, &xQmP, ¤t_curve, &self.scalar[..]); + + let mut points: Vec = Vec::new(); + let mut indices: Vec = Vec::new(); + let mut i: usize = 0; + let mut phi: ThreeIsogeny; + for j in 1..MAX_BOB { + while i < MAX_BOB-j { + points.push(xR).unwrap(); + indices.push(i).unwrap(); + let k = BOB_ISOGENY_STRATEGY[MAX_BOB-i-j]; + xR = xR.pow3k(¤t_curve, k as u32); + i = i + k as usize; + } + assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; + + for k in 0..points.len() { + points[k] = phi.eval(&points[k]); + } + + // Pop xR from points and i from indices. + xR = points.pop().unwrap(); + i = indices.pop().unwrap(); + } + + assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; + + let j_inv = current_curve.j_invariant(); + let shared_secret = j_inv.to_bytes(); + shared_secret + } +} + +/// Generate a keypair for "Alice". Note that because this library does not +/// implement SIDH validation, each keypair should be used for at most one +/// shared secret computation. +pub fn generate_alice_keypair(rng: &mut R) -> (SIDHPublicKeyAlice, SIDHSecretKeyAlice) { + let mut scalar = [0u8; SECRET_KEY_SIZE]; + rng.fill_bytes(&mut scalar[..]); + + // Bit-twiddle to ensure scalar is in 2*[0,2^371): + scalar[47] = 0; + scalar[46] &= 15; // Clear high bits, so scalar < 2^372. + scalar[0] &= 254; // Clear low bit, so scalar is even. + + // We actually want scalar in 2*(0,2^371), but the above procedure + // generates 0 with probability 2^(-371), which isn't worth checking + // for. + let secret_key = SIDHSecretKeyAlice{ scalar }; + let public_key = secret_key.public_key(); + + (public_key, secret_key) +} + +/// Generate a keypair for "Bob". Note that because this library does not +/// implement SIDH validation, each keypair should be used for at most one +/// shared secret computation. +pub fn generate_bob_keypair(rng: &mut R) -> (SIDHPublicKeyBob, SIDHSecretKeyBob) { + let mut scalar = [0u8; SECRET_KEY_SIZE]; + // Perform rejection sampling to obtain a random value in [0,3^238]: + let mut ok: u32 = 1; + for _ in 0..102 { + rng.fill_bytes(&mut scalar[..]); + // Mask the high bits to obtain a uniform value in [0,2^378): + scalar[47] &= 3; + // Accept if scalar < 3^238 (this happens with probability ~0.5828). + checklt238(&scalar, &mut ok); + if ok == 0 { break; } + } + // ok is nonzero if all 102 trials failed. + // This happens with probability 0.41719...^102 < 2^(-128), i.e., never. + if ok != 0 { panic!("All 102 trials failed!"); } + + // Multiply by 3 to get a scalar in 3*[0,3^238): + mulby3(&mut scalar); + + // We actually want scalar in 2*(0,2^371), but the above procedure + // generates 0 with probability 3^(-238), which isn't worth checking + // for. + let secret_key = SIDHSecretKeyBob{ scalar }; + let public_key = secret_key.public_key(); + + (public_key, secret_key) +} + + +#[cfg(test)] +mod test { + use super::*; + + // 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. + 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); // + xQ.X = (&xQ.X).neg(); // = (-x_P : 1) = x(Q_B) + let mut xQmP = ProjectivePoint::distort_and_difference(&AFFINE_X_PB); // = x(Q_B - P_B) + + let mut xR = ProjectivePoint::secret_point(&AFFINE_X_PA, &AFFINE_Y_PA, &secret_key.scalar[..]); + // Starting curve has a = 0, so (A:C) = (0,1). + let current_curve = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; + + let (mut current_curve, firstPhi) = FirstFourIsogeny::compute_first_four_isogeny(¤t_curve); + + xP = firstPhi.eval(&xP); + xQ = firstPhi.eval(&xQ); + xQmP = firstPhi.eval(&xQmP); + xR = firstPhi.eval(&xR); + + let mut phi: FourIsogeny; + // rev() makes the loop go from 368 down to 0. + for e in (0..(372 - 4 + 1)).rev().step_by(2) { + let xS = xR.pow2k(¤t_curve, e as u32); + assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xS)}; + + xR = phi.eval(&xR); + xP = phi.eval(&xP); + xQ = phi.eval(&xQ); + xQmP = phi.eval(&xQmP); + } + + let (invZP, invZQ, invZQmP) = ExtensionFieldElement::batch3_inv(&xP.Z, &xQ.Z, &xQmP.Z); + let affine_xP = &xP.X * &invZP; + let affine_xQ = &xQ.X * &invZQ; + let affine_xQmP = &xQmP.X * &invZQmP; + + 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. + 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); // + xQ.X = (&xQ.X).neg(); // = (-x_P : 1) = x(Q_A) + let mut xQmP = ProjectivePoint::distort_and_difference(&AFFINE_X_PA); // = x(Q_B - P_B) + + let mut xR = ProjectivePoint::secret_point(&AFFINE_X_PB, &AFFINE_Y_PB, &secret_key.scalar[..]); + // Starting curve has a = 0, so (A:C) = (0,1). + let mut current_curve = ProjectiveCurveParameters{ A: ExtensionFieldElement::zero(), C: ExtensionFieldElement::one() }; + + let mut phi: ThreeIsogeny; + // rev() makes the loop go from 238 down to 0. + for e in (0..MAX_BOB).rev() { + let xS = xR.pow3k(¤t_curve, e as u32); + assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xS)}; + + xR = phi.eval(&xR); + xP = phi.eval(&xP); + xQ = phi.eval(&xQ); + xQmP = phi.eval(&xQmP); + } + + let (invZP, invZQ, invZQmP) = ExtensionFieldElement::batch3_inv(&xP.Z, &xQ.Z, &xQmP.Z); + let affine_xP = &xP.X * &invZP; + let affine_xQ = &xQ.X * &invZQ; + let affine_xQmP = &xQmP.X * &invZQmP; + + 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. + 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); + let xQ = ProjectivePoint::from_affine(&bob_public.affine_xQ); + let xQmP = ProjectivePoint::from_affine(&bob_public.affine_xQmP); + + let mut xR = ProjectivePoint::three_point_ladder(&xP, &xQ, &xQmP, ¤t_curve, &alice_secret.scalar[..]); + + let (mut current_curve, firstPhi) = FirstFourIsogeny::compute_first_four_isogeny(¤t_curve); + xR = firstPhi.eval(&xR); + + let mut phi: FourIsogeny; + // rev() makes the loop go from 368 down to 2. + for e in (2..(372 - 4 + 1)).rev().step_by(2) { + let xS = xR.pow2k(¤t_curve, e as u32); + assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xS)}; + + xR = phi.eval(&xR); + } + + assign!{(current_curve, phi) = FourIsogeny::compute_four_isogeny(&xR)}; + + let j_inv = current_curve.j_invariant(); + 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. + 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); + let xQ = ProjectivePoint::from_affine(&alice_public.affine_xQ); + let xQmP = ProjectivePoint::from_affine(&alice_public.affine_xQmP); + + let mut xR = ProjectivePoint::three_point_ladder(&xP, &xQ, &xQmP, ¤t_curve, &bob_secret.scalar[..]); + + let mut phi: ThreeIsogeny; + // rev() makes the loop go from 239 down to 1. + for e in (1..MAX_BOB).rev() { + let xS = xR.pow3k(¤t_curve, e as u32); + assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xS)}; + + xR = phi.eval(&xR); + } + + assign!{(current_curve, phi) = ThreeIsogeny::compute_three_isogeny(&xR)}; + + let j_inv = current_curve.j_invariant(); + let shared_secret = j_inv.to_bytes(); + shared_secret + } + + #[test] + fn multiply_by_three() { + // sage: repr((3^238 -1).digits(256)) + let mut three238_minus1: [u8; 48] = [248, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2]; + // sage: repr((3*(3^238 -1)).digits(256)) + let three_times_three238_minus1: [u8; 48] = [232, 142, 138, 135, 159, 84, 104, 201, 62, 110, 199, 124, 63, 161, 177, 89, 169, 109, 135, 190, 110, 125, 134, 233, 132, 128, 116, 37, 203, 69, 80, 43, 86, 104, 198, 173, 123, 249, 9, 41, 225, 192, 113, 31, 84, 93, 254, 6]; + + mulby3(&mut three238_minus1); + + assert!(three238_minus1.iter().zip(three_times_three238_minus1.iter()).all(|(a, b)| a == b), + "\nExpected\n{:?}\nfound\n{:?}", &three_times_three238_minus1[..], &three238_minus1[..]); + } + + #[test] + fn check_less_than_three238() { + let three238_minus1: [u8; 48] = [248, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2]; + let three238: [u8; 48] = [249, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2]; + let three238_plus1: [u8; 48] = [250, 132, 131, 130, 138, 113, 205, 237, 20, 122, 66, 212, 191, 53, 59, 115, 56, 207, 215, 148, 207, 41, 130, 248, 214, 42, 124, 12, 153, 108, 197, 99, 199, 34, 66, 143, 126, 168, 88, 184, 245, 234, 37, 181, 198, 201, 84, 2]; + + let mut result: u32 = 57; + + checklt238(&three238_minus1, &mut result); + assert_eq!(result, 0, "\nExpected 0, got {}", result); + + checklt238(&three238, &mut result); + assert_ne!(result, 0, "\nExpected nonzero, got {}", result); + + checklt238(&three238_plus1, &mut result); + assert_ne!(result, 0, "\nExpected nonzero, got {}", result); + } + + #[test] + fn ephemeral_shared_secret() { + fn shared_secrets_match(alice_secret: SIDHSecretKeyAlice, bob_secret: SIDHSecretKeyBob) -> bool { + let alice_public = alice_secret.public_key(); + let bob_public = bob_secret.public_key(); + + let alice_shared_secret = alice_secret.shared_secret(&bob_public); + let bob_shared_secret = bob_secret.shared_secret(&alice_public); + + alice_shared_secret.iter().zip(bob_shared_secret.iter()).all(|(a, b)| a == b) + } + QuickCheck::new().max_tests(8).quickcheck(shared_secrets_match as fn(SIDHSecretKeyAlice, SIDHSecretKeyBob) -> bool); + } + + #[test] + fn alice_keygen_fast_vs_slow() { + // m_A = 2*randint(0,2^371) + let m_A: [u8; 48] = [248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0]; + + let alice_secret_key = SIDHSecretKeyAlice{ scalar: m_A }; + let fast_pubkey = alice_secret_key.public_key(); + let slow_pubkey = alice_keygen_slow(&alice_secret_key); + + assert!(fast_pubkey.affine_xP.vartime_eq(&slow_pubkey.affine_xP), + "\nExpected affine_xP = {:?}\nfound {:?}", fast_pubkey.affine_xP, slow_pubkey.affine_xP); + assert!(fast_pubkey.affine_xQ.vartime_eq(&slow_pubkey.affine_xQ), + "\nExpected affine_xQ = {:?}\nfound {:?}", fast_pubkey.affine_xQ, slow_pubkey.affine_xQ); + assert!(fast_pubkey.affine_xQmP.vartime_eq(&slow_pubkey.affine_xQmP), + "\nExpected affine_xQmP = {:?}\nfound {:?}", fast_pubkey.affine_xQmP, slow_pubkey.affine_xQmP); + } + + #[test] + fn bob_keygen_fast_vs_slow() { + // m_B = 3*randint(0,3^238) + let m_B: [u8; 48] = [246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0]; + + let bob_secret_key = SIDHSecretKeyBob{ scalar: m_B }; + let fast_pubkey = bob_secret_key.public_key(); + let slow_pubkey = bob_keygen_slow(&bob_secret_key); + + assert!(fast_pubkey.affine_xP.vartime_eq(&slow_pubkey.affine_xP), + "\nExpected affine_xP = {:?}\nfound {:?}", fast_pubkey.affine_xP, slow_pubkey.affine_xP); + assert!(fast_pubkey.affine_xQ.vartime_eq(&slow_pubkey.affine_xQ), + "\nExpected affine_xQ = {:?}\nfound {:?}", fast_pubkey.affine_xQ, slow_pubkey.affine_xQ); + assert!(fast_pubkey.affine_xQmP.vartime_eq(&slow_pubkey.affine_xQmP), + "\nExpected affine_xQmP = {:?}\nfound {:?}", fast_pubkey.affine_xQmP, slow_pubkey.affine_xQmP); + } + + #[test] + fn shared_secret() { + // m_A = 2*randint(0,2^371) + let m_A: [u8; 48] = [248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0]; + // m_B = 3*randint(0,3^238) + let m_B: [u8; 48] = [246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0]; + + let alice_secret = SIDHSecretKeyAlice{ scalar: m_A }; + let bob_secret = SIDHSecretKeyBob{ scalar: m_B }; + + let alice_public = alice_secret.public_key(); + let bob_public = bob_secret.public_key(); + + let alice_shared_secret_slow = alice_shared_secret_slow(&bob_public, &alice_secret); + let alice_shared_secret_fast = alice_secret.shared_secret(&bob_public); + let bob_shared_secret_slow = bob_shared_secret_slow(&alice_public, &bob_secret); + let bob_shared_secret_fast = bob_secret.shared_secret(&alice_public); + + assert!(alice_shared_secret_fast.iter().zip(bob_shared_secret_fast.iter()).all(|(a, b)| a == b), + "\nShared secret (fast) mismatch: Alice has {:?}\nBob has {:?}", &alice_shared_secret_fast[..], &bob_shared_secret_fast[..]); + assert!(alice_shared_secret_slow.iter().zip(bob_shared_secret_slow.iter()).all(|(a, b)| a == b), + "\nShared secret (slow) mismatch: Alice has {:?}\nBob has {:?}", &alice_shared_secret_slow[..], &bob_shared_secret_slow[..]); + assert!(alice_shared_secret_slow.iter().zip(bob_shared_secret_fast.iter()).all(|(a, b)| a == b), + "\nShared secret mismatch: Alice (slow) has {:?}\nBob (fast) has {:?}", &alice_shared_secret_slow[..], &bob_shared_secret_fast[..]); + } + + #[test] + fn secret_point() { + // m_A = 2*randint(0,2^371) + let m_A: [u8; 48] = [248, 31, 9, 39, 165, 125, 79, 135, 70, 97, 87, 231, 221, 204, 245, 38, 150, 198, 187, 184, 199, 148, 156, 18, 137, 71, 248, 83, 111, 170, 138, 61, 112, 25, 188, 197, 132, 151, 1, 0, 207, 178, 24, 72, 171, 22, 11, 0]; + // m_B = 3*randint(0,3^238) + let m_B: [u8; 48] = [246, 217, 158, 190, 100, 227, 224, 181, 171, 32, 120, 72, 92, 115, 113, 62, 103, 57, 71, 252, 166, 121, 126, 201, 55, 99, 213, 234, 243, 228, 171, 68, 9, 239, 214, 37, 255, 242, 217, 180, 25, 54, 242, 61, 101, 245, 78, 0]; + + let xR_A = ProjectivePoint::secret_point(&AFFINE_X_PA, &AFFINE_Y_PA, &m_A[..]); + let xR_B = ProjectivePoint::secret_point(&AFFINE_X_PB, &AFFINE_Y_PB, &m_B[..]); + + let sage_affine_xR_A = ExtensionFieldElement{ + A: Fp751Element([0x2103d089, 0x29f1dff1, 0x955e0d87, 0x7409b9bf, 0x1cca7288, 0xe812441c, 0xefba55f9, 0xc32b8b13, 0x696d83da, 0xc3b76a80, 0x3a3dc373, 0x185dd4f9, 0x115b6717, 0xfc07c1a9, 0x3b5c4254, 0x39bfcdd6, 0x1d41efd8, 0xc4d097d5, 0x389b21c7, 0x4f893494, 0x1d3d0446, 0x37343321, 0x5ccc3d22, 0x53c3]), + B: Fp751Element([0x33e40815, 0x722e718f, 0xdf715667, 0x8c5fc0f, 0xbbe8c74c, 0x850fd292, 0xfcbf5d3, 0x212938a6, 0xd58dc6e7, 0xfdb2a099, 0x63c9c205, 0x232f83ab, 0xa5543f5e, 0x23eda62f, 0x55d9d04f, 0x49b57588, 0x42ef25d1, 0x6b455e66, 0x37470202, 0x96511625, 0xf2e96ff0, 0xfeced582, 0xe0c0dea8, 0x33a9]) + }; + + let sage_affine_xR_B = ExtensionFieldElement{ + A: Fp751Element([0x6e8499f5, 0xdd4e6607, 0x907519da, 0xe7efddc6, 0xb337108c, 0xe31f9955, 0x79ffc5e1, 0x8e558c54, 0xd776bfc2, 0xfee963ea, 0x5846bf15, 0x33aa04c3, 0x23617a0d, 0xab77d91b, 0x746070e2, 0xbdd70948, 0xc277e942, 0x66f71291, 0x2f901fce, 0x187c39db, 0xd5d32aa2, 0x69262987, 0xb40057dc, 0xe1d]), + B: Fp751Element([0xcfd5c167, 0xd1b766ab, 0xc8a382fa, 0x4591059d, 0x736c223d, 0x1ddf9490, 0xbdf2b3dd, 0xc96db091, 0xc292f502, 0x7b8b9c3d, 0x5e4d3e33, 0xe5b18ad8, 0x6664b931, 0xc3f3479b, 0x299e21e6, 0xa4f17865, 0x32fa1c6e, 0x3f7ef5b3, 0xdab06119, 0x875bedb5, 0xa2e23b93, 0x9b5a06e, 0x8296fb26, 0x43d4]) + }; + + let affine_xR_A = xR_A.to_affine(); + assert!(sage_affine_xR_A.vartime_eq(&affine_xR_A), + "\nExpected\n{:?}\nfound\n{:?}", sage_affine_xR_A, affine_xR_A); + + let affine_xR_B = xR_B.to_affine(); + assert!(sage_affine_xR_B.vartime_eq(&affine_xR_B), + "\nExpected\n{:?}\nfound\n{:?}", sage_affine_xR_B, affine_xR_B); + } +} \ No newline at end of file diff --git a/zerotier-network-hypervisor/src/lib.rs b/zerotier-network-hypervisor/src/lib.rs index f77882b15..c8d6be57f 100644 --- a/zerotier-network-hypervisor/src/lib.rs +++ b/zerotier-network-hypervisor/src/lib.rs @@ -14,6 +14,17 @@ pub mod defaults; mod node; +/// Standard packet buffer type including pool container. +pub type PacketBuffer = crate::util::pool::Pooled, crate::vl1::buffer::PooledBufferFactory<{ crate::vl1::protocol::PACKET_SIZE_MAX }>>; + +/// Factory type to supply to a new PacketBufferPool. +pub type PacketBufferFactory = crate::vl1::buffer::PooledBufferFactory<{ crate::vl1::protocol::PACKET_SIZE_MAX }>; + +/// Source for instances of PacketBuffer +pub type PacketBufferPool = crate::util::pool::Pool, crate::vl1::buffer::PacketBufferFactory>; + +pub use node::{CallerInterface, Node}; + pub const VERSION_MAJOR: u8 = 1; pub const VERSION_MINOR: u8 = 99; pub const VERSION_REVISION: u8 = 1; diff --git a/zerotier-network-hypervisor/src/node.rs b/zerotier-network-hypervisor/src/node.rs index 6d2a07cc3..befe65906 100644 --- a/zerotier-network-hypervisor/src/node.rs +++ b/zerotier-network-hypervisor/src/node.rs @@ -9,9 +9,11 @@ use std::sync::Arc; use std::time::Duration; -use crate::vl1::node::VL1CallerInterface; use crate::error::InvalidParameterError; -use crate::vl1::{PacketBuffer, PacketBufferPool, Address, Identity, Endpoint}; +use crate::vl1::{Address, Identity, Endpoint}; +use crate::vl1::vl1node::{VL1CallerInterface, VL1Node}; +use crate::vl2::switch::Switch; +use crate::{PacketBuffer, PacketBufferPool}; pub trait CallerInterface: VL1CallerInterface { } @@ -20,15 +22,15 @@ pub trait CallerInterface: VL1CallerInterface { /// /// This is a composition of the VL1 node and the VL2 virtual switch. pub struct Node { - vl1: crate::vl1::node::Node, - vl2: crate::vl2::switch::Switch, + vl1: VL1Node, + vl2: Switch, } impl Node { pub fn new(ci: &CI, auto_generate_identity_type: Option) -> Result { Ok(Node { - vl1: crate::vl1::node::Node::new(ci, auto_generate_identity_type)?, - vl2: crate::vl2::switch::Switch::new(), + vl1: VL1Node::new(ci, auto_generate_identity_type)?, + vl2: Switch::new(), }) } diff --git a/zerotier-network-hypervisor/src/vl1/fragmentedpacket.rs b/zerotier-network-hypervisor/src/vl1/fragmentedpacket.rs index 32fbbc70e..5e1d58f1e 100644 --- a/zerotier-network-hypervisor/src/vl1/fragmentedpacket.rs +++ b/zerotier-network-hypervisor/src/vl1/fragmentedpacket.rs @@ -6,7 +6,7 @@ * https://www.zerotier.com/ */ -use crate::vl1::node::PacketBuffer; +use crate::vl1::vl1node::PacketBuffer; use crate::vl1::protocol::*; /// Packet fragment re-assembler and container. diff --git a/zerotier-network-hypervisor/src/vl1/identity.rs b/zerotier-network-hypervisor/src/vl1/identity.rs index e943c49f8..96c909d05 100644 --- a/zerotier-network-hypervisor/src/vl1/identity.rs +++ b/zerotier-network-hypervisor/src/vl1/identity.rs @@ -20,6 +20,7 @@ use zerotier_core_crypto::hash::*; use zerotier_core_crypto::p521::*; use zerotier_core_crypto::salsa::Salsa; use zerotier_core_crypto::secret::Secret; + use crate::error::InvalidFormatError; use crate::vl1::Address; use crate::vl1::buffer::Buffer; diff --git a/zerotier-network-hypervisor/src/vl1/mod.rs b/zerotier-network-hypervisor/src/vl1/mod.rs index bd2728964..1dda609aa 100644 --- a/zerotier-network-hypervisor/src/vl1/mod.rs +++ b/zerotier-network-hypervisor/src/vl1/mod.rs @@ -14,7 +14,7 @@ pub mod rootset; #[allow(unused)] pub(crate) mod protocol; pub(crate) mod buffer; -pub(crate) mod node; +pub(crate) mod vl1node; pub(crate) mod path; pub(crate) mod peer; pub(crate) mod dictionary; @@ -32,4 +32,4 @@ pub use dictionary::Dictionary; pub use inetaddress::InetAddress; pub use peer::Peer; pub use path::Path; -pub use node::{PacketBuffer, PacketBufferPool, PacketBufferFactory}; +pub use vl1node::{PacketBuffer, PacketBufferPool, PacketBufferFactory, VL1CallerInterface}; diff --git a/zerotier-network-hypervisor/src/vl1/path.rs b/zerotier-network-hypervisor/src/vl1/path.rs index 7da15fafb..3b26aac8e 100644 --- a/zerotier-network-hypervisor/src/vl1/path.rs +++ b/zerotier-network-hypervisor/src/vl1/path.rs @@ -14,7 +14,7 @@ use parking_lot::Mutex; use crate::util::U64PassThroughHasher; use crate::vl1::Endpoint; use crate::vl1::fragmentedpacket::FragmentedPacket; -use crate::vl1::node::{PacketBuffer, VL1CallerInterface}; +use crate::vl1::vl1node::{PacketBuffer, VL1CallerInterface}; use crate::vl1::protocol::*; /// Keepalive interval for paths in milliseconds. diff --git a/zerotier-network-hypervisor/src/vl1/peer.rs b/zerotier-network-hypervisor/src/vl1/peer.rs index 8f7cee3fd..079827a0c 100644 --- a/zerotier-network-hypervisor/src/vl1/peer.rs +++ b/zerotier-network-hypervisor/src/vl1/peer.rs @@ -28,7 +28,7 @@ use crate::defaults::UDP_DEFAULT_MTU; use crate::util::pool::{Pool, PoolFactory}; use crate::vl1::{Dictionary, Endpoint, Identity, InetAddress, Path}; use crate::vl1::buffer::Buffer; -use crate::vl1::node::*; +use crate::vl1::vl1node::*; use crate::vl1::protocol::*; /// Interval for servicing and background operations on peers. @@ -193,7 +193,7 @@ impl Peer { /// Receive, decrypt, authenticate, and process an incoming packet from this peer. /// If the packet comes in multiple fragments, the fragments slice should contain all /// those fragments after the main packet header and first chunk. - pub(crate) fn receive(&self, node: &Node, ci: &CI, ph: &PH, time_ticks: i64, source_path: &Arc, header: &PacketHeader, packet: &Buffer<{ PACKET_SIZE_MAX }>, fragments: &[Option]) { + pub(crate) fn receive(&self, node: &VL1Node, ci: &CI, ph: &PH, time_ticks: i64, source_path: &Arc, header: &PacketHeader, packet: &Buffer<{ PACKET_SIZE_MAX }>, fragments: &[Option]) { let _ = packet.as_bytes_starting_at(PACKET_VERB_INDEX).map(|packet_frag0_payload_bytes| { let mut payload = node.get_packet_buffer(); @@ -377,7 +377,7 @@ impl Peer { /// /// This will go directly if there is an active path, or otherwise indirectly /// via a root or some other route. - pub(crate) fn send(&self, ci: &CI, node: &Node, time_ticks: i64, packet_id: PacketID, packet: &Buffer<{ PACKET_SIZE_MAX }>) -> bool { + pub(crate) fn send(&self, ci: &CI, node: &VL1Node, time_ticks: i64, packet_id: PacketID, packet: &Buffer<{ PACKET_SIZE_MAX }>) -> bool { self.path(node).map_or(false, |path| { if self.send_to_endpoint(ci, path.endpoint(), Some(path.local_socket()), Some(path.local_interface()), packet_id, packet) { self.last_send_time_ticks.store(time_ticks, Ordering::Relaxed); @@ -415,7 +415,7 @@ impl Peer { /// /// This has its own send logic so it can handle either an explicit endpoint or a /// known one. - pub(crate) fn send_hello(&self, ci: &CI, node: &Node, explicit_endpoint: Option) -> bool { + pub(crate) fn send_hello(&self, ci: &CI, node: &VL1Node, explicit_endpoint: Option) -> bool { let path = if explicit_endpoint.is_none() { self.path(node) } else { None }; explicit_endpoint.as_ref().map_or_else(|| Some(path.as_ref().unwrap().endpoint()), |ep| Some(ep)).map_or(false, |endpoint| { let mut packet: Buffer<{ PACKET_SIZE_MAX }> = Buffer::new(); @@ -507,10 +507,10 @@ impl Peer { pub(crate) fn call_every_interval(&self, ct: &CI, time_ticks: i64) {} #[inline(always)] - fn receive_hello(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} + fn receive_hello(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} #[inline(always)] - fn receive_error(&self, ci: &CI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) { + fn receive_error(&self, ci: &CI, ph: &PH, node: &VL1Node, time_ticks: i64, source_path: &Arc, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) { let mut cursor: usize = 0; let _ = payload.read_struct::(&mut cursor).map(|error_header| { let in_re_packet_id = error_header.in_re_packet_id; @@ -530,7 +530,7 @@ impl Peer { } #[inline(always)] - fn receive_ok(&self, ci: &CI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) { + fn receive_ok(&self, ci: &CI, ph: &PH, node: &VL1Node, time_ticks: i64, source_path: &Arc, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) { let mut cursor: usize = 0; let _ = payload.read_struct::(&mut cursor).map(|ok_header| { let in_re_packet_id = ok_header.in_re_packet_id; @@ -554,25 +554,25 @@ impl Peer { } #[inline(always)] - fn receive_whois(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} + fn receive_whois(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} #[inline(always)] - fn receive_rendezvous(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} + fn receive_rendezvous(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} #[inline(always)] - fn receive_echo(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} + fn receive_echo(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} #[inline(always)] - fn receive_push_direct_paths(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} + fn receive_push_direct_paths(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} #[inline(always)] - fn receive_user_message(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} + fn receive_user_message(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc, payload: &Buffer<{ PACKET_SIZE_MAX }>) {} /// Get current best path or None if there are no direct paths to this peer. pub fn direct_path(&self) -> Option> { self.paths.lock().first().map(|p| p.clone()) } /// Get either the current best direct path or an indirect path. - pub fn path(&self, node: &Node) -> Option> { + pub fn path(&self, node: &VL1Node) -> Option> { self.direct_path().map_or_else(|| node.root().map_or(None, |root| root.direct_path().map_or(None, |bp| Some(bp))), |bp| Some(bp)) } diff --git a/zerotier-network-hypervisor/src/vl1/rootset.rs b/zerotier-network-hypervisor/src/vl1/rootset.rs index e314c4646..2a638a8b9 100644 --- a/zerotier-network-hypervisor/src/vl1/rootset.rs +++ b/zerotier-network-hypervisor/src/vl1/rootset.rs @@ -88,7 +88,7 @@ pub struct RootSet { impl RootSet { /// Create and sign a new root set. /// This cannot create legacy "planet" or "moon" type root sets. For those the old mkworld code must be used. - pub fn create(roots: &[Root], timestamp: i64, oob_update_url: Option<&str>, name: Option<&str>, contact: Option<&str>, signing_key: &RootSetSecretSigningKey) -> Result { + pub fn create(roots: &[Root], timestamp: i64, oob_update_url: Option<&str>, name: Option<&str>, contact: Option<&str>, signing_key: &RootSetSecretSigningKey) -> Result { let mut sorted_roots = roots.to_vec(); sorted_roots.sort(); sorted_roots.dedup(); diff --git a/zerotier-network-hypervisor/src/vl1/node.rs b/zerotier-network-hypervisor/src/vl1/vl1node.rs similarity index 96% rename from zerotier-network-hypervisor/src/vl1/node.rs rename to zerotier-network-hypervisor/src/vl1/vl1node.rs index 7be27a290..e22d3dbc3 100644 --- a/zerotier-network-hypervisor/src/vl1/node.rs +++ b/zerotier-network-hypervisor/src/vl1/vl1node.rs @@ -25,15 +25,7 @@ use crate::vl1::peer::Peer; use crate::vl1::protocol::*; use crate::vl1::rootset::RootSet; use crate::vl1::whoisqueue::{QueuedPacket, WhoisQueue}; - -/// Standard packet buffer type including pool container. -pub type PacketBuffer = Pooled, PooledBufferFactory<{ PACKET_SIZE_MAX }>>; - -/// Factory type to supply to a new PacketBufferPool. -pub type PacketBufferFactory = PooledBufferFactory<{ PACKET_SIZE_MAX }>; - -/// Source for instances of PacketBuffer -pub type PacketBufferPool = Pool, PacketBufferFactory>; +use crate::{PacketBuffer, PacketBufferPool}; /// Callback interface and call context for calls to the node (for VL1). /// @@ -131,7 +123,7 @@ struct BackgroundTaskIntervals { peers: IntervalGate<{ Peer::INTERVAL }>, } -pub struct Node { +pub struct VL1Node { pub(crate) instance_id: u64, identity: Identity, intervals: Mutex, @@ -144,7 +136,7 @@ pub struct Node { secure_prng: SecureRandom, } -impl Node { +impl VL1Node { /// Create a new Node. /// /// If the auto-generate identity type is not None, a new identity will be generated if @@ -339,6 +331,6 @@ impl Node { } } -unsafe impl Send for Node {} +unsafe impl Send for VL1Node {} -unsafe impl Sync for Node {} +unsafe impl Sync for VL1Node {} diff --git a/zerotier-network-hypervisor/src/vl1/whoisqueue.rs b/zerotier-network-hypervisor/src/vl1/whoisqueue.rs index 844917b29..db4c361f4 100644 --- a/zerotier-network-hypervisor/src/vl1/whoisqueue.rs +++ b/zerotier-network-hypervisor/src/vl1/whoisqueue.rs @@ -13,7 +13,7 @@ use parking_lot::Mutex; use crate::util::gate::IntervalGate; use crate::vl1::Address; use crate::vl1::fragmentedpacket::FragmentedPacket; -use crate::vl1::node::{Node, PacketBuffer, VL1CallerInterface}; +use crate::vl1::vl1node::{VL1Node, PacketBuffer, VL1CallerInterface}; use crate::vl1::protocol::{WHOIS_RETRY_INTERVAL, WHOIS_MAX_WAITING_PACKETS, WHOIS_RETRY_MAX}; pub(crate) enum QueuedPacket { @@ -35,7 +35,7 @@ impl WhoisQueue { pub fn new() -> Self { Self(Mutex::new(HashMap::new())) } /// Launch or renew a WHOIS query and enqueue a packet to be processed when (if) it is received. - pub fn query(&self, node: &Node, ci: &CI, target: Address, packet: Option) { + pub fn query(&self, node: &VL1Node, ci: &CI, target: Address, packet: Option) { let mut q = self.0.lock(); let qi = q.entry(target).or_insert_with(|| WhoisQueueItem { @@ -63,7 +63,7 @@ impl WhoisQueue { } /// Called every INTERVAL during background tasks. - pub fn call_every_interval(&self, node: &Node, ci: &CI, time_ticks: i64) { + pub fn call_every_interval(&self, node: &VL1Node, ci: &CI, time_ticks: i64) { let mut targets: Vec
= Vec::new(); self.0.lock().retain(|target, qi| { if qi.retry_count < WHOIS_RETRY_MAX { @@ -81,7 +81,7 @@ impl WhoisQueue { } } - fn send_whois(&self, node: &Node, ci: &CI, targets: &[Address]) { + fn send_whois(&self, node: &VL1Node, ci: &CI, targets: &[Address]) { todo!() } } diff --git a/zerotier-network-hypervisor/src/vl2/switch.rs b/zerotier-network-hypervisor/src/vl2/switch.rs index f2ad20d81..87ad9b0c1 100644 --- a/zerotier-network-hypervisor/src/vl2/switch.rs +++ b/zerotier-network-hypervisor/src/vl2/switch.rs @@ -8,7 +8,7 @@ use std::sync::Arc; -use crate::vl1::node::VL1PacketHandler; +use crate::vl1::vl1node::VL1PacketHandler; use crate::vl1::{Peer, Path}; use crate::vl1::buffer::Buffer; use crate::vl1::protocol::{PACKET_SIZE_MAX, PacketID}; diff --git a/zerotier-system-service/Cargo.lock b/zerotier-system-service/Cargo.lock index 7dfa52df3..d22e2036f 100644 --- a/zerotier-system-service/Cargo.lock +++ b/zerotier-system-service/Cargo.lock @@ -2,6 +2,40 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "aead" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fc95d1bdb8e6666b2b217308eeeb09f2d6728d104be3e31916cc74d15420331" +dependencies = [ + "generic-array", +] + +[[package]] +name = "aes" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "884391ef1066acaa41e766ba8f596341b96e93ce34f9a43e7d24bf0a0eaf0561" +dependencies = [ + "aes-soft", + "aesni", + "cipher", +] + +[[package]] +name = "aes-gcm" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5278b5fabbb9bd46e24aa69b2fdea62c99088e0a950a9be40e3e0101298f88da" +dependencies = [ + "aead", + "aes", + "cipher", + "ctr", + "ghash", + "subtle", +] + [[package]] name = "aes-gmac-siv" version = "0.1.0" @@ -10,6 +44,35 @@ dependencies = [ "openssl", ] +[[package]] +name = "aes-soft" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be14c7498ea50828a38d0e24a765ed2effe92a705885b57d029cd67d45744072" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "aesni" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea2e11f5e94c2f7d386164cc2aa1f97823fed6f259e486940a71c174dd01b0ce" +dependencies = [ + "cipher", + "opaque-debug", +] + +[[package]] +name = "aho-corasick" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f" +dependencies = [ + "memchr", +] + [[package]] name = "ansi_term" version = "0.11.0" @@ -19,6 +82,232 @@ dependencies = [ "winapi", ] +[[package]] +name = "anyhow" +version = "1.0.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee10e43ae4a853c0a3591d4e2ada1719e553be18199d9da9d4a83f5927c2f5c7" + +[[package]] +name = "async-channel" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2114d64672151c0c5eaa5e131ec84a74f06e1e559830dabba01ca30605d66319" +dependencies = [ + "concurrent-queue", + "event-listener", + "futures-core", +] + +[[package]] +name = "async-dup" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7427a12b8dc09291528cfb1da2447059adb4a257388c2acd6497a79d55cf6f7c" +dependencies = [ + "futures-io", + "simple-mutex", +] + +[[package]] +name = "async-executor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "871f9bb5e0a22eeb7e8cf16641feb87c9dc67032ccf8ff49e772eb9941d3a965" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "once_cell", + "slab", +] + +[[package]] +name = "async-fs" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b3ca4f8ff117c37c278a2f7415ce9be55560b846b5bc4412aaa5d29c1c3dae2" +dependencies = [ + "async-lock", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-global-executor" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9586ec52317f36de58453159d48351bc244bc24ced3effc1fce22f3d48664af6" +dependencies = [ + "async-channel", + "async-executor", + "async-io", + "async-mutex", + "blocking", + "futures-lite", + "num_cpus", + "once_cell", +] + +[[package]] +name = "async-h1" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc5142de15b549749cce62923a50714b0d7b77f5090ced141599e78899865451" +dependencies = [ + "async-channel", + "async-dup", + "async-std", + "byte-pool", + "futures-core", + "http-types", + "httparse", + "lazy_static", + "log", + "pin-project", +] + +[[package]] +name = "async-io" +version = "1.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a811e6a479f2439f0c04038796b5cfb3d2ad56c230e0f2d3f7b04d68cfee607b" +dependencies = [ + "concurrent-queue", + "futures-lite", + "libc", + "log", + "once_cell", + "parking", + "polling", + "slab", + "socket2", + "waker-fn", + "winapi", +] + +[[package]] +name = "async-lock" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6a8ea61bf9947a1007c5cada31e647dbc77b103c679858150003ba697ea798b" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-mutex" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479db852db25d9dbf6204e6cb6253698f175c15726470f78af0d918e99d6156e" +dependencies = [ + "event-listener", +] + +[[package]] +name = "async-net" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5373304df79b9b4395068fb080369ec7178608827306ce4d081cba51cac551df" +dependencies = [ + "async-io", + "blocking", + "futures-lite", +] + +[[package]] +name = "async-process" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b21b63ab5a0db0369deb913540af2892750e42d949faacc7a61495ac418a1692" +dependencies = [ + "async-io", + "blocking", + "cfg-if 1.0.0", + "event-listener", + "futures-lite", + "libc", + "once_cell", + "signal-hook", + "winapi", +] + +[[package]] +name = "async-sse" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53bba003996b8fd22245cd0c59b869ba764188ed435392cf2796d03b805ade10" +dependencies = [ + "async-channel", + "async-std", + "http-types", + "log", + "memchr", + "pin-project-lite 0.1.12", +] + +[[package]] +name = "async-std" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8056f1455169ab86dd47b47391e4ab0cbd25410a70e9fe675544f49bafaf952" +dependencies = [ + "async-channel", + "async-global-executor", + "async-io", + "async-lock", + "async-process", + "crossbeam-utils", + "futures-channel", + "futures-core", + "futures-io", + "futures-lite", + "gloo-timers", + "kv-log-macro", + "log", + "memchr", + "num_cpus", + "once_cell", + "pin-project-lite 0.2.7", + "pin-utils", + "slab", + "wasm-bindgen-futures", +] + +[[package]] +name = "async-task" +version = "4.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91831deabf0d6d7ec49552e489aed63b7456a7a3c46cff62adad428110b0af0" + +[[package]] +name = "async-trait" +version = "0.1.51" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44318e776df68115a881de9a8fd1b9e53368d7a4a5ce4cc48517da3393233a5e" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "atomic-polyfill" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e686d748538a32325b28d6411dd8a939e7ad5128e5d0023cc4fd3573db456042" +dependencies = [ + "critical-section", + "riscv-target", +] + +[[package]] +name = "atomic-waker" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a" + [[package]] name = "atty" version = "0.2.14" @@ -36,12 +325,45 @@ version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" +[[package]] +name = "bare-metal" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5deb64efa5bd81e31fcd1938615a6d98c82eafcbcd787162b6f63b91d6bac5b3" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "bare-metal" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fe8f5a8a398345e52358e18ff07cc17a568fbca5c6f73873d3a62056309603" + +[[package]] +name = "base-x" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4521f3e3d031370679b3b140beb36dfe4801b09ac77e30c61941f97df3ef28b" + [[package]] name = "base64" version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bit_field" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4" + +[[package]] +name = "bitfield" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46afbd2983a5d5a7bd740ccb198caf5b82f45c40c09c0eed36052d91cb92e719" + [[package]] name = "bitflags" version = "1.3.2" @@ -57,6 +379,36 @@ dependencies = [ "generic-array", ] +[[package]] +name = "blocking" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e170dbede1f740736619b776d7251cb1b9095c435c34d8ca9f57fcd2f335e9" +dependencies = [ + "async-channel", + "async-task", + "atomic-waker", + "fastrand", + "futures-lite", + "once_cell", +] + +[[package]] +name = "bumpalo" +version = "3.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1e260c3a9040a7c19a12468758f4c16f31a81a1fe087482be9570ec864bb6c" + +[[package]] +name = "byte-pool" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8c7230ddbb427b1094d477d821a99f3f54d36333178eeb806e279bcdcecf0ca" +dependencies = [ + "crossbeam-queue", + "stable_deref_trait", +] + [[package]] name = "byteorder" version = "1.4.3" @@ -64,10 +416,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" [[package]] -name = "bytes" -version = "1.1.0" +name = "cache-padded" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8" +checksum = "631ae5198c9be5e753e5cc215e1bd73c2b466a3565173db433f52bb9d3e66dba" [[package]] name = "cc" @@ -96,10 +448,19 @@ dependencies = [ "libc", "num-integer", "num-traits", - "time", + "time 0.1.43", "winapi", ] +[[package]] +name = "cipher" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f8e7987cbd042a63249497f41aed09f8e65add917ea6566effbc56578d6801" +dependencies = [ + "generic-array", +] + [[package]] name = "clap" version = "2.33.3" @@ -128,18 +489,47 @@ dependencies = [ ] [[package]] -name = "console" -version = "0.15.0" +name = "concurrent-queue" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b32d32ca44b70c3e4acd7db1babf555fa026e385fb95f18028f88848b3c31" +checksum = "30ed07550be01594c6026cff2a1d7fe9c8f683caa798e12b68694ac9e88286a3" dependencies = [ - "encode_unicode", - "libc", - "once_cell", - "regex", - "terminal_size", - "unicode-width", - "winapi", + "cache-padded", +] + +[[package]] +name = "const_fn" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f92cfa0fd5690b3cf8c1ef2cabbd9b7ef22fa53cf5e1f92b05103f6d5d1cf6e7" + +[[package]] +name = "cookie" +version = "0.14.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a5d7b21829bc7b4bf4754a978a241ae54ea55a40f92bb20216e54096f4b951" +dependencies = [ + "aes-gcm", + "base64", + "hkdf", + "hmac", + "percent-encoding", + "rand 0.8.4", + "sha2", + "time 0.2.27", + "version_check", +] + +[[package]] +name = "cortex-m" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ac919ef424449ec8c08d515590ce15d9262c0ca5f0da5b0c901e971a3b783b3" +dependencies = [ + "bare-metal 0.2.5", + "bitfield", + "embedded-hal", + "volatile-register", ] [[package]] @@ -151,6 +541,54 @@ dependencies = [ "libc", ] +[[package]] +name = "cpuid-bool" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcb25d077389e53838a8158c8e99174c5a9d902dee4904320db714f3c653ffba" + +[[package]] +name = "critical-section" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01e191a5a6f6edad9b679777ef6b6c0f2bdd4a333f2ecb8f61c3e28109a03d70" +dependencies = [ + "bare-metal 1.0.0", + "cfg-if 1.0.0", + "cortex-m", + "riscv", +] + +[[package]] +name = "crossbeam-queue" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b10ddc024425c88c2ad148c1b0fd53f4c6d38db9697c9f1588381212fa657c9" +dependencies = [ + "cfg-if 1.0.0", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d82cfc11ce7f2c3faef78d8a684447b40d503d9681acebed6cb728d45940c4db" +dependencies = [ + "cfg-if 1.0.0", + "lazy_static", +] + +[[package]] +name = "crypto-mac" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bff07008ec701e8028e2ceb8f83f0e4274ee62bd2dbdc4fefff2e9a91824081a" +dependencies = [ + "generic-array", + "subtle", +] + [[package]] name = "cstr-argument" version = "0.1.2" @@ -161,6 +599,25 @@ dependencies = [ "memchr", ] +[[package]] +name = "ctor" +version = "0.1.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc0a48a9b826acdf4028595adc9db92caea352f7af011a3034acd172a52a0aa" +dependencies = [ + "quote", + "syn", +] + +[[package]] +name = "ctr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb4a30d54f7443bf3d6191dcd486aca19e67cb3c49fa7a06a319966346707e7f" +dependencies = [ + "cipher", +] + [[package]] name = "curve25519-dalek" version = "3.2.0" @@ -184,18 +641,6 @@ dependencies = [ "num_cpus", ] -[[package]] -name = "dialoguer" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61579ada4ec0c6031cfac3f86fdba0d195a7ebeb5e36693bd53cb5999a25beeb" -dependencies = [ - "console", - "lazy_static", - "tempfile", - "zeroize", -] - [[package]] name = "digest" version = "0.9.0" @@ -218,6 +663,12 @@ dependencies = [ "sha2", ] +[[package]] +name = "discard" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "212d0f5754cb6769937f4501cc0e67f4f4483c8d2c3e1e922ee9edbe4ab4c7c0" + [[package]] name = "ed25519" version = "1.2.0" @@ -242,16 +693,29 @@ dependencies = [ ] [[package]] -name = "encode_unicode" -version = "0.3.6" +name = "embedded-hal" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "e36cfb62ff156596c892272f3015ef952fe1525e85261fa3a7f327bd6b384ab9" +dependencies = [ + "nb 0.1.3", + "void", +] [[package]] -name = "fnv" -version = "1.0.7" +name = "event-listener" +version = "2.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +checksum = "f7531096570974c3a9dcf9e4b8e1cede1ec26cf5046219fb3b9d897503b9be59" + +[[package]] +name = "fastrand" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b394ed3d285a429378d3b384b9eb1285267e7df4b166df24b7a6939a04dc392e" +dependencies = [ + "instant", +] [[package]] name = "foreign-types" @@ -269,18 +733,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" [[package]] -name = "futures" -version = "0.3.17" +name = "form_urlencoded" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a12aa0eb539080d55c3f2d45a67c3b58b6b0773c1a3ca2dfec66d58c97fd66ca" +checksum = "5fc25a87fa4fd2094bffb06925852034d90a17f0d1e05197d4956d3555752191" dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", + "matches", + "percent-encoding", ] [[package]] @@ -290,7 +749,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5da6ba8c3bb3c165d3c7319fc1cc8304facf1fb8db99c5de877183c08a273888" dependencies = [ "futures-core", - "futures-sink", ] [[package]] @@ -299,23 +757,27 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "88d1c26957f23603395cd326b0ffe64124b818f4449552f960d815cfba83a53d" -[[package]] -name = "futures-executor" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45025be030969d763025784f7f355043dc6bc74093e4ecc5000ca4dc50d8745c" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - [[package]] name = "futures-io" version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "522de2a0fe3e380f1bc577ba0474108faf3f6b18321dbf60b3b9c39a75073377" +[[package]] +name = "futures-lite" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "memchr", + "parking", + "pin-project-lite 0.2.7", + "waker-fn", +] + [[package]] name = "futures-macro" version = "0.3.17" @@ -329,12 +791,6 @@ dependencies = [ "syn", ] -[[package]] -name = "futures-sink" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36ea153c13024fe480590b3e3d4cad89a0cfacecc24577b68f86c6ced9c2bc11" - [[package]] name = "futures-task" version = "0.3.17" @@ -348,14 +804,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36568465210a3a6ee45e1f165136d68671471a501e632e9a98d96872222b5481" dependencies = [ "autocfg", - "futures-channel", "futures-core", - "futures-io", "futures-macro", - "futures-sink", "futures-task", - "memchr", - "pin-project-lite", + "pin-project-lite 0.2.7", "pin-utils", "proc-macro-hack", "proc-macro-nested", @@ -408,6 +860,29 @@ dependencies = [ "wasi 0.10.2+wasi-snapshot-preview1", ] +[[package]] +name = "ghash" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97304e4cd182c3846f7575ced3890c53012ce534ad9114046b0a9e00bb30a375" +dependencies = [ + "opaque-debug", + "polyval", +] + +[[package]] +name = "gloo-timers" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47204a46aaff920a1ea58b11d03dec6f704287d27561724a4631e450654a891f" +dependencies = [ + "futures-channel", + "futures-core", + "js-sys", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "gpg-error" version = "0.5.2" @@ -417,6 +892,27 @@ dependencies = [ "libgpg-error-sys", ] +[[package]] +name = "hash32" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0c35f58762feb77d74ebe43bdbc3210f09be9fe6742234d573bacc26ed92b67" +dependencies = [ + "byteorder", +] + +[[package]] +name = "heapless" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe65ef062f1af5b1b189842b0bc45bd671c38e1d22c6aa22e6ada03d01026d53" +dependencies = [ + "atomic-polyfill", + "hash32", + "spin", + "stable_deref_trait", +] + [[package]] name = "hermit-abi" version = "0.1.19" @@ -433,25 +929,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" [[package]] -name = "http" -version = "0.2.5" +name = "hkdf" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1323096b05d41827dadeaee54c9981958c0f94e670bc94ed80037d1a7b8b186b" +checksum = "51ab2f639c231793c5f6114bdb9bbe50a7dbbfcd7c7c6bd8475dec2d991e964f" dependencies = [ - "bytes", - "fnv", - "itoa", + "digest", + "hmac", ] [[package]] -name = "http-body" -version = "0.4.3" +name = "hmac" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "399c583b2979440c60be0821a6199eca73bc3c8dcd9d070d75ac726e2c6186e5" +checksum = "c1441c6b1e930e2817404b5046f1f989899143a12bf92de603b69f4e0aee1e15" dependencies = [ - "bytes", - "http", - "pin-project-lite", + "crypto-mac", + "digest", +] + +[[package]] +name = "http-client" +version = "6.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea880b03c18a7e981d7fb3608b8904a98425d53c440758fcebf7d934aa56547c" +dependencies = [ + "async-trait", + "cfg-if 1.0.0", + "dashmap", + "http-types", + "log", +] + +[[package]] +name = "http-types" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9b187a72d63adbfba487f48095306ac823049cb504ee195541e91c7775f5ad" +dependencies = [ + "anyhow", + "async-channel", + "async-std", + "base64", + "cookie", + "futures-lite", + "infer", + "pin-project-lite 0.2.7", + "rand 0.7.3", + "serde", + "serde_json", + "serde_qs", + "serde_urlencoded", + "url", ] [[package]] @@ -461,33 +990,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "acd94fdbe1d4ff688b67b04eee2e17bd50995534a61539e45adfefb45e5e5503" [[package]] -name = "httpdate" -version = "1.0.1" +name = "idna" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6456b8a6c8f33fee7d958fcd1b60d55b11940a79e63ae87013e6d22e26034440" +checksum = "418a0a6fab821475f634efe3ccc45c013f742efe03d853e8d3355d5cb850ecf8" +dependencies = [ + "matches", + "unicode-bidi", + "unicode-normalization", +] [[package]] -name = "hyper" -version = "0.14.13" +name = "infer" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15d1cfb9e4f68655fa04c01f59edb405b6074a0f7118ea881e5026e4a1cd8593" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2 0.4.2", - "tokio", - "tower-service", - "tracing", - "want", -] +checksum = "64e9829a50b42bb782c1df523f78d332fe371b10c661e78b7a3c34b0198e9fac" [[package]] name = "instant" @@ -504,6 +1021,24 @@ version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b71991ff56294aa922b450139ee08b3bfc70982c6b2c7562771375cf73542dd4" +[[package]] +name = "js-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84" +dependencies = [ + "wasm-bindgen", +] + +[[package]] +name = "kv-log-macro" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0de8b303297635ad57c9f5059fd9cee7a47f8e8daa09df0fcd07dd39fb22977f" +dependencies = [ + "log", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -554,6 +1089,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710" dependencies = [ "cfg-if 1.0.0", + "value-bag", ] [[package]] @@ -565,6 +1101,21 @@ dependencies = [ "twox-hash", ] +[[package]] +name = "mach" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa" +dependencies = [ + "libc", +] + +[[package]] +name = "matches" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" + [[package]] name = "md-5" version = "0.9.1" @@ -583,46 +1134,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a" [[package]] -name = "mio" -version = "0.7.13" +name = "nb" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c2bdb6314ec10835cd3293dd268473a835c02b7b352e788be788b3c6ca6bb16" +checksum = "801d31da0513b6ec5214e9bf433a77966320625a37860f910be265be6e18d06f" dependencies = [ - "libc", - "log", - "miow", - "ntapi", - "winapi", + "nb 1.0.0", ] [[package]] -name = "miow" -version = "0.3.7" +name = "nb" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9f1c5b025cda876f66ef43a113f91ebc9f4ccef34843000e0adf6ebbab84e21" -dependencies = [ - "winapi", -] - -[[package]] -name = "ntapi" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f6bb902e437b6d86e03cce10a7e2af662292c5dfef23b65899ea3ac9354ad44" -dependencies = [ - "winapi", -] - -[[package]] -name = "num-derive" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "546c37ac5d9e56f55e73b677106873d9d9f5190605e41a856503623648488cae" [[package]] name = "num-integer" @@ -692,6 +1216,12 @@ dependencies = [ "vcpkg", ] +[[package]] +name = "parking" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "427c3892f9e783d91cc128285287e70a59e206ca452770ece88a76f7a3eddd72" + [[package]] name = "parking_lot" version = "0.11.2" @@ -717,6 +1247,38 @@ dependencies = [ "winapi", ] +[[package]] +name = "percent-encoding" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fd5641d01c8f18a23da7b6fe29298ff4b55afcccdf78973b24cf3175fee32e" + +[[package]] +name = "pin-project" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "576bc800220cc65dac09e99e97b08b358cfab6e17078de8dc5fee223bd2d0c08" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e8fe8163d14ce7f0cdac2e040116f22eac817edabff0be91e8aff7e9accf389" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "pin-project-lite" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "257b64915a082f7811703966789728173279bdebb956b143dbcd23f6f970a777" + [[package]] name = "pin-project-lite" version = "0.2.7" @@ -735,6 +1297,30 @@ version = "0.3.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c9b1041b4387893b91ee6746cddfc28516aff326a3519fb2adf820932c5e6cb" +[[package]] +name = "polling" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92341d779fa34ea8437ef4d82d440d5e1ce3f3ff7f824aa64424cd481f9a1f25" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "log", + "wepoll-ffi", + "winapi", +] + +[[package]] +name = "polyval" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc4aa140b9abd2bc40d9c3f7ccec842679cd79045ac3a7ac698c1a064b7cd" +dependencies = [ + "cpuid-bool", + "opaque-debug", + "universal-hash", +] + [[package]] name = "ppv-lite86" version = "0.2.14" @@ -867,6 +1453,8 @@ version = "1.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461" dependencies = [ + "aho-corasick", + "memchr", "regex-syntax", ] @@ -877,12 +1465,39 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] -name = "remove_dir_all" -version = "0.5.3" +name = "riscv" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acd125665422973a33ac9d3dd2df85edad0f4ae9b00dafb1a05e43a9f5ef8e7" +checksum = "6907ccdd7a31012b70faf2af85cd9e5ba97657cc3987c4f13f8e4d2c2a088aba" dependencies = [ - "winapi", + "bare-metal 1.0.0", + "bit_field", + "riscv-target", +] + +[[package]] +name = "riscv-target" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "88aa938cda42a0cf62a20cfe8d139ff1af20c2e681212b5b34adb5a58333f222" +dependencies = [ + "lazy_static", + "regex", +] + +[[package]] +name = "route-recognizer" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56770675ebc04927ded3e60633437841581c285dc6236109ea25fbf3beb7b59e" + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", ] [[package]] @@ -897,6 +1512,21 @@ version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + [[package]] name = "serde" version = "1.0.130" @@ -928,6 +1558,35 @@ dependencies = [ "serde", ] +[[package]] +name = "serde_qs" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7715380eec75f029a4ef7de39a9200e0a63823176b759d055b613f5a87df6a6" +dependencies = [ + "percent-encoding", + "serde", + "thiserror", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edfa57a7f8d9c1d260a549e7224100f6c43d43f9103e06dd8b4095a9b2b43ce9" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "sha1" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d" + [[package]] name = "sha2" version = "0.9.8" @@ -941,6 +1600,16 @@ dependencies = [ "opaque-debug", ] +[[package]] +name = "signal-hook" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c98891d737e271a2954825ef19e46bd16bdb98e2746f2eec4f7a4ef7946efd1" +dependencies = [ + "libc", + "signal-hook-registry", +] + [[package]] name = "signal-hook-registry" version = "1.4.0" @@ -962,6 +1631,15 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cc47a29ce97772ca5c927f75bac34866b16d64e07f330c3248e2d7226623901b" +[[package]] +name = "simple-mutex" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38aabbeafa6f6dead8cebf246fe9fae1f9215c8d29b3a69f93bd62a9e4a3dcd6" +dependencies = [ + "event-listener", +] + [[package]] name = "slab" version = "0.4.5" @@ -975,14 +1653,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309" [[package]] -name = "socket2" -version = "0.3.19" +name = "smol" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "122e570113d28d773067fab24266b66753f6ea915758651696b6e35e49f88d6e" +checksum = "85cf3b5351f3e783c1d79ab5fc604eeed8b8ae9abd36b166e8b87a089efd85e4" dependencies = [ - "cfg-if 1.0.0", - "libc", - "winapi", + "async-channel", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-net", + "async-process", + "blocking", + "futures-lite", + "once_cell", ] [[package]] @@ -995,12 +1680,85 @@ dependencies = [ "winapi", ] +[[package]] +name = "spin" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5" +dependencies = [ + "lock_api", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "standback" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e113fb6f3de07a243d434a56ec6f186dfd51cb08448239fe7bcae73f87ff28ff" +dependencies = [ + "version_check", +] + [[package]] name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +[[package]] +name = "stdweb" +version = "0.4.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d022496b16281348b52d0e30ae99e01a73d737b2f45d38fed4edf79f9325a1d5" +dependencies = [ + "discard", + "rustc_version", + "stdweb-derive", + "stdweb-internal-macros", + "stdweb-internal-runtime", + "wasm-bindgen", +] + +[[package]] +name = "stdweb-derive" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c87a60a40fccc84bef0652345bbbbbe20a605bf5d0ce81719fc476f5c03b50ef" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "serde_derive", + "syn", +] + +[[package]] +name = "stdweb-internal-macros" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58fa5ff6ad0d98d1ffa8cb115892b6e69d67799f6763e162a1c9db421dc22e11" +dependencies = [ + "base-x", + "proc-macro2", + "quote", + "serde", + "serde_derive", + "serde_json", + "sha1", + "syn", +] + +[[package]] +name = "stdweb-internal-runtime" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "213701ba3370744dcd1a12960caa4843b3d68b4d1c0a5d575e0d65b2ee9d16c0" + [[package]] name = "strsim" version = "0.8.0" @@ -1013,6 +1771,12 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601" +[[package]] +name = "sval" +version = "1.0.0-alpha.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45f6ee7c7b87caf59549e9fe45d6a69c75c8019e79e212a835c5da0e92f0ba08" + [[package]] name = "syn" version = "1.0.80" @@ -1036,20 +1800,6 @@ dependencies = [ "unicode-xid", ] -[[package]] -name = "tempfile" -version = "3.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22" -dependencies = [ - "cfg-if 1.0.0", - "libc", - "rand 0.8.4", - "redox_syscall", - "remove_dir_all", - "winapi", -] - [[package]] name = "term_size" version = "0.3.2" @@ -1060,16 +1810,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "terminal_size" -version = "0.1.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "633c1a546cee861a1a6d0dc69ebeca693bf4296661ba7852b9d21d159e0506df" -dependencies = [ - "libc", - "winapi", -] - [[package]] name = "textwrap" version = "0.11.0" @@ -1080,6 +1820,47 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "thiserror" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tide" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c459573f0dd2cc734b539047f57489ea875af8ee950860ded20cf93a79a1dee0" +dependencies = [ + "async-h1", + "async-sse", + "async-std", + "async-trait", + "futures-util", + "http-client", + "http-types", + "kv-log-macro", + "log", + "pin-project-lite 0.2.7", + "route-recognizer", + "serde", + "serde_json", +] + [[package]] name = "time" version = "0.1.43" @@ -1091,63 +1872,57 @@ dependencies = [ ] [[package]] -name = "tokio" -version = "1.12.0" +name = "time" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2c2416fdedca8443ae44b4527de1ea633af61d8f7169ffa6e72c5b53d24efcc" +checksum = "4752a97f8eebd6854ff91f1c1824cd6160626ac4bd44287f7f4ea2035a02a242" dependencies = [ - "autocfg", + "const_fn", "libc", - "mio", - "once_cell", - "pin-project-lite", - "signal-hook-registry", - "tokio-macros", + "standback", + "stdweb", + "time-macros", + "version_check", "winapi", ] [[package]] -name = "tokio-macros" -version = "1.5.0" +name = "time-macros" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2dd85aeaba7b68df939bd357c6afb36c87951be9e80bf9c859f2fc3e9fca0fd" +checksum = "957e9c6e26f12cb6d0dd7fc776bb67a706312e7299aed74c8dd5b17ebb27e2f1" dependencies = [ + "proc-macro-hack", + "time-macros-impl", +] + +[[package]] +name = "time-macros-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3c141a1b43194f3f56a1411225df8646c55781d5f26db825b3d98507eb482f" +dependencies = [ + "proc-macro-hack", "proc-macro2", "quote", + "standback", "syn", ] [[package]] -name = "tower-service" -version = "0.3.1" +name = "tinyvec" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" - -[[package]] -name = "tracing" -version = "0.1.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "f83b2a3d4d9091d0abd7eba4dc2710b1718583bd4d8992e2190720ea38f391f7" dependencies = [ - "cfg-if 1.0.0", - "pin-project-lite", - "tracing-core", + "tinyvec_macros", ] [[package]] -name = "tracing-core" -version = "0.1.21" +name = "tinyvec_macros" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "try-lock" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59547bce71d9c38b83d9c0e92b6066c4253371f15005def0c30d9657f50c7642" +checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "twox-hash" @@ -1165,6 +1940,21 @@ version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b63708a265f51345575b27fe43f9500ad611579e764c79edbc2037b1121959ec" +[[package]] +name = "unicode-bidi" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f" + +[[package]] +name = "unicode-normalization" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d54590932941a9e9266f0832deed84ebe1bf2e4c9e4a3554d393d18f5e854bf9" +dependencies = [ + "tinyvec", +] + [[package]] name = "unicode-width" version = "0.1.9" @@ -1177,12 +1967,52 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "universal-hash" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05" +dependencies = [ + "generic-array", + "subtle", +] + +[[package]] +name = "url" +version = "2.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a507c383b2d33b5fc35d1861e77e6b383d158b2da5e14fe51b83dfedf6fd578c" +dependencies = [ + "form_urlencoded", + "idna", + "matches", + "percent-encoding", + "serde", +] + [[package]] name = "urlencoding" version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68b90931029ab9b034b300b797048cf23723400aa757e8a2bfb9d748102f9821" +[[package]] +name = "value-bag" +version = "1.0.0-alpha.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79923f7731dc61ebfba3633098bf3ac533bbd35ccd8c57e7088d9a5eebe0263f" +dependencies = [ + "ctor", + "sval", + "version_check", +] + +[[package]] +name = "vcell" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77439c1b53d2303b20d9459b1ade71a83c716e3f9c34f3228c00e6f185d6c002" + [[package]] name = "vcpkg" version = "0.2.15" @@ -1202,15 +2032,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe" [[package]] -name = "want" -version = "0.3.0" +name = "void" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ce8a968cb1cd110d136ff8b819a556d6fb6d919363c61534f6860c7eb172ba0" +checksum = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" + +[[package]] +name = "volatile-register" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ee8f19f9d74293faf70901bc20ad067dc1ad390d2cbf1e3f75f721ffee908b6" dependencies = [ - "log", - "try-lock", + "vcell", ] +[[package]] +name = "waker-fn" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d5b2c62b4012a3e1eca5a7e077d13b3bf498c4073e33ccd58626607748ceeca" + [[package]] name = "wasi" version = "0.9.0+wasi-snapshot-preview1" @@ -1223,6 +2064,91 @@ version = "0.10.2+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6" +[[package]] +name = "wasm-bindgen" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce" +dependencies = [ + "cfg-if 1.0.0", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b" +dependencies = [ + "bumpalo", + "lazy_static", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8d7523cb1f2a4c96c1317ca690031b714a51cc14e05f712446691f413f5d39" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.78" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc" + +[[package]] +name = "web-sys" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wepoll-ffi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d743fdedc5c64377b5fc2bc036b01c7fd642205a0d96356034ae3404d49eb7fb" +dependencies = [ + "cc", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1293,7 +2219,9 @@ dependencies = [ "aes-gmac-siv", "ed25519-dalek", "gcrypt", + "heapless", "rand_core 0.5.1", + "subtle", "x25519-dalek", ] @@ -1319,19 +2247,16 @@ dependencies = [ "chrono", "clap", "colored", - "dialoguer", "digest_auth", - "futures", - "hyper", - "lazy_static", "libc", - "num-derive", - "num-traits", + "mach", "num_cpus", + "parking_lot", "serde", "serde_json", - "socket2 0.3.19", - "tokio", + "smol", + "tide", "winapi", + "zerotier-core-crypto", "zerotier-network-hypervisor", ] diff --git a/zerotier-system-service/Cargo.toml b/zerotier-system-service/Cargo.toml index 49bd3e122..719aaa832 100644 --- a/zerotier-system-service/Cargo.toml +++ b/zerotier-system-service/Cargo.toml @@ -13,24 +13,23 @@ panic = 'abort' [dependencies] zerotier-network-hypervisor = { path = "../zerotier-network-hypervisor" } -num_cpus = "^1" -tokio = { version = "^1", features = ["rt", "net", "time", "signal", "macros"] } -serde = { version = "^1", features = ["derive"] } -serde_json = "^1" -futures = "^0" +zerotier-core-crypto = { path = "../zerotier-core-crypto" } +serde = { version = "^1", features = ["derive"], default-features = false } +serde_json = { version = "^1", features = [], default-features = false } clap = { version = "^2", features = ["suggestions", "wrap_help"] } -chrono = "^0" -lazy_static = "^1" -num-traits = "^0" -num-derive = "^0" -hyper = { version = "^0", features = ["http1", "runtime", "server", "client", "tcp", "stream"] } -socket2 = { version = "^0", features = ["reuseport", "unix", "pair"] } -dialoguer = "^0" -digest_auth = "^0" colored = "^2" +num_cpus = "^1" +parking_lot = "^0" +smol = "^1" +tide = { version = "^0", features = ["h1-server"], default-features = false } +digest_auth = "^0" +chrono = "^0" [target."cfg(windows)".dependencies] winapi = { version = "^0", features = ["handleapi", "ws2ipdef", "ws2tcpip"] } [target."cfg(not(windows))".dependencies] libc = "^0" + +[target."cfg(any(target_os = \"macos\", target_os = \"ios\"))".dependencies] +mach = "^0" diff --git a/zerotier-system-service/src/api.rs b/zerotier-system-service/src/api.rs deleted file mode 100644 index f2952a47a..000000000 --- a/zerotier-system-service/src/api.rs +++ /dev/null @@ -1,41 +0,0 @@ -/* 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::Arc; - -use hyper::{Request, Body, StatusCode, Method}; - -use crate::service::Service; - -pub(crate) fn status(service: Arc, req: Request) -> (StatusCode, Body) { - if req.method() == Method::GET { - service.status().map_or_else(|| { - (StatusCode::SERVICE_UNAVAILABLE, Body::from("node shutdown in progress")) - }, |status| { - (StatusCode::OK, Body::from(serde_json::to_string(&status).unwrap())) - }) - } else { - (StatusCode::METHOD_NOT_ALLOWED, Body::from("/status allows method(s): GET")) - } -} - -pub(crate) fn config(service: Arc, req: Request) -> (StatusCode, Body) { - let config = service.local_config(); - if req.method() == Method::POST || req.method() == Method::PUT { - // TODO: diff config - } - (StatusCode::OK, Body::from(serde_json::to_string(config.as_ref()).unwrap())) -} - -pub(crate) fn peer(service: Arc, req: Request) -> (StatusCode, Body) { - (StatusCode::NOT_IMPLEMENTED, Body::from("")) -} - -pub(crate) fn network(service: Arc, req: Request) -> (StatusCode, Body) { - (StatusCode::NOT_IMPLEMENTED, Body::from("")) -} diff --git a/zerotier-system-service/src/commands/controller.rs b/zerotier-system-service/src/commands/controller.rs deleted file mode 100644 index 6479dbe46..000000000 --- a/zerotier-system-service/src/commands/controller.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* 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/ - */ - diff --git a/zerotier-system-service/src/commands/identity.rs b/zerotier-system-service/src/commands/identity.rs deleted file mode 100644 index 00c9643b4..000000000 --- a/zerotier-system-service/src/commands/identity.rs +++ /dev/null @@ -1,127 +0,0 @@ -/* 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 clap::ArgMatches; - -fn new_(cli_args: &ArgMatches) -> i32 { - let id_type = cli_args.value_of("type").map_or(IdentityType::Curve25519, |idt| { - match idt { - "p384" => IdentityType::NistP384, - _ => IdentityType::Curve25519, - } - }); - let id = Identity::new_generate(id_type); - if id.is_err() { - println!("ERROR: identity generation failed: {}", id.err().unwrap().to_str()); - return 1; - } - println!("{}", id.ok().unwrap().to_secret_string()); - 0 -} - -fn getpublic(cli_args: &ArgMatches) -> i32 { - let identity = crate::utils::read_identity(cli_args.value_of("identity").unwrap_or(""), false); - identity.map_or_else(|e| { - println!("ERROR: identity invalid: {}", e.to_string()); - 1 - }, |id| { - println!("{}", id.to_string()); - 0 - }) -} - -fn fingerprint(cli_args: &ArgMatches) -> i32 { - let identity = crate::utils::read_identity(cli_args.value_of("identity").unwrap_or(""), false); - identity.map_or_else(|e| { - println!("ERROR: identity invalid: {}", e.to_string()); - 1 - }, |id| { - println!("{}", id.fingerprint().to_string()); - 0 - }) -} - -fn validate(cli_args: &ArgMatches) -> i32 { - crate::utils::read_identity(cli_args.value_of("identity").unwrap_or(""), false).map_or_else(|e| { - println!("FAILED"); - 1 - }, |id| { - if id.locally_validate() { - println!("OK"); - 0 - } else { - println!("FAILED"); - 1 - } - }) -} - -fn sign(cli_args: &ArgMatches) -> i32 { - crate::utils::read_identity(cli_args.value_of("identity").unwrap_or(""), false).map_or_else(|e| { - println!("ERROR: invalid or unreadable identity: {}", e.as_str()); - 1 - }, |id| { - if id.has_private() { - std::fs::read(cli_args.value_of("path").unwrap()).map_or_else(|e| { - println!("ERROR: unable to read file: {}", e.to_string()); - 1 - }, |data| { - id.sign(data.as_slice()).map_or_else(|e| { - println!("ERROR: failed to sign: {}", e.to_str()); - 1 - }, |sig| { - println!("{}", hex::encode(sig.as_ref())); - 0 - }) - }) - } else { - println!("ERROR: identity must include secret key to sign."); - 1 - } - }) -} - -fn verify(cli_args: &ArgMatches) -> i32 { - crate::utils::read_identity(cli_args.value_of("identity").unwrap_or(""), false).map_or_else(|e| { - println!("ERROR: invalid or unreadable identity: {}", e.as_str()); - 1 - }, |id| { - std::fs::read(cli_args.value_of("path").unwrap()).map_or_else(|e| { - println!("ERROR: unable to read file: {}", e.to_string()); - 1 - }, |data| { - hex::decode(cli_args.value_of("signature").unwrap()).map_or_else(|e| { - println!("FAILED"); - 1 - }, |sig| { - if id.verify(data.as_slice(), sig.as_slice()) { - println!("OK"); - 0 - } else { - println!("FAILED"); - 1 - } - }) - }) - }) -} - -pub(crate) fn run<'a>(cli_args: &ArgMatches<'a>) -> i32 { - match cli_args.subcommand() { - ("new", Some(sub_cli_args)) => new_(sub_cli_args), - ("getpublic", Some(sub_cli_args)) => getpublic(sub_cli_args), - ("fingerprint", Some(sub_cli_args)) => fingerprint(sub_cli_args), - ("validate", Some(sub_cli_args)) => validate(sub_cli_args), - ("sign", Some(sub_cli_args)) => sign(sub_cli_args), - ("verify", Some(sub_cli_args)) => verify(sub_cli_args), - _ => { - crate::print_help(true); - 1 - } - } -} diff --git a/zerotier-system-service/src/commands/join.rs b/zerotier-system-service/src/commands/join.rs deleted file mode 100644 index 6479dbe46..000000000 --- a/zerotier-system-service/src/commands/join.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* 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/ - */ - diff --git a/zerotier-system-service/src/commands/leave.rs b/zerotier-system-service/src/commands/leave.rs deleted file mode 100644 index 6479dbe46..000000000 --- a/zerotier-system-service/src/commands/leave.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* 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/ - */ - diff --git a/zerotier-system-service/src/commands/mod.rs b/zerotier-system-service/src/commands/mod.rs deleted file mode 100644 index 3c5953152..000000000 --- a/zerotier-system-service/src/commands/mod.rs +++ /dev/null @@ -1,16 +0,0 @@ -/* 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/ - */ - -pub(crate) mod status; -pub(crate) mod set; -pub(crate) mod peer; -pub(crate) mod network; -pub(crate) mod join; -pub(crate) mod leave; -pub(crate) mod controller; -pub(crate) mod identity; diff --git a/zerotier-system-service/src/commands/network.rs b/zerotier-system-service/src/commands/network.rs deleted file mode 100644 index 6479dbe46..000000000 --- a/zerotier-system-service/src/commands/network.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* 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/ - */ - diff --git a/zerotier-system-service/src/commands/peer.rs b/zerotier-system-service/src/commands/peer.rs deleted file mode 100644 index 6479dbe46..000000000 --- a/zerotier-system-service/src/commands/peer.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* 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/ - */ - diff --git a/zerotier-system-service/src/commands/set.rs b/zerotier-system-service/src/commands/set.rs deleted file mode 100644 index 6479dbe46..000000000 --- a/zerotier-system-service/src/commands/set.rs +++ /dev/null @@ -1,8 +0,0 @@ -/* 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/ - */ - diff --git a/zerotier-system-service/src/commands/status.rs b/zerotier-system-service/src/commands/status.rs deleted file mode 100644 index 492f80681..000000000 --- a/zerotier-system-service/src/commands/status.rs +++ /dev/null @@ -1,46 +0,0 @@ -/* 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::error::Error; -use std::sync::Arc; - -use hyper::{Uri, Method, StatusCode}; -use colored::*; - -use crate::store::Store; -use crate::httpclient::*; -use crate::service::ServiceStatus; -use crate::{GlobalFlags, HTTP_API_OBJECT_SIZE_LIMIT}; - -pub(crate) async fn run(store: Arc, global_flags: GlobalFlags, client: HttpClient, api_base_uri: Uri, auth_token: String) -> Result> { - let uri = append_uri_path(api_base_uri, "/status").unwrap(); - let mut res = request(&client, Method::GET, uri, None, auth_token.as_str()).await?; - - match res.status() { - StatusCode::OK => { - let status = read_object_limited::(res.body_mut(), HTTP_API_OBJECT_SIZE_LIMIT).await?; - - if global_flags.json_output { - println!("{}", serde_json::to_string_pretty(&status).unwrap()) - } else { - println!("address {} version {} status {}", - status.address.to_string().as_str().bright_white(), - status.version.as_str().bright_white(), - if status.online { - "ONLINE".bright_green() - } else { - "OFFLINE".bright_red() - }); - // TODO: print more detailed status information - } - - Ok(0) - }, - _ => Err(Box::new(UnexpectedStatusCodeError(res.status(), ""))) - } -} diff --git a/zerotier-system-service/src/fastudpsocket.rs b/zerotier-system-service/src/fastudpsocket.rs index 82f2dcfbc..78778c776 100644 --- a/zerotier-system-service/src/fastudpsocket.rs +++ b/zerotier-system-service/src/fastudpsocket.rs @@ -12,7 +12,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use num_traits::cast::AsPrimitive; -use zerotier_network_hypervisor::vl1::{InetAddress, PacketBuffer, PacketBufferPool}; +use zerotier_network_hypervisor::vl1::InetAddress; +use zerotier_network_hypervisor::{PacketBuffer, PacketBufferPool}; /* * This is a threaded UDP socket listener for high performance. The fastest way to receive UDP @@ -280,9 +281,8 @@ impl Drop for FastUDPSocket { mod tests { use std::sync::atomic::{AtomicU32, Ordering}; - use zerotier_network_hypervisor::vl1::{PacketBuffer, PacketBufferPool, PacketBufferFactory}; - use crate::fastudpsocket::*; + use zerotier_network_hypervisor::{PacketBufferPool, PacketBufferFactory, PacketBuffer}; #[test] fn test_udp_bind_and_transfer() { diff --git a/zerotier-system-service/src/httpclient.rs b/zerotier-system-service/src/httpclient.rs deleted file mode 100644 index bafca963b..000000000 --- a/zerotier-system-service/src/httpclient.rs +++ /dev/null @@ -1,207 +0,0 @@ -/* 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::error::Error; -use std::future::Future; -use std::rc::Rc; -use std::str::FromStr; -use std::sync::Arc; -use std::time::Duration; - -use futures::stream::StreamExt; -use hyper::{Body, Method, Request, Response, StatusCode, Uri}; -use hyper::http::uri::{Authority, PathAndQuery, Scheme}; -use serde::de::DeserializeOwned; - -use crate::GlobalFlags; -use crate::store::Store; - -pub(crate) type HttpClient = Rc>; - -#[derive(Debug)] -pub(crate) struct IncorrectAuthTokenError; - -impl Error for IncorrectAuthTokenError {} - -impl std::fmt::Display for IncorrectAuthTokenError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!(f, "401 UNAUTHORIZED (incorrect authorization token or not allowed to read token)") - } -} - -#[derive(Debug)] -pub(crate) struct UnexpectedStatusCodeError(pub StatusCode, pub &'static str); - -impl Error for UnexpectedStatusCodeError {} - -impl std::fmt::Display for UnexpectedStatusCodeError { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - if self.1.is_empty() { - write!(f, "{} {} (???)", self.0.as_str(), self.0.canonical_reason().unwrap_or("???")) - } else { - write!(f, "{} {} ({})", self.0.as_str(), self.0.canonical_reason().unwrap_or("???"), self.1) - } - } -} - -/// Launch the supplied function with a ready to go HTTP client, the auth token, and the API URI. -/// This is boilerplate code for CLI commands that invoke the HTTP API. Since it instantiates and -/// then kills a tokio runtime, it's not for use in the service code that runs in a long-running -/// tokio runtime. -pub(crate) fn run_command< - R: Future>>, - F: FnOnce(Arc, GlobalFlags, HttpClient, Uri, String) -> R ->(store: Arc, global_flags: GlobalFlags, func: F) -> i32 { - let rt = tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap(); - let code = rt.block_on(async move { - let uri = store.load_uri(); - if uri.is_err() { - println!("ERROR: 'zerotier.uri' not found in '{}', unable to get service API endpoint.", store.base_path.to_str().unwrap()); - 1 - } else { - let auth_token = store.auth_token(false); - if auth_token.is_err() { - println!("ERROR: unable to read API authorization token from '{}': {}", store.base_path.to_str().unwrap(), auth_token.err().unwrap().to_string()); - 1 - } else { - let uri = uri.unwrap(); - let uri_str = uri.to_string(); - func(store, global_flags, Rc::new(hyper::Client::builder().http1_max_buf_size(65536).build_http()), uri, auth_token.unwrap()).await.map_or_else(|e| { - println!("ERROR: service API HTTP request ({}) failed: {}", uri_str, e); - println!(); - println!("Common causes: service is not running, authorization token incorrect"); - println!("or not readable, or a local firewall is blocking loopback connections."); - 1 - }, |code| { - code - }) - } - } - }); - rt.shutdown_timeout(Duration::from_millis(1)); // all tasks should be done in a command anyway, this is just a sanity check - code -} - -/// Send a request to the API with support for HTTP digest authentication. -/// The data option is for PUT and POST requests. For GET it is ignored. This will try to -/// authenticate if a WWW-Authorized header is sent in an unauthorized response. If authentication -/// with auth_token fails, IncorrectAuthTokenError is returned as an error. If the request is -/// unauthorizred and no WWW-Authorired header is present, a normal response is returned. The -/// caller must always check the response status code. -pub(crate) async fn request(client: &HttpClient, method: Method, uri: Uri, data: Option<&[u8]>, auth_token: &str) -> Result, Box> { - let body: Vec = data.map_or_else(|| Vec::new(), |data| data.to_vec()); - - let req = Request::builder().method(&method).version(hyper::Version::HTTP_11).uri(&uri).body(Body::from(body.clone())); - if req.is_err() { - return Err(Box::new(req.err().unwrap())); - } - let res = client.request(req.unwrap()).await; - if res.is_err() { - return Err(Box::new(res.err().unwrap())); - } - let res = res.unwrap(); - - if res.status() == StatusCode::UNAUTHORIZED { - let auth = res.headers().get(hyper::header::WWW_AUTHENTICATE); - if auth.is_none() { - return Ok(res); - } - let auth = auth.unwrap().to_str(); - if auth.is_err() { - return Err(Box::new(auth.err().unwrap())); - } - let auth = digest_auth::parse(auth.unwrap()); - if auth.is_err() { - return Err(Box::new(auth.err().unwrap())); - } - let ac = digest_auth::AuthContext::new_with_method("", auth_token, uri.to_string(), None::<&[u8]>, match method { - Method::GET => digest_auth::HttpMethod::GET, - Method::POST => digest_auth::HttpMethod::POST, - Method::HEAD => digest_auth::HttpMethod::HEAD, - Method::PUT => digest_auth::HttpMethod::OTHER("PUT"), - Method::DELETE => digest_auth::HttpMethod::OTHER("DELETE"), - _ => digest_auth::HttpMethod::OTHER(""), - }); - let auth = auth.unwrap().respond(&ac); - if auth.is_err() { - return Err(Box::new(auth.err().unwrap())); - } - - let req = Request::builder().method(&method).version(hyper::Version::HTTP_11).uri(&uri).header(hyper::header::AUTHORIZATION, auth.unwrap().to_header_string()).body(Body::from(body)); - if req.is_err() { - return Err(Box::new(req.err().unwrap())); - } - let res = client.request(req.unwrap()).await; - if res.is_err() { - return Err(Box::new(res.err().unwrap())); - } - let res = res.unwrap(); - - if res.status() == StatusCode::UNAUTHORIZED { - return Err(Box::new(IncorrectAuthTokenError)); - } - - return Ok(res); - } - - return Ok(res); -} - -/// Append to a URI path, returning None on error or a new Uri. -pub(crate) fn append_uri_path(uri: Uri, new_path: &str) -> Option { - let parts = uri.into_parts(); - let mut path = parts.path_and_query.map_or_else(|| String::new(), |pq| pq.to_string()); - while path.ends_with("/") { - let _ = path.pop(); - } - path.push_str(new_path); - let path = PathAndQuery::from_str(path.as_str()); - if path.is_err() { - None - } else { - Uri::builder() - .scheme(parts.scheme.unwrap_or(Scheme::HTTP)) - .authority(parts.authority.unwrap_or(Authority::from_static("127.0.0.1"))) - .path_and_query(path.unwrap()) - .build() - .map_or_else(|_| None, |uri| Some(uri)) - } -} - -/// Read HTTP body with a size limit. -pub(crate) async fn read_body_limited(body: &mut Body, max_size: usize) -> Result, Box> { - let mut data: Vec = Vec::new(); - loop { - let blk = body.next().await; - if blk.is_some() { - let blk = blk.unwrap(); - if blk.is_err() { - return Err(Box::new(blk.err().unwrap())); - } - for b in blk.unwrap().iter() { - data.push(*b); - if data.len() >= max_size { - return Ok(data); - } - } - } else { - break; - } - } - Ok(data) -} - -pub(crate) async fn read_object_limited(body: &mut Body, max_size: usize) -> Result> { - let data = read_body_limited(body, max_size).await?; - let obj = serde_json::from_slice(data.as_slice()); - if obj.is_err() { - Err(Box::new(obj.err().unwrap())) - } else { - Ok(obj.unwrap()) - } -} diff --git a/zerotier-system-service/src/httplistener.rs b/zerotier-system-service/src/httplistener.rs deleted file mode 100644 index ef358464d..000000000 --- a/zerotier-system-service/src/httplistener.rs +++ /dev/null @@ -1,200 +0,0 @@ -/* 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::cell::Cell; -use std::convert::Infallible; -use std::net::SocketAddr; -#[cfg(target_os = "linux")] -use std::os::unix::io::AsRawFd; -use std::sync::Arc; - -use digest_auth::{AuthContext, AuthorizationHeader, Charset, WwwAuthenticateHeader}; -use hyper::{Body, Method, Request, Response, StatusCode}; -use hyper::server::Server; -use hyper::service::{make_service_fn, service_fn}; -use tokio::task::JoinHandle; - -use crate::api; -use crate::service::Service; -use crate::utils::{create_http_auth_nonce, decrypt_http_auth_nonce, ms_since_epoch}; - -const HTTP_MAX_NONCE_AGE_MS: i64 = 30000; - -/// Listener for http connections to the API or for TCP P2P. -/// Dropping a listener initiates shutdown of the background hyper Server instance, -/// but it might not shut down instantly as this occurs asynchronously. -pub(crate) struct HttpListener { - pub address: SocketAddr, - shutdown_tx: Cell>>, - server: JoinHandle>, -} - -async fn http_handler(service: Arc, req: Request) -> Result, Infallible> { - let req_path = req.uri().path(); - - let mut authorized = false; - let mut stale = false; - - let auth_token = service.store().auth_token(false); - if auth_token.is_err() { - return Ok(Response::builder().status(StatusCode::INTERNAL_SERVER_ERROR).body(Body::from("authorization token unreadable")).unwrap()); - } - let auth_context = AuthContext::new_with_method("", auth_token.unwrap(), req.uri().to_string(), None::<&[u8]>, match *req.method() { - Method::GET => digest_auth::HttpMethod::GET, - Method::POST => digest_auth::HttpMethod::POST, - Method::HEAD => digest_auth::HttpMethod::HEAD, - Method::PUT => digest_auth::HttpMethod::OTHER("PUT"), - Method::DELETE => digest_auth::HttpMethod::OTHER("DELETE"), - _ => { - return Ok(Response::builder().status(StatusCode::METHOD_NOT_ALLOWED).body(Body::from("unrecognized method")).unwrap()); - } - }); - - let auth_header = req.headers().get(hyper::header::AUTHORIZATION); - if auth_header.is_some() { - let auth_header = AuthorizationHeader::parse(auth_header.unwrap().to_str().unwrap_or("")); - if auth_header.is_err() { - return Ok(Response::builder().status(StatusCode::BAD_REQUEST).body(Body::from(format!("invalid authorization header: {}", auth_header.err().unwrap().to_string()))).unwrap()); - } - let auth_header = auth_header.unwrap(); - - let mut expected = AuthorizationHeader { - realm: "zerotier-service-api".to_owned(), - nonce: auth_header.nonce.clone(), - opaque: None, - userhash: false, - algorithm: digest_auth::Algorithm::new(digest_auth::AlgorithmType::SHA2_512_256, false), - response: String::new(), - username: String::new(), - uri: req.uri().to_string(), - qop: Some(digest_auth::Qop::AUTH), - cnonce: auth_header.cnonce.clone(), - nc: auth_header.nc, - }; - expected.digest(&auth_context); - if auth_header.response == expected.response { - if (ms_since_epoch() - decrypt_http_auth_nonce(auth_header.nonce.as_str())) <= HTTP_MAX_NONCE_AGE_MS { - authorized = true; - } else { - stale = true; - } - } - } - - if authorized { - let (status, body) = - if req_path == "/status" { - api::status(service, req) - } else if req_path == "/config" { - api::config(service, req) - } else if req_path.starts_with("/peer") { - api::peer(service, req) - } else if req_path.starts_with("/network") { - api::network(service, req) - } else if req_path.starts_with("/controller") { - (StatusCode::NOT_IMPLEMENTED, Body::from("not implemented yet")) - } else if req_path == "/teapot" { - (StatusCode::IM_A_TEAPOT, Body::from("I'm a little teapot short and stout!")) - } else { - (StatusCode::NOT_FOUND, Body::from("not found")) - }; - Ok(Response::builder().header("Content-Type", "application/json").status(status).body(body).unwrap()) - } else { - Ok(Response::builder().header(hyper::header::WWW_AUTHENTICATE, WwwAuthenticateHeader { - domain: None, - realm: "zerotier-service-api".to_owned(), - nonce: create_http_auth_nonce(ms_since_epoch()), - opaque: None, - stale, - algorithm: digest_auth::Algorithm::new(digest_auth::AlgorithmType::SHA2_512_256, false), - qop: Some(vec![digest_auth::Qop::AUTH]), - userhash: false, - charset: Charset::ASCII, - nc: 0, - }.to_string()).status(StatusCode::UNAUTHORIZED).body(Body::empty()).unwrap()) - } -} - -impl HttpListener { - /// Create a new "background" TCP WebListener using the current tokio reactor async runtime. - pub async fn new(_device_name: &str, address: SocketAddr, service: &Arc) -> Result> { - let listener = if address.is_ipv4() { - let listener = socket2::Socket::new(socket2::Domain::ipv4(), socket2::Type::stream(), Some(socket2::Protocol::tcp())); - if listener.is_err() { - return Err(Box::new(listener.err().unwrap())); - } - let listener = listener.unwrap(); - #[cfg(unix)] { - let _ = listener.set_reuse_port(true); - } - listener - } else { - let listener = socket2::Socket::new(socket2::Domain::ipv6(), socket2::Type::stream(), Some(socket2::Protocol::tcp())); - if listener.is_err() { - return Err(Box::new(listener.err().unwrap())); - } - let listener = listener.unwrap(); - #[cfg(unix)] { - let _ = listener.set_reuse_port(true); - } - let _ = listener.set_only_v6(true); - listener - }; - - #[cfg(target_os = "linux")] { - if !_device_name.is_empty() { - let sock = listener.as_raw_fd(); - unsafe { - let _ = std::ffi::CString::new(_device_name).map(|dn| { - let dnb = dn.as_bytes_with_nul(); - let _ = libc::setsockopt(sock as std::os::raw::c_int, libc::SOL_SOCKET as std::os::raw::c_int, libc::SO_BINDTODEVICE as std::os::raw::c_int, dnb.as_ptr().cast(), (dnb.len() - 1) as libc::socklen_t); - }); - } - } - } - - let addr = socket2::SockAddr::from(address); - if let Err(e) = listener.bind(&addr) { - return Err(Box::new(e)); - } - if let Err(e) = listener.listen(128) { - return Err(Box::new(e)); - } - let listener = listener.into_tcp_listener(); - - let builder = Server::from_tcp(listener); - if builder.is_err() { - return Err(Box::new(builder.err().unwrap())); - } - let builder = builder.unwrap().http1_half_close(false).http1_keepalive(true).http1_max_buf_size(131072); - - let (shutdown_tx, shutdown_rx) = tokio::sync::oneshot::channel::<()>(); - let service = service.clone(); - let server = tokio::task::spawn(builder.serve(make_service_fn(move |_| { - let service = service.clone(); - async move { - Ok::<_, Infallible>(service_fn(move |req: Request| http_handler(service.clone(), req))) - } - })).with_graceful_shutdown(async { let _ = shutdown_rx.await; })); - - Ok(HttpListener { - address, - shutdown_tx: Cell::new(Some(shutdown_tx)), - server, - }) - } -} - -impl Drop for HttpListener { - fn drop(&mut self) { - let _ = self.shutdown_tx.take().map(|tx| { - let _ = tx.send(()); - self.server.abort(); - }); - } -} diff --git a/zerotier-system-service/src/log.rs b/zerotier-system-service/src/log.rs index e37f4dd9b..bf291e244 100644 --- a/zerotier-system-service/src/log.rs +++ b/zerotier-system-service/src/log.rs @@ -7,10 +7,13 @@ */ use std::fs::{File, OpenOptions}; -use std::io::{Seek, SeekFrom, Write, stderr}; -use std::sync::Mutex; +use std::io::{Seek, SeekFrom, stderr, Write}; +use std::sync::Arc; -struct LogIntl { +use parking_lot::Mutex; + +/// It's big it's heavy it's wood. +pub struct Log { prefix: String, path: String, file: Option, @@ -20,53 +23,51 @@ struct LogIntl { debug: bool, } -/// It's big it's heavy it's wood. -pub(crate) struct Log { - inner: Mutex, -} - impl Log { + /// Minimum "maximum size" parameter. const MIN_MAX_SIZE: usize = 1024; /// Construct a new logger. + /// /// If path is empty logs will not be written to files. If log_to_stderr is also /// false then no logs will be output at all. - pub fn new(path: &str, max_size: usize, log_to_stderr: bool, debug: bool, prefix: &str) -> Log { + /// + /// This returns an Arc> suitable for use with the l! and d! macros, which + /// expect a mutex guarded instance. + pub fn new(path: &str, max_size: usize, log_to_stderr: bool, debug: bool, prefix: &str) -> Arc> { let mut p = String::from(prefix); if !p.is_empty() { p.push(' '); } - Log{ - inner: Mutex::new(LogIntl { - prefix: p, - path: String::from(path), - file: None, - cur_size: 0, - max_size: if max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { max_size }, - log_to_stderr, - debug, - }), - } + Arc::new(Mutex::new(Log{ + prefix: p, + path: String::from(path), + file: None, + cur_size: 0, + max_size: max_size.max(Self::MIN_MAX_SIZE), + log_to_stderr, + debug, + })) } - pub fn set_max_size(&self, new_max_size: usize) { - self.inner.lock().unwrap().max_size = if new_max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { new_max_size }; + pub fn set_max_size(&mut self, new_max_size: usize) { + self.max_size = if new_max_size < Log::MIN_MAX_SIZE { Log::MIN_MAX_SIZE } else { new_max_size }; } - pub fn set_log_to_stderr(&self, log_to_stderr: bool) { - self.inner.lock().unwrap().log_to_stderr = log_to_stderr; + pub fn set_log_to_stderr(&mut self, log_to_stderr: bool) { + self.log_to_stderr = log_to_stderr; } - pub fn set_debug(&self, debug: bool) { - self.inner.lock().unwrap().debug = debug; + pub fn set_debug(&mut self, debug: bool) { + self.debug = debug; } - fn log_internal(&self, l: &mut LogIntl, s: &str, pfx: &'static str) { + fn log_internal(&mut self, pfx: &str, s: &str) { if !s.is_empty() { let log_line = format!("{}[{}] {}{}\n", l.prefix.as_str(), chrono::Local::now().format("%Y-%m-%d %H:%M:%S").to_string(), pfx, s); - if !l.path.is_empty() { - if l.file.is_none() { - let f = OpenOptions::new().read(true).write(true).create(true).open(l.path.as_str()); + if !self.path.is_empty() { + if self.file.is_none() { + let f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str()); if f.is_err() { return; } @@ -75,60 +76,57 @@ impl Log { if eof.is_err() { return; } - l.cur_size = eof.unwrap(); - l.file = Some(f); + self.cur_size = eof.unwrap(); + self.file = Some(f); } - if l.max_size > 0 && l.cur_size > l.max_size as u64 { - l.file = None; - l.cur_size = 0; + if self.max_size > 0 && self.cur_size > self.max_size as u64 { + self.file = None; + self.cur_size = 0; - let mut old_path = l.path.clone(); + let mut old_path = self.path.clone(); old_path.push_str(".old"); let _ = std::fs::remove_file(old_path.as_str()); - let _ = std::fs::rename(l.path.as_str(), old_path.as_str()); - let _ = std::fs::remove_file(l.path.as_str()); // should fail + let _ = std::fs::rename(self.path.as_str(), old_path.as_str()); + let _ = std::fs::remove_file(self.path.as_str()); // should fail - let f = OpenOptions::new().read(true).write(true).create(true).open(l.path.as_str()); + let f = OpenOptions::new().read(true).write(true).create(true).open(self.path.as_str()); if f.is_err() { return; } - l.file = Some(f.unwrap()); + self.file = Some(f.unwrap()); } - let f = l.file.as_mut().unwrap(); + let f = self.file.as_mut().unwrap(); let e = f.write_all(log_line.as_bytes()); if e.is_err() { eprintln!("ERROR: I/O error writing to log: {}", e.err().unwrap().to_string()); - l.file = None; + self.file = None; } else { let _ = f.flush(); - l.cur_size += log_line.len() as u64; + self.cur_size += log_line.len() as u64; } } - if l.log_to_stderr { + if self.log_to_stderr { let _ = stderr().write_all(log_line.as_bytes()); } } } - pub fn log>(&self, s: S) { - let mut l = self.inner.lock().unwrap(); - self.log_internal(&mut (*l), s.as_ref(), ""); + pub fn log>(&mut self, s: S) { + self.log_internal("", s.as_ref()); } - pub fn debug>(&self, s: S) { - let mut l = self.inner.lock().unwrap(); - if l.debug { - self.log_internal(&mut (*l), s.as_ref(), "DEBUG: "); + pub fn debug>(&mut self, s: S) { + if self.debug { + self.log_internal("DEBUG: ", s.as_ref()); } } - pub fn fatal>(&self, s: S) { - let mut l = self.inner.lock().unwrap(); + pub fn fatal>(&mut self, s: S) { let ss = s.as_ref(); - self.log_internal(&mut (*l), ss, "FATAL: "); + self.log_internal("FATAL: ", ss); eprintln!("FATAL: {}", ss); } } @@ -136,19 +134,17 @@ impl Log { #[macro_export] macro_rules! l( ($logger:expr, $($arg:tt)*) => { - $logger.log(format!($($arg)*)) + $logger.lock().log(format!($($arg)*)) } ); #[macro_export] macro_rules! d( ($logger:expr, $($arg:tt)*) => { - $logger.debug(format!($($arg)*)) + $logger.lock().debug(format!($($arg)*)) } ); -unsafe impl Sync for Log {} - /* #[cfg(test)] mod tests { diff --git a/zerotier-system-service/src/main.rs b/zerotier-system-service/src/main.rs index eb03de539..43a94b35a 100644 --- a/zerotier-system-service/src/main.rs +++ b/zerotier-system-service/src/main.rs @@ -6,33 +6,26 @@ * https://www.zerotier.com/ */ -mod api; -mod commands; mod fastudpsocket; mod localconfig; mod getifaddrs; #[macro_use] mod log; mod store; -mod network; mod vnic; mod service; mod utils; -mod httplistener; -mod httpclient; use std::io::Write; -use std::sync::Arc; use std::str::FromStr; use clap::{App, Arg, ArgMatches, ErrorKind}; -use crate::store::Store; +use zerotier_network_hypervisor::{VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION}; pub const HTTP_API_OBJECT_SIZE_LIMIT: usize = 131072; fn make_help(long_help: bool) -> String { - let ver = zerotier_core::version(); format!(r###"ZeroTier Network Hypervisor Service Version {}.{}.{} (c)2013-2021 ZeroTier, Inc. Licensed under the ZeroTier BSL (see LICENSE.txt) @@ -82,12 +75,13 @@ Common Operations: · join Join a virtual network · leave Leave a virtual network {}"###, - ver.0, ver.1, ver.2, if long_help { + VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, + if long_help { r###" Advanced Operations: - service Start node - (usually run by service manager) + service Start local service + (usually not invoked manually) controller [option] · list List networks on controller @@ -112,59 +106,17 @@ Advanced Operations: } else { "" }) } -pub(crate) fn print_help(long_help: bool) { +pub fn print_help(long_help: bool) { let h = make_help(long_help); let _ = std::io::stdout().write_all(h.as_bytes()); } -pub(crate) fn parse_bool(v: &str) -> Result { - if !v.is_empty() { - match v.chars().next().unwrap() { - 'y' | 'Y' | '1' | 't' | 'T' => { return Ok(true); } - 'n' | 'N' | '0' | 'f' | 'F' => { return Ok(false); } - _ => {} - } - } - Err(format!("invalid boolean value: '{}'", v)) -} - -#[inline(always)] -fn is_valid_bool(v: String) -> Result<(), String> { - parse_bool(v.as_str()).map(|_| ()) -} - -fn is_valid_port(v: String) -> Result<(), String> { - let i = u16::from_str(v.as_str()).unwrap_or(0); - if i >= 1 { - return Ok(()); - } - Err(format!("invalid TCP/IP port number: {}", v)) -} - -fn make_store(cli_args: &ArgMatches) -> Arc { - let zerotier_path = cli_args.value_of("path").map_or_else(|| unsafe { zerotier_core::cstr_to_string(osdep::platformDefaultHomePath(), -1) }, |ztp| ztp.to_string()); - let store = Store::new(zerotier_path.as_str(), cli_args.value_of("token_path").map_or(None, |tp| Some(tp.to_string())), cli_args.value_of("token").map_or(None, |tok| Some(tok.trim().to_string()))); - if store.is_err() { - eprintln!("FATAL: error accessing directory '{}': {}", zerotier_path, store.err().unwrap().to_string()); - std::process::exit(1); - } - Arc::new(store.unwrap()) -} - -#[derive(Clone)] -pub(crate) struct GlobalFlags { +pub struct GlobalCommandLineFlags { pub json_output: bool, } -#[inline(always)] -fn get_global_flags(cli_args: &ArgMatches) -> GlobalFlags { - GlobalFlags { - json_output: cli_args.is_present("json") - } -} - fn main() { - let cli_args = { + let cli_args = Box::new({ let help = make_help(false); let args = App::new("zerotier") .arg(Arg::with_name("json").short("j")) @@ -176,18 +128,18 @@ fn main() { .subcommand(App::new("status")) .subcommand(App::new("set") .subcommand(App::new("port") - .arg(Arg::with_name("port#").index(1).validator(is_valid_port))) + .arg(Arg::with_name("port#").index(1).validator(utils::is_valid_port))) .subcommand(App::new("secondaryport") - .arg(Arg::with_name("port#").index(1).validator(is_valid_port))) + .arg(Arg::with_name("port#").index(1).validator(utils::is_valid_port))) .subcommand(App::new("blacklist") .subcommand(App::new("cidr") .arg(Arg::with_name("ip_bits").index(1)) - .arg(Arg::with_name("boolean").index(2).validator(is_valid_bool))) + .arg(Arg::with_name("boolean").index(2).validator(utils::is_valid_bool))) .subcommand(App::new("if") .arg(Arg::with_name("prefix").index(1)) - .arg(Arg::with_name("boolean").index(2).validator(is_valid_bool)))) + .arg(Arg::with_name("boolean").index(2).validator(utils::is_valid_bool)))) .subcommand(App::new("portmap") - .arg(Arg::with_name("boolean").index(1).validator(is_valid_bool)))) + .arg(Arg::with_name("boolean").index(1).validator(utils::is_valid_bool)))) .subcommand(App::new("peer") .subcommand(App::new("show") .arg(Arg::with_name("address").index(1).required(true))) @@ -252,6 +204,10 @@ fn main() { std::process::exit(1); } args + }); + + let global_cli_flags = GlobalCommandLineFlags { + json_output: cli_args.is_present("json") }; std::process::exit({ @@ -264,28 +220,23 @@ fn main() { print_help(true); 0 } - ("oldhelp", None) => { - // TODO - 0 - } + ("oldhelp", None) => todo!(), ("version", None) => { - let ver = zerotier_core::version(); - println!("{}.{}.{}", ver.0, ver.1, ver.2); + println!("{}.{}.{}", VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION); 0 } - ("status", None) => crate::httpclient::run_command(make_store(&cli_args), get_global_flags(&cli_args), crate::commands::status::run), - ("set", Some(sub_cli_args)) => { 0 } - ("peer", Some(sub_cli_args)) => { 0 } - ("network", Some(sub_cli_args)) => { 0 } - ("join", Some(sub_cli_args)) => { 0 } - ("leave", Some(sub_cli_args)) => { 0 } + ("status", None) => todo!(), + ("set", Some(sub_cli_args)) => todo!(), + ("peer", Some(sub_cli_args)) => todo!(), + ("network", Some(sub_cli_args)) => todo!(), + ("join", Some(sub_cli_args)) => todo!(), + ("leave", Some(sub_cli_args)) => todo!(), ("service", None) => { - let store = make_store(&cli_args); drop(cli_args); // free no longer needed memory before entering service - service::run(store) + service::run() } - ("controller", Some(sub_cli_args)) => { 0 } - ("identity", Some(sub_cli_args)) => crate::commands::identity::run(sub_cli_args), + ("controller", Some(sub_cli_args)) => todo!(), + ("identity", Some(sub_cli_args)) => todo!(), _ => { print_help(false); 1 diff --git a/zerotier-system-service/src/network.rs b/zerotier-system-service/src/network.rs deleted file mode 100644 index 043cd7a34..000000000 --- a/zerotier-system-service/src/network.rs +++ /dev/null @@ -1,12 +0,0 @@ -/* 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/ - */ - -pub struct Network {} - -impl Network { -} diff --git a/zerotier-system-service/src/service.rs b/zerotier-system-service/src/service.rs index efc564233..49a3e9209 100644 --- a/zerotier-system-service/src/service.rs +++ b/zerotier-system-service/src/service.rs @@ -6,6 +6,72 @@ * https://www.zerotier.com/ */ +use std::sync::Arc; + +use parking_lot::Mutex; + +use zerotier_network_hypervisor::{CallerInterface, Node}; +use zerotier_network_hypervisor::vl1::{Endpoint, Identity, VL1CallerInterface}; + +use crate::log::Log; +use crate::utils::{ms_monotonic, ms_since_epoch}; + +struct Service { + pub log: Arc>, + node_ptr: Arc>, +} + +impl Service { + pub fn node(&self) -> &Node { + // The node_ref option is only None during initial service startup since there is a + // chicken or egg problem. Use of this accessor during startup will panic and is a bug. + (*self.node_ptr).as_ref().unwrap() + } +} + +impl VL1CallerInterface for Service { + fn event_node_is_up(&self) {} + + fn event_node_is_down(&self) {} + + fn event_identity_collision(&self) {} + + fn event_online_status_change(&self, online: bool) {} + + fn event_user_message(&self, source: &Identity, message_type: u64, message: &[u8]) {} + + fn load_node_identity(&self) -> Option<&[u8]> { + todo!() + } + + fn save_node_identity(&self, id: &Identity, public: &[u8], secret: &[u8]) {} + + fn wire_send(&self, endpoint: &Endpoint, local_socket: Option, local_interface: Option, data: &[&[u8]], packet_ttl: u8) -> bool { + todo!() + } + + fn check_path(&self, id: &Identity, endpoint: &Endpoint, local_socket: Option, local_interface: Option) -> bool { + true + } + + fn get_path_hints(&self, id: &Identity) -> Option<&[(&Endpoint, Option, Option)]> { + todo!() + } + + #[inline(always)] + fn time_ticks(&self) -> i64 { ms_monotonic() } + + #[inline(always)] + fn time_clock(&self) -> i64 { ms_since_epoch() } +} + +impl CallerInterface for Service {} + +pub fn run() -> i32 { + 0 +} + +/* use std::cell::Cell; use std::collections::BTreeMap; use std::net::{SocketAddr, Ipv4Addr, IpAddr, Ipv6Addr}; @@ -14,6 +80,8 @@ use std::sync::atomic::{AtomicBool, Ordering}; use std::time::Duration; use zerotier_network_hypervisor::vl1::{Address, Identity, InetAddress, MAC, PacketBuffer}; +use zerotier_network_hypervisor::vl1::inetaddress::IpScope; +use zerotier_network_hypervisor::{CallerInterface, Node}; use futures::StreamExt; use serde::{Serialize, Deserialize}; @@ -26,10 +94,7 @@ use crate::network::Network; use crate::store::Store; use crate::utils::{ms_since_epoch, ms_monotonic}; use crate::httplistener::HttpListener; -use zerotier_network_hypervisor::vl1::inetaddress::IpScope; -/// How often to check for major configuration changes. This shouldn't happen -/// too often since it uses a bit of CPU. const CONFIG_CHECK_INTERVAL: i64 = 5000; /// ServiceStatus is the object returned by the API /status endpoint @@ -64,7 +129,7 @@ pub struct ServiceStatus { /// Core ZeroTier service, which is sort of just a container for all the things. pub(crate) struct Service { pub(crate) log: Log, - _node: Cell, Network, Service>>>, // never modified after node is created + node: Option, udp_local_endpoints: Mutex>, http_local_endpoints: Mutex>, interrupt: Mutex>, @@ -76,101 +141,6 @@ pub(crate) struct Service { online: AtomicBool, } -impl NodeEventHandler for Service { - #[inline(always)] - fn virtual_network_config(&self, network_id: NetworkId, network_obj: &Network, config_op: VirtualNetworkConfigOperation, config: Option<&VirtualNetworkConfig>) {} - - #[inline(always)] - fn virtual_network_frame(&self, network_id: NetworkId, network_obj: &Network, source_mac: MAC, dest_mac: MAC, ethertype: u16, vlan_id: u16, data: &[u8]) {} - - #[inline(always)] - fn event(&self, event: Event, event_data: &[u8]) { - match event { - Event::Up => { - d!(self.log, "node startup event received."); - } - - Event::Down => { - d!(self.log, "node shutdown event received."); - self.online.store(false, Ordering::Relaxed); - } - - Event::Online => { - d!(self.log, "node is online."); - self.online.store(true, Ordering::Relaxed); - } - - Event::Offline => { - d!(self.log, "node is offline."); - self.online.store(false, Ordering::Relaxed); - } - - Event::Trace => { - if !event_data.is_empty() { - let _ = Dictionary::new_from_bytes(event_data).map(|tm| { - let tm = TraceEvent::parse_message(&tm); - let _ = tm.map(|tm: TraceEvent| { - let local_config = self.local_config(); - if match tm.layer() { - TraceEventLayer::VL1 => local_config.settings.log.vl1, - TraceEventLayer::VL2 => local_config.settings.log.vl2, - TraceEventLayer::VL2Filter => local_config.settings.log.vl2_trace_rules, - TraceEventLayer::VL2Multicast => local_config.settings.log.vl2_trace_multicast, - _ => true, - } { - self.log.log(tm.to_string()); - } - }); - }); - } - } - - Event::UserMessage => {} - } - } - - #[inline(always)] - fn state_put(&self, obj_type: StateObjectType, obj_id: &[u64], obj_data: &[u8]) -> std::io::Result<()> { - if !obj_data.is_empty() { - self.store.store_object(&obj_type, obj_id, obj_data) - } else { - self.store.erase_object(&obj_type, obj_id); - Ok(()) - } - } - - #[inline(always)] - fn state_get(&self, obj_type: StateObjectType, obj_id: &[u64]) -> std::io::Result> { - self.store.load_object(&obj_type, obj_id) - } - - #[inline(always)] - fn wire_packet_send(&self, local_socket: i64, sock_addr: &InetAddress, data: &[u8], packet_ttl: u32) -> i32 { - 0 - } - - #[inline(always)] - fn path_check(&self, _: Address, _: &Identity, _: i64, _: &InetAddress) -> bool { - true - } - - #[inline(always)] - fn path_lookup(&self, address: Address, id: &Identity, desired_family: InetAddressFamily) -> Option { - let lc = self.local_config(); - lc.virtual_.get(&address).map_or(None, |c: &LocalConfigVirtualConfig| { - if c.try_.is_empty() { - None - } else { - let t = c.try_.get((zerotier_core::random() as usize) % c.try_.len()); - t.map_or(None, |v: &InetAddress| { - d!(self.log, "path lookup for {} returned {}", address.to_string(), v.to_string()); - Some(v.clone()) - }) - } - }) - } -} - impl Service { pub fn local_config(&self) -> Arc { self.local_config.lock().unwrap().clone() @@ -180,15 +150,6 @@ impl Service { *(self.local_config.lock().unwrap()) = Arc::new(new_lc); } - /// Get the node running with this service. - /// This can return None during shutdown because Service holds a weak - /// reference to Node to avoid circular Arc<> pointers. This will only - /// return None during shutdown, in which case whatever is happening - /// should abort as quietly as possible. - pub fn node(&self) -> Option, Network, Service>>> { - unsafe { &*self._node.as_ptr() }.upgrade() - } - #[inline(always)] pub fn store(&self) -> &Arc { &self.store @@ -252,7 +213,7 @@ async fn run_async(store: Arc, local_config: Arc) -> i32 { local_config.settings.log.debug, "", ), - _node: Cell::new(Weak::new()), + node: None, udp_local_endpoints: Mutex::new(Vec::new()), http_local_endpoints: Mutex::new(Vec::new()), interrupt: Mutex::new(interrupt_tx), @@ -509,3 +470,5 @@ pub(crate) fn run(store: Arc) -> i32 { process_exit_value } + +*/ diff --git a/zerotier-system-service/src/utils.rs b/zerotier-system-service/src/utils.rs index 8f59255cd..4b37a753a 100644 --- a/zerotier-system-service/src/utils.rs +++ b/zerotier-system-service/src/utils.rs @@ -10,34 +10,63 @@ use std::borrow::Borrow; use std::fs::File; use std::io::Read; use std::path::Path; +use std::str::FromStr; -use serde::Serialize; use serde::de::DeserializeOwned; +use serde::Serialize; use zerotier_network_hypervisor::vl1::Identity; use crate::osdep; -#[inline(always)] -pub(crate) fn ms_since_epoch() -> i64 { - unsafe { osdep::msSinceEpoch() } +pub fn ms_since_epoch() -> i64 { + std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_millis() as i64 } -#[inline(always)] -pub(crate) fn ms_monotonic() -> i64 { - unsafe { osdep::msMonotonic() } +#[cfg(any(target_os = "macos", target_os = "ios"))] +pub fn ms_monotonic() -> i64 { + unsafe { + let mut tb: mach::mach_time::mach_timebase_info_data_t = std::mem::zeroed(); + if mach::mach_time::mach_timebase_info(&mut tb) == 0 { + let mt = mach::mach_time::mach_continuous_approximate_time(); + (((mt as u128) * tb.numer as u128 * 1000000_u128) / (tb.denom as u128)) as i64 + } else { + panic!("FATAL: mach_timebase_info() failed"); + } + } +} + +pub fn parse_bool(v: &str) -> Result { + if !v.is_empty() { + match v.chars().next().unwrap() { + 'y' | 'Y' | '1' | 't' | 'T' => { return Ok(true); } + 'n' | 'N' | '0' | 'f' | 'F' => { return Ok(false); } + _ => {} + } + } + Err(format!("invalid boolean value: '{}'", v)) +} + +pub fn is_valid_bool(v: String) -> Result<(), String> { parse_bool(v.as_str()).map(|_| ()) } + +pub fn is_valid_port(v: String) -> Result<(), String> { + let i = u16::from_str(v.as_str()).unwrap_or(0); + if i >= 1 { + return Ok(()); + } + Err(format!("invalid TCP/IP port number: {}", v)) } /// Convenience function to read up to limit bytes from a file. /// If the file is larger than limit, the excess is not read. -pub(crate) fn read_limit>(path: P, limit: usize) -> std::io::Result> { +pub fn read_limit>(path: P, limit: usize) -> std::io::Result> { let mut v: Vec = Vec::new(); let _ = File::open(path)?.take(limit as u64).read_to_end(&mut v)?; Ok(v) } /// Read an identity as either a literal or from a file. -pub(crate) fn read_identity(input: &str, validate: bool) -> Result { +pub fn parse_cli_identity(input: &str, validate: bool) -> Result { let parse_func = |s: &str| { Identity::new_from_string(s).map_or_else(|e| { Err(format!("invalid identity: {}", e.to_str())) @@ -64,35 +93,11 @@ pub(crate) fn read_identity(input: &str, validate: bool) -> Result Result { - let parse_func = |s: &str| { - Locator::new_from_string(s).map_or_else(|e| { - Err(format!("invalid locator: {}", e.to_str())) - }, |loc| { - Ok(loc) - }) - }; - if Path::new(input).exists() { - read_limit(input, 16384).map_or_else(|e| { - Err(e.to_string()) - }, |v| { - String::from_utf8(v).map_or_else(|e| { - Err(e.to_string()) - }, |s| { - parse_func(s.as_str()) - }) - }) - } else { - parse_func(input) - } -} - /// Create a new HTTP authorization nonce by encrypting the current time. /// The key used to encrypt the current time is random and is re-created for /// each execution of the process. By decrypting this nonce when it is returned, /// the client and server may check the age of a digest auth exchange. -pub(crate) fn create_http_auth_nonce(timestamp: i64) -> String { +pub fn create_http_auth_nonce(timestamp: i64) -> String { let mut nonce_plaintext: [u64; 2] = [timestamp as u64, timestamp as u64]; unsafe { osdep::encryptHttpAuthNonce(nonce_plaintext.as_mut_ptr().cast()); @@ -102,7 +107,7 @@ pub(crate) fn create_http_auth_nonce(timestamp: i64) -> String { /// Decrypt HTTP auth nonce encrypted by this process and return the timestamp. /// This returns zero if the input was not valid. -pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> i64 { +pub fn decrypt_http_auth_nonce(nonce: &str) -> i64 { let nonce = hex::decode(nonce.trim()); if !nonce.is_err() { let mut nonce = nonce.unwrap(); @@ -120,12 +125,12 @@ pub(crate) fn decrypt_http_auth_nonce(nonce: &str) -> i64 { } /// Shortcut to use serde_json to serialize an object, returns "null" on error. -pub(crate) fn to_json(o: &O) -> String { +pub fn to_json(o: &O) -> String { serde_json::to_string(o).unwrap_or("null".into()) } /// Shortcut to use serde_json to serialize an object, returns "null" on error. -pub(crate) fn to_json_pretty(o: &O) -> String { +pub fn to_json_pretty(o: &O) -> String { serde_json::to_string_pretty(o).unwrap_or("null".into()) } @@ -135,7 +140,7 @@ pub(crate) fn to_json_pretty(o: &O) -> String { /// If the source tries to set an object to something other than another object, this is ignored. /// Other fields are replaced. This is used for RESTful config object updates. The depth limit /// field is to prevent stack overflows via the API. -pub(crate) fn json_patch(target: &mut serde_json::value::Value, source: &serde_json::value::Value, depth_limit: usize) { +pub fn json_patch(target: &mut serde_json::value::Value, source: &serde_json::value::Value, depth_limit: usize) { if target.is_object() { if source.is_object() { let mut target = target.as_object_mut().unwrap(); @@ -161,7 +166,7 @@ pub(crate) fn json_patch(target: &mut serde_json::value::Value, source: &serde_j /// Patch a serializable object with the fields present in a JSON object. /// If there are no changes, None is returned. The depth limit is passed through to json_patch and /// should be set to a sanity check value to prevent overflows. -pub(crate) fn json_patch_object(obj: O, patch: &str, depth_limit: usize) -> Result, serde_json::Error> { +pub fn json_patch_object(obj: O, patch: &str, depth_limit: usize) -> Result, serde_json::Error> { serde_json::from_str::(patch).map_or_else(|e| Err(e), |patch| { serde_json::value::to_value(obj.borrow()).map_or_else(|e| Err(e), |mut obj_value| { json_patch(&mut obj_value, &patch, depth_limit);