mirror of
https://github.com/zerotier/ZeroTierOne.git
synced 2025-07-26 04:02:50 +02:00
A lot of service work, and incorporate fork of https://github.com/etairi/sidh-rs into zerotier-core-crypto.
This commit is contained in:
parent
e732c3569c
commit
91493713a1
41 changed files with 4817 additions and 1258 deletions
|
@ -5,11 +5,14 @@ edition = "2018"
|
|||
license = "MPL-2.0"
|
||||
authors = ["ZeroTier, Inc. <contact@zerotier.com>", "Adam Ierymenko <adam.ierymenko@zerotier.com>"]
|
||||
|
||||
[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"
|
||||
|
|
|
@ -46,7 +46,7 @@ pub fn hash<const SPACE_COST: usize, const TIME_COST: usize, const DELTA: usize>
|
|||
|
||||
/* 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;
|
||||
|
|
|
@ -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;
|
||||
|
|
25
zerotier-core-crypto/src/sidh/constants.rs
Normal file
25
zerotier-core-crypto/src/sidh/constants.rs
Normal file
|
@ -0,0 +1,25 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
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]) };
|
1057
zerotier-core-crypto/src/sidh/curve.rs
Normal file
1057
zerotier-core-crypto/src/sidh/curve.rs
Normal file
File diff suppressed because it is too large
Load diff
781
zerotier-core-crypto/src/sidh/field.rs
Normal file
781
zerotier-core-crypto/src/sidh/field.rs
Normal file
|
@ -0,0 +1,781 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
//! 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);
|
||||
}
|
||||
}
|
460
zerotier-core-crypto/src/sidh/fp.rs
Normal file
460
zerotier-core-crypto/src/sidh/fp.rs
Normal file
|
@ -0,0 +1,460 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
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::<u32>() as u32;
|
||||
let mask_low = <u32>::max_value() >> (sizeof_u32 * 4);
|
||||
let mask_high = <u32>::max_value() << (sizeof_u32 * 4);
|
||||
|
||||
let al = a & mask_low;
|
||||
let 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]);
|
384
zerotier-core-crypto/src/sidh/isogeny.rs
Normal file
384
zerotier-core-crypto/src/sidh/isogeny.rs
Normal file
|
@ -0,0 +1,384 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
//! 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)/<P_3> = 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)/<P_4>`.
|
||||
///
|
||||
/// 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.<x> = 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.<x> = 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.<x> = 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());
|
||||
}
|
||||
}
|
26
zerotier-core-crypto/src/sidh/mod.rs
Normal file
26
zerotier-core-crypto/src/sidh/mod.rs
Normal file
|
@ -0,0 +1,26 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
#[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;
|
674
zerotier-core-crypto/src/sidh/sidh.rs
Normal file
674
zerotier-core-crypto/src/sidh/sidh.rs
Normal file
|
@ -0,0 +1,674 @@
|
|||
// This file is part of sidh-rs.
|
||||
// Copyright (c) 2017 Erkan Tairi
|
||||
// See LICENSE for licensing information.
|
||||
//
|
||||
// Author:
|
||||
// - Erkan Tairi <erkan.tairi@gmail.com>
|
||||
//
|
||||
|
||||
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<ProjectivePoint, MAX_INT_POINTS_ALICE> = Vec::new();
|
||||
let mut indices: Vec<usize, MAX_INT_POINTS_ALICE> = 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<ProjectivePoint, MAX_INT_POINTS_ALICE> = Vec::new();
|
||||
let mut indices: Vec<usize, MAX_INT_POINTS_ALICE> = 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<ProjectivePoint, MAX_INT_POINTS_BOB> = Vec::new();
|
||||
let mut indices: Vec<usize, MAX_INT_POINTS_BOB> = 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<ProjectivePoint, MAX_INT_POINTS_BOB> = Vec::new();
|
||||
let mut indices: Vec<usize, MAX_INT_POINTS_BOB> = 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<R: RngCore + CryptoRng>(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<R: RngCore + CryptoRng>(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);
|
||||
}
|
||||
}
|
|
@ -14,6 +14,17 @@ pub mod defaults;
|
|||
|
||||
mod node;
|
||||
|
||||
/// Standard packet buffer type including pool container.
|
||||
pub type PacketBuffer = crate::util::pool::Pooled<Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, 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<Buffer<{ crate::vl1::protocol::PACKET_SIZE_MAX }>, 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;
|
||||
|
|
|
@ -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: CallerInterface>(ci: &CI, auto_generate_identity_type: Option<crate::vl1::identity::Type>) -> Result<Node, InvalidParameterError> {
|
||||
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(),
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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<CI: VL1CallerInterface, PH: VL1PacketHandler>(&self, node: &Node, ci: &CI, ph: &PH, time_ticks: i64, source_path: &Arc<Path>, header: &PacketHeader, packet: &Buffer<{ PACKET_SIZE_MAX }>, fragments: &[Option<PacketBuffer>]) {
|
||||
pub(crate) fn receive<CI: VL1CallerInterface, PH: VL1PacketHandler>(&self, node: &VL1Node, ci: &CI, ph: &PH, time_ticks: i64, source_path: &Arc<Path>, header: &PacketHeader, packet: &Buffer<{ PACKET_SIZE_MAX }>, fragments: &[Option<PacketBuffer>]) {
|
||||
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<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, packet_id: PacketID, packet: &Buffer<{ PACKET_SIZE_MAX }>) -> bool {
|
||||
pub(crate) fn send<CI: VL1CallerInterface>(&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<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, explicit_endpoint: Option<Endpoint>) -> bool {
|
||||
pub(crate) fn send_hello<CI: VL1CallerInterface>(&self, ci: &CI, node: &VL1Node, explicit_endpoint: Option<Endpoint>) -> 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<CI: VL1CallerInterface>(&self, ct: &CI, time_ticks: i64) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_hello<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
fn receive_hello<CI: VL1CallerInterface>(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_error<CI: VL1CallerInterface, PH: VL1PacketHandler>(&self, ci: &CI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
fn receive_error<CI: VL1CallerInterface, PH: VL1PacketHandler>(&self, ci: &CI, ph: &PH, node: &VL1Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
let mut cursor: usize = 0;
|
||||
let _ = payload.read_struct::<message_component_structs::ErrorHeader>(&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<CI: VL1CallerInterface, PH: VL1PacketHandler>(&self, ci: &CI, ph: &PH, node: &Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
fn receive_ok<CI: VL1CallerInterface, PH: VL1PacketHandler>(&self, ci: &CI, ph: &PH, node: &VL1Node, time_ticks: i64, source_path: &Arc<Path>, forward_secrecy: bool, extended_authentication: bool, payload: &Buffer<{ PACKET_SIZE_MAX }>) {
|
||||
let mut cursor: usize = 0;
|
||||
let _ = payload.read_struct::<message_component_structs::OkHeader>(&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<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
fn receive_whois<CI: VL1CallerInterface>(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_rendezvous<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
fn receive_rendezvous<CI: VL1CallerInterface>(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_echo<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
fn receive_echo<CI: VL1CallerInterface>(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_push_direct_paths<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
fn receive_push_direct_paths<CI: VL1CallerInterface>(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
|
||||
#[inline(always)]
|
||||
fn receive_user_message<CI: VL1CallerInterface>(&self, ci: &CI, node: &Node, time_ticks: i64, source_path: &Arc<Path>, payload: &Buffer<{ PACKET_SIZE_MAX }>) {}
|
||||
fn receive_user_message<CI: VL1CallerInterface>(&self, ci: &CI, node: &VL1Node, time_ticks: i64, source_path: &Arc<Path>, 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<Arc<Path>> { 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<Arc<Path>> {
|
||||
pub fn path(&self, node: &VL1Node) -> Option<Arc<Path>> {
|
||||
self.direct_path().map_or_else(|| node.root().map_or(None, |root| root.direct_path().map_or(None, |bp| Some(bp))), |bp| Some(bp))
|
||||
}
|
||||
|
||||
|
|
|
@ -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<RootSet, InvalidParameterError> {
|
||||
pub fn create(roots: &[Root], timestamp: i64, oob_update_url: Option<&str>, name: Option<&str>, contact: Option<&str>, signing_key: &RootSetSecretSigningKey) -> Result<Self, InvalidParameterError> {
|
||||
let mut sorted_roots = roots.to_vec();
|
||||
sorted_roots.sort();
|
||||
sorted_roots.dedup();
|
||||
|
|
|
@ -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<Buffer<{ PACKET_SIZE_MAX }>, 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<Buffer<{ PACKET_SIZE_MAX }>, 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<BackgroundTaskIntervals>,
|
||||
|
@ -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 {}
|
|
@ -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<CI: VL1CallerInterface>(&self, node: &Node, ci: &CI, target: Address, packet: Option<QueuedPacket>) {
|
||||
pub fn query<CI: VL1CallerInterface>(&self, node: &VL1Node, ci: &CI, target: Address, packet: Option<QueuedPacket>) {
|
||||
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<CI: VL1CallerInterface>(&self, node: &Node, ci: &CI, time_ticks: i64) {
|
||||
pub fn call_every_interval<CI: VL1CallerInterface>(&self, node: &VL1Node, ci: &CI, time_ticks: i64) {
|
||||
let mut targets: Vec<Address> = Vec::new();
|
||||
self.0.lock().retain(|target, qi| {
|
||||
if qi.retry_count < WHOIS_RETRY_MAX {
|
||||
|
@ -81,7 +81,7 @@ impl WhoisQueue {
|
|||
}
|
||||
}
|
||||
|
||||
fn send_whois<CI: VL1CallerInterface>(&self, node: &Node, ci: &CI, targets: &[Address]) {
|
||||
fn send_whois<CI: VL1CallerInterface>(&self, node: &VL1Node, ci: &CI, targets: &[Address]) {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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};
|
||||
|
|
1359
zerotier-system-service/Cargo.lock
generated
1359
zerotier-system-service/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -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"
|
||||
|
|
|
@ -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<Service>, req: Request<Body>) -> (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<Service>, req: Request<Body>) -> (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<Service>, req: Request<Body>) -> (StatusCode, Body) {
|
||||
(StatusCode::NOT_IMPLEMENTED, Body::from(""))
|
||||
}
|
||||
|
||||
pub(crate) fn network(service: Arc<Service>, req: Request<Body>) -> (StatusCode, Body) {
|
||||
(StatusCode::NOT_IMPLEMENTED, Body::from(""))
|
||||
}
|
|
@ -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/
|
||||
*/
|
||||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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/
|
||||
*/
|
||||
|
|
@ -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/
|
||||
*/
|
||||
|
|
@ -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;
|
|
@ -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/
|
||||
*/
|
||||
|
|
@ -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/
|
||||
*/
|
||||
|
|
@ -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/
|
||||
*/
|
||||
|
|
@ -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<Store>, global_flags: GlobalFlags, client: HttpClient, api_base_uri: Uri, auth_token: String) -> Result<i32, Box<dyn Error>> {
|
||||
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::<ServiceStatus>(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(), "")))
|
||||
}
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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<hyper::Client<hyper::client::HttpConnector, Body>>;
|
||||
|
||||
#[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<Output = Result<i32, Box<dyn Error>>>,
|
||||
F: FnOnce(Arc<Store>, GlobalFlags, HttpClient, Uri, String) -> R
|
||||
>(store: Arc<Store>, 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<Response<Body>, Box<dyn Error>> {
|
||||
let body: Vec<u8> = 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<Uri> {
|
||||
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<Vec<u8>, Box<dyn Error>> {
|
||||
let mut data: Vec<u8> = 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<O: DeserializeOwned>(body: &mut Body, max_size: usize) -> Result<O, Box<dyn Error>> {
|
||||
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())
|
||||
}
|
||||
}
|
|
@ -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<Option<tokio::sync::oneshot::Sender<()>>>,
|
||||
server: JoinHandle<hyper::Result<()>>,
|
||||
}
|
||||
|
||||
async fn http_handler(service: Arc<Service>, req: Request<Body>) -> Result<Response<Body>, 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<Service>) -> Result<HttpListener, Box<dyn std::error::Error>> {
|
||||
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<Body>| 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();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -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<File>,
|
||||
|
@ -20,53 +23,51 @@ struct LogIntl {
|
|||
debug: bool,
|
||||
}
|
||||
|
||||
/// It's big it's heavy it's wood.
|
||||
pub(crate) struct Log {
|
||||
inner: Mutex<LogIntl>,
|
||||
}
|
||||
|
||||
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<Mutex<Log>> 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<Mutex<Log>> {
|
||||
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<S: AsRef<str>>(&self, s: S) {
|
||||
let mut l = self.inner.lock().unwrap();
|
||||
self.log_internal(&mut (*l), s.as_ref(), "");
|
||||
pub fn log<S: AsRef<str>>(&mut self, s: S) {
|
||||
self.log_internal("", s.as_ref());
|
||||
}
|
||||
|
||||
pub fn debug<S: AsRef<str>>(&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<S: AsRef<str>>(&mut self, s: S) {
|
||||
if self.debug {
|
||||
self.log_internal("DEBUG: ", s.as_ref());
|
||||
}
|
||||
}
|
||||
|
||||
pub fn fatal<S: AsRef<str>>(&self, s: S) {
|
||||
let mut l = self.inner.lock().unwrap();
|
||||
pub fn fatal<S: AsRef<str>>(&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 {
|
||||
|
|
|
@ -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 <network> Join a virtual network
|
||||
· leave <network> 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 <command> [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<bool, String> {
|
||||
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<Store> {
|
||||
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
|
||||
|
|
|
@ -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 {
|
||||
}
|
|
@ -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<Mutex<Log>>,
|
||||
node_ptr: Arc<Option<Node>>,
|
||||
}
|
||||
|
||||
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<i64>, local_interface: Option<i64>, data: &[&[u8]], packet_ttl: u8) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn check_path(&self, id: &Identity, endpoint: &Endpoint, local_socket: Option<i64>, local_interface: Option<i64>) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn get_path_hints(&self, id: &Identity) -> Option<&[(&Endpoint, Option<i64>, Option<i64>)]> {
|
||||
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<Weak<Node<Arc<Service>, Network, Service>>>, // never modified after node is created
|
||||
node: Option<Node>,
|
||||
udp_local_endpoints: Mutex<Vec<InetAddress>>,
|
||||
http_local_endpoints: Mutex<Vec<InetAddress>>,
|
||||
interrupt: Mutex<futures::channel::mpsc::Sender<()>>,
|
||||
|
@ -76,101 +141,6 @@ pub(crate) struct Service {
|
|||
online: AtomicBool,
|
||||
}
|
||||
|
||||
impl NodeEventHandler<Network> 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<Vec<u8>> {
|
||||
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<InetAddress> {
|
||||
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<LocalConfig> {
|
||||
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<Arc<Node<Arc<Service>, Network, Service>>> {
|
||||
unsafe { &*self._node.as_ptr() }.upgrade()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn store(&self) -> &Arc<Store> {
|
||||
&self.store
|
||||
|
@ -252,7 +213,7 @@ async fn run_async(store: Arc<Store>, local_config: Arc<LocalConfig>) -> 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<Store>) -> i32 {
|
|||
|
||||
process_exit_value
|
||||
}
|
||||
|
||||
*/
|
||||
|
|
|
@ -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<bool, String> {
|
||||
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<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
|
||||
pub fn read_limit<P: AsRef<Path>>(path: P, limit: usize) -> std::io::Result<Vec<u8>> {
|
||||
let mut v: Vec<u8> = 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<Identity, String> {
|
||||
pub fn parse_cli_identity(input: &str, validate: bool) -> Result<Identity, String> {
|
||||
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<Identity, Str
|
|||
}
|
||||
}
|
||||
|
||||
/// Read a locator as either a literal or from a file.
|
||||
pub(crate) fn read_locator(input: &str) -> Result<Locator, String> {
|
||||
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: serde::Serialize>(o: &O) -> String {
|
||||
pub fn to_json<O: serde::Serialize>(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: serde::Serialize>(o: &O) -> String {
|
||||
pub fn to_json_pretty<O: serde::Serialize>(o: &O) -> String {
|
||||
serde_json::to_string_pretty(o).unwrap_or("null".into())
|
||||
}
|
||||
|
||||
|
@ -135,7 +140,7 @@ pub(crate) fn to_json_pretty<O: serde::Serialize>(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<O: Serialize + DeserializeOwned + Eq>(obj: O, patch: &str, depth_limit: usize) -> Result<Option<O>, serde_json::Error> {
|
||||
pub fn json_patch_object<O: Serialize + DeserializeOwned + Eq>(obj: O, patch: &str, depth_limit: usize) -> Result<Option<O>, serde_json::Error> {
|
||||
serde_json::from_str::<serde_json::value::Value>(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);
|
||||
|
|
Loading…
Add table
Reference in a new issue