From 422a50c6b2332cb13a67406c57a58a8f51d941a1 Mon Sep 17 00:00:00 2001 From: moana Date: Tue, 10 Oct 2023 18:56:17 +0200 Subject: [PATCH 1/3] Move dusk modules to separate files --- src/dusk.rs | 285 +++++++++++++++++++++++++++++++++++++++++++++ src/fr.rs | 303 +----------------------------------------------- src/fr/dusk.rs | 305 ++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 308 +++---------------------------------------------- 4 files changed, 609 insertions(+), 592 deletions(-) create mode 100644 src/dusk.rs create mode 100644 src/fr/dusk.rs diff --git a/src/dusk.rs b/src/dusk.rs new file mode 100644 index 0000000..137e0b0 --- /dev/null +++ b/src/dusk.rs @@ -0,0 +1,285 @@ +// 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 http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +#[cfg(feature = "alloc")] +extern crate alloc; + +use core::ops::Mul; +use ff::Field; +use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption}; + +pub use dusk_bls12_381::BlsScalar; +use dusk_bytes::{Error as BytesError, Serializable}; + +use crate::{Fr, JubJubAffine, JubJubExtended, EDWARDS_D}; + +/// Compute a shared secret `secret · public` using DHKE protocol +pub fn dhke(secret: &Fr, public: &JubJubExtended) -> JubJubAffine { + public.mul(secret).into() +} + +/// Use a fixed generator point. +/// The point is then reduced according to the prime field. We need only to +/// state the coordinates, so users can exploit its properties +/// which are proven by tests, checking: +/// - It lies on the curve, +/// - Is of prime order, +/// - Is not the identity point. +/// Using: +/// x = 0x3fd2814c43ac65a6f1fbf02d0fd6cce62e3ebb21fd6c54ed4df7b7ffec7beaca +/// y = 0x0000000000000000000000000000000000000000000000000000000000000012 +pub const GENERATOR: JubJubAffine = JubJubAffine { + u: BlsScalar::from_raw([ + 0x4df7b7ffec7beaca, + 0x2e3ebb21fd6c54ed, + 0xf1fbf02d0fd6cce6, + 0x3fd2814c43ac65a6, + ]), + v: BlsScalar::from_raw([ + 0x0000000000000012, + 000000000000000000, + 000000000000000000, + 000000000000, + ]), +}; + +/// [`GENERATOR`] in [`JubJubExtended`] form +pub const GENERATOR_EXTENDED: JubJubExtended = JubJubExtended { + u: GENERATOR.u, + v: GENERATOR.v, + z: BlsScalar::one(), + t1: GENERATOR.u, + t2: GENERATOR.v, +}; + +/// GENERATOR NUMS which is obtained following the specs in: +/// https://app.gitbook.com/@dusk-network/s/specs/specifications/poseidon/pedersen-commitment-scheme +/// The counter = 18 and the hash function used to compute it was blake2b +/// Using: +/// x = 0x5e67b8f316f414f7bd9514c773fd4456931e316a39fe4541921710179df76377 +/// y = 0x43d80eb3b2f3eb1b7b162dbeeb3b34fd9949ba0f82a5507a6705b707162e3ef8 +pub const GENERATOR_NUMS: JubJubAffine = JubJubAffine { + u: BlsScalar::from_raw([ + 0x921710179df76377, + 0x931e316a39fe4541, + 0xbd9514c773fd4456, + 0x5e67b8f316f414f7, + ]), + v: BlsScalar::from_raw([ + 0x6705b707162e3ef8, + 0x9949ba0f82a5507a, + 0x7b162dbeeb3b34fd, + 0x43d80eb3b2f3eb1b, + ]), +}; + +/// [`GENERATOR_NUMS`] in [`JubJubExtended`] form +pub const GENERATOR_NUMS_EXTENDED: JubJubExtended = JubJubExtended { + u: GENERATOR_NUMS.u, + v: GENERATOR_NUMS.v, + z: BlsScalar::one(), + t1: GENERATOR_NUMS.u, + t2: GENERATOR_NUMS.v, +}; + +impl Serializable<32> for JubJubAffine { + type Error = BytesError; + + /// Attempts to interpret a byte representation of an + /// affine point, failing if the element is not on + /// the curve or non-canonical. + /// + /// NOTE: ZIP 216 is enabled by default and the only way to interact + /// with serialization. + /// See: for more details. + fn from_bytes(b: &[u8; Self::SIZE]) -> Result { + let mut b = b.clone(); + + // Grab the sign bit from the representation + let sign = b[31] >> 7; + + // Mask away the sign bit + b[31] &= 0b0111_1111; + + // Interpret what remains as the y-coordinate + let v = >::from_bytes(&b)?; + + // -x^2 + y^2 = 1 + d.x^2.y^2 + // -x^2 = 1 + d.x^2.y^2 - y^2 (rearrange) + // -x^2 - d.x^2.y^2 = 1 - y^2 (rearrange) + // x^2 + d.x^2.y^2 = y^2 - 1 (flip signs) + // x^2 (1 + d.y^2) = y^2 - 1 (factor) + // x^2 = (y^2 - 1) / (1 + d.y^2) (isolate x^2) + // We know that (1 + d.y^2) is nonzero for all y: + // (1 + d.y^2) = 0 + // d.y^2 = -1 + // y^2 = -(1 / d) No solutions, as -(1 / d) is not a square + + let v2 = v.square(); + + Option::from( + ((v2 - BlsScalar::one()) + * ((BlsScalar::one() + EDWARDS_D * &v2) + .invert() + .unwrap_or(BlsScalar::zero()))) + .sqrt() + .and_then(|u| { + // Fix the sign of `x` if necessary + let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); + let u = BlsScalar::conditional_select(&u, &-u, flip_sign); + // If x == 0, flip_sign == sign_bit. We therefore want to + // reject the encoding as non-canonical + // if all of the following occur: + // - x == 0 + // - flip_sign == true + let u_is_zero = u.ct_eq(&BlsScalar::zero()); + CtOption::new(JubJubAffine { u, v }, !(u_is_zero & flip_sign)) + }), + ) + .ok_or(BytesError::InvalidData) + } + + /// Converts this element into its byte representation. + fn to_bytes(&self) -> [u8; Self::SIZE] { + let mut tmp = self.v.to_bytes(); + let u = self.u.to_bytes(); + + // Encode the sign of the x-coordinate in the most + // significant bit. + tmp[31] |= u[0] << 7; + + tmp + } +} + +impl JubJubExtended { + /// Constructs an extended point (with `Z = 1`) from + /// an affine point using the map `(x, y) => (x, y, 1, x, y)`. + pub const fn from_affine(affine: JubJubAffine) -> Self { + Self::from_raw_unchecked( + affine.u, + affine.v, + BlsScalar::one(), + affine.u, + affine.v, + ) + } + + /// Constructs an extended point from its raw internals + pub const fn from_raw_unchecked( + u: BlsScalar, + v: BlsScalar, + z: BlsScalar, + t1: BlsScalar, + t2: BlsScalar, + ) -> Self { + Self { u, v, z, t1, t2 } + } + + /// Returns the `u`-coordinate of this point. + pub const fn get_u(&self) -> BlsScalar { + self.u + } + + /// Returns the `v`-coordinate of this point. + pub const fn get_v(&self) -> BlsScalar { + self.v + } + + /// Returns the `z`-coordinate of this point. + pub const fn get_z(&self) -> BlsScalar { + self.z + } + + /// Returns the `t1`-coordinate of this point. + pub const fn get_t1(&self) -> BlsScalar { + self.t1 + } + + /// Returns the `t2`-coordinate of this point. + pub const fn get_t2(&self) -> BlsScalar { + self.t2 + } + + /// Returns two scalars suitable for hashing that represent the + /// Extended Point. + pub fn to_hash_inputs(&self) -> [BlsScalar; 2] { + // The same JubJubAffine can have different JubJubExtended + // representations, therefore we convert from Extended to Affine + // before hashing, to ensure deterministic result + let p = JubJubAffine::from(self); + [p.u, p.v] + } +} + +#[test] +fn test_affine_point_generator_has_order_p() { + assert_eq!(GENERATOR.is_prime_order().unwrap_u8(), 1); +} + +#[test] +fn test_extended_point_generator_has_order_p() { + assert_eq!(GENERATOR_EXTENDED.is_prime_order().unwrap_u8(), 1); +} + +#[test] +fn test_affine_point_generator_nums_has_order_p() { + assert_eq!(GENERATOR_NUMS.is_prime_order().unwrap_u8(), 1); +} + +#[test] +fn test_affine_point_generator_is_not_identity() { + assert_ne!( + JubJubExtended::from(GENERATOR.mul_by_cofactor()), + JubJubExtended::identity() + ); +} + +#[test] +fn test_extended_point_generator_is_not_identity() { + assert_ne!( + GENERATOR_EXTENDED.mul_by_cofactor(), + JubJubExtended::identity() + ); +} + +#[test] +fn test_affine_point_generator_nums_is_not_identity() { + assert_ne!( + JubJubExtended::from(GENERATOR_NUMS.mul_by_cofactor()), + JubJubExtended::identity() + ); +} + +#[ignore] +#[test] +fn second_gen_nums() { + use blake2::{Blake2b, Digest}; + let generator_bytes = GENERATOR.to_bytes(); + let mut counter = 0u64; + let mut array = [0u8; 32]; + loop { + let mut hasher = Blake2b::new(); + hasher.update(generator_bytes); + hasher.update(counter.to_le_bytes()); + let res = hasher.finalize(); + array.copy_from_slice(&res[0..32]); + if >::from_bytes(&array).is_ok() + && >::from_bytes(&array) + .unwrap() + .is_prime_order() + .unwrap_u8() + == 1 + { + assert!( + GENERATOR_NUMS + == >::from_bytes(&array) + .unwrap() + ); + } + counter += 1; + } +} diff --git a/src/fr.rs b/src/fr.rs index d5f6e00..6b8540d 100644 --- a/src/fr.rs +++ b/src/fr.rs @@ -2,6 +2,8 @@ //! $\mathbb{F}_r$ where `r = //! 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7` +mod dusk; + use core::convert::TryInto; use core::fmt; use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign}; @@ -1265,304 +1267,3 @@ fn test_from_raw() { assert_eq!(Fr::from_raw([1, 0, 0, 0]), R); } - -// Dusk Network features -mod dusk { - use super::*; - - use core::cmp::{Ord, Ordering, PartialOrd}; - use core::ops::{Index, IndexMut}; - use dusk_bls12_381::BlsScalar; - - use dusk_bytes::{Error as BytesError, Serializable}; - - impl Fr { - /// Generate a valid Scalar choosen uniformly using user- - /// provided rng. - /// - /// By `rng` we mean any Rng that implements: `Rng` + `CryptoRng`. - pub fn random(rand: &mut T) -> Fr - where - T: RngCore, - { - let mut bytes = [0u8; 64]; - rand.fill_bytes(&mut bytes); - - Fr::from_bytes_wide(&bytes) - } - - /// SHR impl: shifts bits n times, equivalent to division by 2^n. - #[inline] - pub fn divn(&mut self, mut n: u32) { - if n >= 256 { - *self = Self::from(0u64); - return; - } - - while n >= 64 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - core::mem::swap(&mut t, i); - } - n -= 64; - } - - if n > 0 { - let mut t = 0; - for i in self.0.iter_mut().rev() { - let t2 = *i << (64 - n); - *i >>= n; - *i |= t; - t = t2; - } - } - } - - /// Reduces bit representation of numbers, such that - /// they can be evaluated in terms of the least significant bit. - pub fn reduce(&self) -> Self { - Fr::montgomery_reduce( - self.0[0], self.0[1], self.0[2], self.0[3], 0u64, 0u64, 0u64, - 0u64, - ) - } - - /// Evaluate if a `Scalar, from Fr` is even or not. - pub fn is_even(&self) -> bool { - self.0[0] % 2 == 0 - } - - /// Compute the result from `Scalar (mod 2^k)`. - /// - /// # Panics - /// - /// If the given k is > 32 (5 bits) as the value gets - /// greater than the limb. - pub fn mod_2_pow_k(&self, k: u8) -> u8 { - (self.0[0] & ((1 << k) - 1)) as u8 - } - - /// Compute the result from `Scalar (mods k)`. - /// - /// # Panics - /// - /// If the given `k > 32 (5 bits)` || `k == 0` as the value gets - /// greater than the limb. - pub fn mods_2_pow_k(&self, w: u8) -> i8 { - assert!(w < 32u8); - let modulus = self.mod_2_pow_k(w) as i8; - let two_pow_w_minus_one = 1i8 << (w - 1); - - match modulus >= two_pow_w_minus_one { - false => modulus, - true => modulus - ((1u8 << w) as i8), - } - } - - /// Computes the windowed-non-adjacent form for a given an element in - /// the JubJub Scalar field. - /// - /// The wnaf of a scalar is its breakdown: - /// scalar = sum_i{wnaf[i]*2^i} - /// where for all i: - /// -2^{w-1} < wnaf[i] < 2^{w-1} - /// and - /// wnaf[i] * wnaf[i+1] = 0 - pub fn compute_windowed_naf(&self, width: u8) -> [i8; 256] { - let mut k = self.reduce(); - let mut i = 0; - let one = Fr::one().reduce(); - let mut res = [0i8; 256]; - - while k >= one { - if !k.is_even() { - let ki = k.mods_2_pow_k(width); - res[i] = ki; - k = k - Fr::from(ki); - } else { - res[i] = 0i8; - }; - - k.divn(1u32); - i += 1; - } - res - } - } - - // TODO implement From for any integer type smaller than 128-bit - impl From for Fr { - // FIXME this could really be better if we removed the match - fn from(val: i8) -> Fr { - match (val >= 0, val < 0) { - (true, false) => Fr([val.abs() as u64, 0u64, 0u64, 0u64]), - (false, true) => -Fr([val.abs() as u64, 0u64, 0u64, 0u64]), - (_, _) => unreachable!(), - } - } - } - - impl From for BlsScalar { - fn from(scalar: Fr) -> BlsScalar { - let bls_scalar = - >::from_bytes(&scalar.to_bytes()); - - // The order of a JubJub's Scalar field is shorter than a BLS - // Scalar, so convert any jubjub scalar to a BLS' Scalar - // should always be safe. - assert!( - bls_scalar.is_ok(), - "Failed to convert a Scalar from JubJub to BLS" - ); - - bls_scalar.unwrap() - } - } - - impl Index for Fr { - type Output = u64; - fn index(&self, _index: usize) -> &u64 { - &(self.0[_index]) - } - } - - impl IndexMut for Fr { - fn index_mut(&mut self, _index: usize) -> &mut u64 { - &mut (self.0[_index]) - } - } - - impl PartialOrd for Fr { - fn partial_cmp(&self, other: &Fr) -> Option { - Some(self.cmp(&other)) - } - } - - impl Ord for Fr { - fn cmp(&self, other: &Self) -> Ordering { - let a = self; - let other = other; - for i in (0..4).rev() { - if a[i] > other[i] { - return Ordering::Greater; - } else if a[i] < other[i] { - return Ordering::Less; - } - } - Ordering::Equal - } - } - - impl Serializable<32> for Fr { - type Error = BytesError; - - /// Attempts to convert a little-endian byte representation of - /// a field element into an element of `Fr`, failing if the input - /// is not canonical (is not smaller than r). - fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result { - let mut tmp = Fr([0, 0, 0, 0]); - - tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); - tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); - tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); - tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); - - // Try to subtract the modulus - let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); - let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); - let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); - let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); - - // If the element is smaller than MODULUS then the - // subtraction will underflow, producing a borrow value - // of 0xffff...ffff. Otherwise, it'll be zero. - let is_some = (borrow as u8) & 1; - - if is_some == 0 { - return Err(BytesError::InvalidData); - } - - // Convert to Montgomery form by computing - // (a.R^0 * R^2) / R = a.R - tmp *= &R2; - - Ok(tmp) - } - - /// Converts an element of `Fr` into a byte representation in - /// little-endian byte order. - fn to_bytes(&self) -> [u8; Self::SIZE] { - // Turn into canonical form by computing - // (a.R) / R = a - let tmp = Fr::montgomery_reduce( - self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0, - ); - - let mut res = [0; Self::SIZE]; - res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); - res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); - res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); - res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); - - res - } - } - - #[test] - fn w_naf_3() { - let scalar = Fr::from(1122334455u64); - let w = 3; - // -1 - 1*2^3 - 1*2^8 - 1*2^11 + 3*2^15 + 1*2^18 - 1*2^21 + 3*2^24 + - // 1*2^30 - let expected_result = [ - -1i8, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 3, 0, 0, 1, 0, - 0, -1, 0, 0, 3, 0, 0, 0, 0, 0, 1, - ]; - - let mut expected = [0i8; 256]; - expected[..expected_result.len()].copy_from_slice(&expected_result); - - let computed = scalar.compute_windowed_naf(w); - - assert_eq!(expected, computed); - } - - #[test] - fn w_naf_4() { - let scalar = Fr::from(58235u64); - let w = 4; - // -5 + 7*2^7 + 7*2^13 - let expected_result = [-5, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 7]; - - let mut expected = [0i8; 256]; - expected[..expected_result.len()].copy_from_slice(&expected_result); - - let computed = scalar.compute_windowed_naf(w); - - assert_eq!(expected, computed); - } - - #[test] - fn w_naf_2() { - let scalar = -Fr::one(); - let w = 2; - let two = Fr::from(2u64); - - let wnaf = scalar.compute_windowed_naf(w); - - let recomputed = - wnaf.iter().enumerate().fold(Fr::zero(), |acc, (i, x)| { - if *x > 0 { - acc + Fr::from(*x as u64) - * two.pow(&[(i as u64), 0u64, 0u64, 0u64]) - } else if *x < 0 { - acc - Fr::from(-(*x) as u64) - * two.pow(&[(i as u64), 0u64, 0u64, 0u64]) - } else { - acc - } - }); - assert_eq!(scalar, recomputed); - } -} -pub use dusk::*; diff --git a/src/fr/dusk.rs b/src/fr/dusk.rs new file mode 100644 index 0000000..da7fe4d --- /dev/null +++ b/src/fr/dusk.rs @@ -0,0 +1,305 @@ +// 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 http://mozilla.org/MPL/2.0/. +// +// Copyright (c) DUSK NETWORK. All rights reserved. + +use core::convert::TryInto; + +use rand_core::RngCore; + +use crate::util::sbb; + +use core::cmp::{Ord, Ordering, PartialOrd}; +use core::ops::{Index, IndexMut}; +use dusk_bls12_381::BlsScalar; + +use dusk_bytes::{Error as BytesError, Serializable}; + +use super::{Fr, MODULUS, R2}; + +impl Fr { + /// Generate a valid Scalar choosen uniformly using user- + /// provided rng. + /// + /// By `rng` we mean any Rng that implements: `Rng` + `CryptoRng`. + pub fn random(rand: &mut T) -> Fr + where + T: RngCore, + { + let mut bytes = [0u8; 64]; + rand.fill_bytes(&mut bytes); + + Fr::from_bytes_wide(&bytes) + } + + /// SHR impl: shifts bits n times, equivalent to division by 2^n. + #[inline] + pub fn divn(&mut self, mut n: u32) { + if n >= 256 { + *self = Self::from(0u64); + return; + } + + while n >= 64 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + core::mem::swap(&mut t, i); + } + n -= 64; + } + + if n > 0 { + let mut t = 0; + for i in self.0.iter_mut().rev() { + let t2 = *i << (64 - n); + *i >>= n; + *i |= t; + t = t2; + } + } + } + + /// Reduces bit representation of numbers, such that + /// they can be evaluated in terms of the least significant bit. + pub fn reduce(&self) -> Self { + Fr::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], 0u64, 0u64, 0u64, 0u64, + ) + } + + /// Evaluate if a `Scalar, from Fr` is even or not. + pub fn is_even(&self) -> bool { + self.0[0] % 2 == 0 + } + + /// Compute the result from `Scalar (mod 2^k)`. + /// + /// # Panics + /// + /// If the given k is > 32 (5 bits) as the value gets + /// greater than the limb. + pub fn mod_2_pow_k(&self, k: u8) -> u8 { + (self.0[0] & ((1 << k) - 1)) as u8 + } + + /// Compute the result from `Scalar (mods k)`. + /// + /// # Panics + /// + /// If the given `k > 32 (5 bits)` || `k == 0` as the value gets + /// greater than the limb. + pub fn mods_2_pow_k(&self, w: u8) -> i8 { + assert!(w < 32u8); + let modulus = self.mod_2_pow_k(w) as i8; + let two_pow_w_minus_one = 1i8 << (w - 1); + + match modulus >= two_pow_w_minus_one { + false => modulus, + true => modulus - ((1u8 << w) as i8), + } + } + + /// Computes the windowed-non-adjacent form for a given an element in + /// the JubJub Scalar field. + /// + /// The wnaf of a scalar is its breakdown: + /// scalar = sum_i{wnaf[i]*2^i} + /// where for all i: + /// -2^{w-1} < wnaf[i] < 2^{w-1} + /// and + /// wnaf[i] * wnaf[i+1] = 0 + pub fn compute_windowed_naf(&self, width: u8) -> [i8; 256] { + let mut k = self.reduce(); + let mut i = 0; + let one = Fr::one().reduce(); + let mut res = [0i8; 256]; + + while k >= one { + if !k.is_even() { + let ki = k.mods_2_pow_k(width); + res[i] = ki; + k = k - Fr::from(ki); + } else { + res[i] = 0i8; + }; + + k.divn(1u32); + i += 1; + } + res + } +} + +// TODO implement From for any integer type smaller than 128-bit +impl From for Fr { + // FIXME this could really be better if we removed the match + fn from(val: i8) -> Fr { + match (val >= 0, val < 0) { + (true, false) => Fr([val.abs() as u64, 0u64, 0u64, 0u64]), + (false, true) => -Fr([val.abs() as u64, 0u64, 0u64, 0u64]), + (_, _) => unreachable!(), + } + } +} + +impl From for BlsScalar { + fn from(scalar: Fr) -> BlsScalar { + let bls_scalar = + >::from_bytes(&scalar.to_bytes()); + + // The order of a JubJub's Scalar field is shorter than a BLS + // Scalar, so convert any jubjub scalar to a BLS' Scalar + // should always be safe. + assert!( + bls_scalar.is_ok(), + "Failed to convert a Scalar from JubJub to BLS" + ); + + bls_scalar.unwrap() + } +} + +impl Index for Fr { + type Output = u64; + fn index(&self, _index: usize) -> &u64 { + &(self.0[_index]) + } +} + +impl IndexMut for Fr { + fn index_mut(&mut self, _index: usize) -> &mut u64 { + &mut (self.0[_index]) + } +} + +impl PartialOrd for Fr { + fn partial_cmp(&self, other: &Fr) -> Option { + Some(self.cmp(&other)) + } +} + +impl Ord for Fr { + fn cmp(&self, other: &Self) -> Ordering { + let a = self; + let other = other; + for i in (0..4).rev() { + if a[i] > other[i] { + return Ordering::Greater; + } else if a[i] < other[i] { + return Ordering::Less; + } + } + Ordering::Equal + } +} + +impl Serializable<32> for Fr { + type Error = BytesError; + + /// Attempts to convert a little-endian byte representation of + /// a field element into an element of `Fr`, failing if the input + /// is not canonical (is not smaller than r). + fn from_bytes(bytes: &[u8; Self::SIZE]) -> Result { + let mut tmp = Fr([0, 0, 0, 0]); + + tmp.0[0] = u64::from_le_bytes(bytes[0..8].try_into().unwrap()); + tmp.0[1] = u64::from_le_bytes(bytes[8..16].try_into().unwrap()); + tmp.0[2] = u64::from_le_bytes(bytes[16..24].try_into().unwrap()); + tmp.0[3] = u64::from_le_bytes(bytes[24..32].try_into().unwrap()); + + // Try to subtract the modulus + let (_, borrow) = sbb(tmp.0[0], MODULUS.0[0], 0); + let (_, borrow) = sbb(tmp.0[1], MODULUS.0[1], borrow); + let (_, borrow) = sbb(tmp.0[2], MODULUS.0[2], borrow); + let (_, borrow) = sbb(tmp.0[3], MODULUS.0[3], borrow); + + // If the element is smaller than MODULUS then the + // subtraction will underflow, producing a borrow value + // of 0xffff...ffff. Otherwise, it'll be zero. + let is_some = (borrow as u8) & 1; + + if is_some == 0 { + return Err(BytesError::InvalidData); + } + + // Convert to Montgomery form by computing + // (a.R^0 * R^2) / R = a.R + tmp *= &R2; + + Ok(tmp) + } + + /// Converts an element of `Fr` into a byte representation in + /// little-endian byte order. + fn to_bytes(&self) -> [u8; Self::SIZE] { + // Turn into canonical form by computing + // (a.R) / R = a + let tmp = Fr::montgomery_reduce( + self.0[0], self.0[1], self.0[2], self.0[3], 0, 0, 0, 0, + ); + + let mut res = [0; Self::SIZE]; + res[0..8].copy_from_slice(&tmp.0[0].to_le_bytes()); + res[8..16].copy_from_slice(&tmp.0[1].to_le_bytes()); + res[16..24].copy_from_slice(&tmp.0[2].to_le_bytes()); + res[24..32].copy_from_slice(&tmp.0[3].to_le_bytes()); + + res + } +} + +#[test] +fn w_naf_3() { + let scalar = Fr::from(1122334455u64); + let w = 3; + // -1 - 1*2^3 - 1*2^8 - 1*2^11 + 3*2^15 + 1*2^18 - 1*2^21 + 3*2^24 + + // 1*2^30 + let expected_result = [ + -1i8, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 3, 0, 0, 1, 0, 0, + -1, 0, 0, 3, 0, 0, 0, 0, 0, 1, + ]; + + let mut expected = [0i8; 256]; + expected[..expected_result.len()].copy_from_slice(&expected_result); + + let computed = scalar.compute_windowed_naf(w); + + assert_eq!(expected, computed); +} + +#[test] +fn w_naf_4() { + let scalar = Fr::from(58235u64); + let w = 4; + // -5 + 7*2^7 + 7*2^13 + let expected_result = [-5, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 0, 0, 7]; + + let mut expected = [0i8; 256]; + expected[..expected_result.len()].copy_from_slice(&expected_result); + + let computed = scalar.compute_windowed_naf(w); + + assert_eq!(expected, computed); +} + +#[test] +fn w_naf_2() { + let scalar = -Fr::one(); + let w = 2; + let two = Fr::from(2u64); + + let wnaf = scalar.compute_windowed_naf(w); + + let recomputed = wnaf.iter().enumerate().fold(Fr::zero(), |acc, (i, x)| { + if *x > 0 { + acc + Fr::from(*x as u64) * two.pow(&[(i as u64), 0u64, 0u64, 0u64]) + } else if *x < 0 { + acc - Fr::from(-(*x) as u64) + * two.pow(&[(i as u64), 0u64, 0u64, 0u64]) + } else { + acc + } + }); + assert_eq!(scalar, recomputed); +} diff --git a/src/lib.rs b/src/lib.rs index 882c7f3..429c574 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -60,11 +60,27 @@ use alloc::vec::Vec; #[cfg(feature = "alloc")] use group::WnafGroup; +// dusk additions: +// +mod dusk; +pub use dusk::{ + dhke, GENERATOR, GENERATOR_EXTENDED, GENERATOR_NUMS, + GENERATOR_NUMS_EXTENDED, +}; +/// An alias for [`AffinePoint`] +pub type JubJubAffine = AffinePoint; +/// An alias for [`ExtendedPoint`] +pub type JubJubExtended = ExtendedPoint; +/// An alias for [`Fr`]. +pub type JubJubScalar = Fr; +pub use dusk_bls12_381::BlsScalar; +pub use dusk_bls12_381::BlsScalar as Fq; +// end + #[macro_use] mod util; mod fr; -pub use dusk_bls12_381::BlsScalar as Fq; pub use fr::Fr; /// Represents an element of the base field $\mathbb{F}_q$ of the Jubjub @@ -2030,293 +2046,3 @@ fn test_zip_216() { } } } - -// Dusk Network features -mod dusk { - use super::*; - - use dusk_bytes::{Error as BytesError, Serializable}; - - pub use dusk_bls12_381::BlsScalar; - - /// An alias for [`AffinePoint`] - pub type JubJubAffine = AffinePoint; - /// An alias for [`ExtendedPoint`] - pub type JubJubExtended = ExtendedPoint; - /// An alias for [`Fr`]. - pub type JubJubScalar = Fr; - - /// Compute a shared secret `secret · public` using DHKE protocol - pub fn dhke(secret: &Fr, public: &JubJubExtended) -> JubJubAffine { - public.mul(secret).into() - } - - /// Use a fixed generator point. - /// The point is then reduced according to the prime field. We need only to - /// state the coordinates, so users can exploit its properties - /// which are proven by tests, checking: - /// - It lies on the curve, - /// - Is of prime order, - /// - Is not the identity point. - /// Using: - /// x = 0x3fd2814c43ac65a6f1fbf02d0fd6cce62e3ebb21fd6c54ed4df7b7ffec7beaca - // y = 0x0000000000000000000000000000000000000000000000000000000000000012 - pub const GENERATOR: JubJubAffine = JubJubAffine { - u: BlsScalar::from_raw([ - 0x4df7b7ffec7beaca, - 0x2e3ebb21fd6c54ed, - 0xf1fbf02d0fd6cce6, - 0x3fd2814c43ac65a6, - ]), - v: BlsScalar::from_raw([ - 0x0000000000000012, - 000000000000000000, - 000000000000000000, - 000000000000, - ]), - }; - - /// [`GENERATOR`] in [`JubJubExtended`] form - pub const GENERATOR_EXTENDED: JubJubExtended = JubJubExtended { - u: GENERATOR.u, - v: GENERATOR.v, - z: BlsScalar::one(), - t1: GENERATOR.u, - t2: GENERATOR.v, - }; - - /// GENERATOR NUMS which is obtained following the specs in: - /// https://app.gitbook.com/@dusk-network/s/specs/specifications/poseidon/pedersen-commitment-scheme - /// The counter = 18 and the hash function used to compute it was blake2b - /// Using: - /// x = 0x5e67b8f316f414f7bd9514c773fd4456931e316a39fe4541921710179df76377 - // y = 0x43d80eb3b2f3eb1b7b162dbeeb3b34fd9949ba0f82a5507a6705b707162e3ef8 - pub const GENERATOR_NUMS: JubJubAffine = JubJubAffine { - u: BlsScalar::from_raw([ - 0x921710179df76377, - 0x931e316a39fe4541, - 0xbd9514c773fd4456, - 0x5e67b8f316f414f7, - ]), - v: BlsScalar::from_raw([ - 0x6705b707162e3ef8, - 0x9949ba0f82a5507a, - 0x7b162dbeeb3b34fd, - 0x43d80eb3b2f3eb1b, - ]), - }; - - /// [`GENERATOR_NUMS`] in [`JubJubExtended`] form - pub const GENERATOR_NUMS_EXTENDED: JubJubExtended = JubJubExtended { - u: GENERATOR_NUMS.u, - v: GENERATOR_NUMS.v, - z: BlsScalar::one(), - t1: GENERATOR_NUMS.u, - t2: GENERATOR_NUMS.v, - }; - - impl Serializable<32> for JubJubAffine { - type Error = BytesError; - - /// Attempts to interpret a byte representation of an - /// affine point, failing if the element is not on - /// the curve or non-canonical. - /// - /// NOTE: ZIP 216 is enabled by default and the only way to interact - /// with serialization. - /// See: for more details. - fn from_bytes(b: &[u8; Self::SIZE]) -> Result { - let mut b = b.clone(); - - // Grab the sign bit from the representation - let sign = b[31] >> 7; - - // Mask away the sign bit - b[31] &= 0b0111_1111; - - // Interpret what remains as the y-coordinate - let v = >::from_bytes(&b)?; - - // -x^2 + y^2 = 1 + d.x^2.y^2 - // -x^2 = 1 + d.x^2.y^2 - y^2 (rearrange) - // -x^2 - d.x^2.y^2 = 1 - y^2 (rearrange) - // x^2 + d.x^2.y^2 = y^2 - 1 (flip signs) - // x^2 (1 + d.y^2) = y^2 - 1 (factor) - // x^2 = (y^2 - 1) / (1 + d.y^2) (isolate x^2) - // We know that (1 + d.y^2) is nonzero for all y: - // (1 + d.y^2) = 0 - // d.y^2 = -1 - // y^2 = -(1 / d) No solutions, as -(1 / d) is not a square - - let v2 = v.square(); - - Option::from( - ((v2 - BlsScalar::one()) - * ((BlsScalar::one() + EDWARDS_D * &v2) - .invert() - .unwrap_or(BlsScalar::zero()))) - .sqrt() - .and_then(|u| { - // Fix the sign of `x` if necessary - let flip_sign = Choice::from((u.to_bytes()[0] ^ sign) & 1); - let u = BlsScalar::conditional_select(&u, &-u, flip_sign); - // If x == 0, flip_sign == sign_bit. We therefore want to - // reject the encoding as non-canonical - // if all of the following occur: - // - x == 0 - // - flip_sign == true - let u_is_zero = u.ct_eq(&BlsScalar::zero()); - CtOption::new( - JubJubAffine { u, v }, - !(u_is_zero & flip_sign), - ) - }), - ) - .ok_or(BytesError::InvalidData) - } - - /// Converts this element into its byte representation. - fn to_bytes(&self) -> [u8; Self::SIZE] { - let mut tmp = self.v.to_bytes(); - let u = self.u.to_bytes(); - - // Encode the sign of the x-coordinate in the most - // significant bit. - tmp[31] |= u[0] << 7; - - tmp - } - } - - impl ExtendedPoint { - /// Constructs an extended point (with `Z = 1`) from - /// an affine point using the map `(x, y) => (x, y, 1, x, y)`. - pub const fn from_affine(affine: JubJubAffine) -> Self { - Self::from_raw_unchecked( - affine.u, - affine.v, - BlsScalar::one(), - affine.u, - affine.v, - ) - } - - /// Constructs an extended point from its raw internals - pub const fn from_raw_unchecked( - u: BlsScalar, - v: BlsScalar, - z: BlsScalar, - t1: BlsScalar, - t2: BlsScalar, - ) -> Self { - Self { u, v, z, t1, t2 } - } - - /// Returns the `u`-coordinate of this point. - pub const fn get_u(&self) -> BlsScalar { - self.u - } - - /// Returns the `v`-coordinate of this point. - pub const fn get_v(&self) -> BlsScalar { - self.v - } - - /// Returns the `z`-coordinate of this point. - pub const fn get_z(&self) -> BlsScalar { - self.z - } - - /// Returns the `t1`-coordinate of this point. - pub const fn get_t1(&self) -> BlsScalar { - self.t1 - } - - /// Returns the `t2`-coordinate of this point. - pub const fn get_t2(&self) -> BlsScalar { - self.t2 - } - - /// Returns two scalars suitable for hashing that represent the - /// Extended Point. - pub fn to_hash_inputs(&self) -> [BlsScalar; 2] { - // The same JubJubAffine can have different JubJubExtended - // representations, therefore we convert from Extended to Affine - // before hashing, to ensure deterministic result - let p = JubJubAffine::from(self); - [p.u, p.v] - } - } - - #[test] - fn test_affine_point_generator_has_order_p() { - assert_eq!(GENERATOR.is_prime_order().unwrap_u8(), 1); - } - - #[test] - fn test_extended_point_generator_has_order_p() { - assert_eq!(GENERATOR_EXTENDED.is_prime_order().unwrap_u8(), 1); - } - - #[test] - fn test_affine_point_generator_nums_has_order_p() { - assert_eq!(GENERATOR_NUMS.is_prime_order().unwrap_u8(), 1); - } - - #[test] - fn test_affine_point_generator_is_not_identity() { - assert_ne!( - JubJubExtended::from(GENERATOR.mul_by_cofactor()), - JubJubExtended::identity() - ); - } - - #[test] - fn test_extended_point_generator_is_not_identity() { - assert_ne!( - GENERATOR_EXTENDED.mul_by_cofactor(), - JubJubExtended::identity() - ); - } - - #[test] - fn test_affine_point_generator_nums_is_not_identity() { - assert_ne!( - JubJubExtended::from(GENERATOR_NUMS.mul_by_cofactor()), - JubJubExtended::identity() - ); - } - - #[ignore] - #[test] - fn second_gen_nums() { - use blake2::{Blake2b, Digest}; - let generator_bytes = GENERATOR.to_bytes(); - let mut counter = 0u64; - let mut array = [0u8; 32]; - loop { - let mut hasher = Blake2b::new(); - hasher.update(generator_bytes); - hasher.update(counter.to_le_bytes()); - let res = hasher.finalize(); - array.copy_from_slice(&res[0..32]); - if >::from_bytes(&array).is_ok() - && >::from_bytes(&array) - .unwrap() - .is_prime_order() - .unwrap_u8() - == 1 - { - assert!( - GENERATOR_NUMS - == >::from_bytes( - &array - ) - .unwrap() - ); - } - counter += 1; - } - } -} -pub use dusk::*; From f5887822539090ddf1bae862cb52b53ade2b6499 Mon Sep 17 00:00:00 2001 From: moana Date: Wed, 11 Oct 2023 13:37:27 +0200 Subject: [PATCH 2/3] Expose `EDWARDS_D` const --- CHANGELOG.md | 4 ++++ src/dusk.rs | 6 +++--- src/lib.rs | 4 ++-- 3 files changed, 9 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 10aaf6f..09b4b3d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Expose `EDWARDS_D` constant + ## [0.13.0] - 2023-06-07 ### Added diff --git a/src/dusk.rs b/src/dusk.rs index 137e0b0..b87c162 100644 --- a/src/dusk.rs +++ b/src/dusk.rs @@ -40,9 +40,9 @@ pub const GENERATOR: JubJubAffine = JubJubAffine { ]), v: BlsScalar::from_raw([ 0x0000000000000012, - 000000000000000000, - 000000000000000000, - 000000000000, + 0x0000000000000000, + 0x0000000000000000, + 0x0000000000000000, ]), }; diff --git a/src/lib.rs b/src/lib.rs index 429c574..867d7a5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -446,8 +446,8 @@ impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint { impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, ExtendedPoint); -// `d = -(10240/10241)` -const EDWARDS_D: Fq = Fq::from_raw([ +/// `d = -(10240/10241)` +pub const EDWARDS_D: Fq = Fq::from_raw([ 0x0106_5fd6_d634_3eb1, 0x292d_7f6d_3757_9d26, 0xf5fd_9207_e6bd_7fd4, From 4697665b379b015197a990c454b20073148fac92 Mon Sep 17 00:00:00 2001 From: moana Date: Wed, 11 Oct 2023 14:11:29 +0200 Subject: [PATCH 3/3] Fix clippy in dusk modules --- src/dusk.rs | 4 ++-- src/fr/dusk.rs | 10 +++++----- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/dusk.rs b/src/dusk.rs index b87c162..28a1705 100644 --- a/src/dusk.rs +++ b/src/dusk.rs @@ -96,7 +96,7 @@ impl Serializable<32> for JubJubAffine { /// with serialization. /// See: for more details. fn from_bytes(b: &[u8; Self::SIZE]) -> Result { - let mut b = b.clone(); + let mut b = *b; // Grab the sign bit from the representation let sign = b[31] >> 7; @@ -122,7 +122,7 @@ impl Serializable<32> for JubJubAffine { Option::from( ((v2 - BlsScalar::one()) - * ((BlsScalar::one() + EDWARDS_D * &v2) + * ((BlsScalar::one() + EDWARDS_D * v2) .invert() .unwrap_or(BlsScalar::zero()))) .sqrt() diff --git a/src/fr/dusk.rs b/src/fr/dusk.rs index da7fe4d..df227c3 100644 --- a/src/fr/dusk.rs +++ b/src/fr/dusk.rs @@ -119,7 +119,7 @@ impl Fr { if !k.is_even() { let ki = k.mods_2_pow_k(width); res[i] = ki; - k = k - Fr::from(ki); + k -= Fr::from(ki); } else { res[i] = 0i8; }; @@ -136,8 +136,8 @@ impl From for Fr { // FIXME this could really be better if we removed the match fn from(val: i8) -> Fr { match (val >= 0, val < 0) { - (true, false) => Fr([val.abs() as u64, 0u64, 0u64, 0u64]), - (false, true) => -Fr([val.abs() as u64, 0u64, 0u64, 0u64]), + (true, false) => Fr([val.unsigned_abs() as u64, 0u64, 0u64, 0u64]), + (false, true) => -Fr([val.unsigned_abs() as u64, 0u64, 0u64, 0u64]), (_, _) => unreachable!(), } } @@ -175,15 +175,15 @@ impl IndexMut for Fr { impl PartialOrd for Fr { fn partial_cmp(&self, other: &Fr) -> Option { - Some(self.cmp(&other)) + Some(self.cmp(other)) } } impl Ord for Fr { fn cmp(&self, other: &Self) -> Ordering { let a = self; - let other = other; for i in (0..4).rev() { + #[allow(clippy::comparison_chain)] if a[i] > other[i] { return Ordering::Greater; } else if a[i] < other[i] {