#![cfg_attr(not(feature = "std"), no_std)]
#![deny(broken_intra_doc_links)]
#![deny(missing_debug_implementations)]
#![deny(missing_docs)]
#![deny(unsafe_code)]
#![allow(clippy::suspicious_arithmetic_impl)]
#[cfg(test)]
#[macro_use]
extern crate std;
#[cfg(feature = "canon")]
use canonical::Canon;
#[cfg(feature = "canon")]
use canonical_derive::Canon;
use core::ops::{Add, AddAssign, Mul, MulAssign, Neg, Sub, SubAssign};
use dusk_bytes::{Error as BytesError, Serializable};
use subtle::{Choice, ConditionallySelectable, ConstantTimeEq, CtOption};
#[macro_use]
mod util;
mod fr;
pub mod elgamal;
pub use dusk_bls12_381::BlsScalar;
pub use fr::Fr as JubJubScalar;
pub(crate) use fr::Fr;
pub type Scalar = Fr;
const FR_MODULUS_BYTES: [u8; 32] = [
183, 44, 247, 214, 94, 14, 151, 208, 130, 16, 200, 204, 147, 32, 104, 166,
0, 59, 52, 1, 1, 59, 103, 6, 169, 175, 51, 101, 234, 180, 125, 14,
];
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "canon", derive(Canon))]
pub struct JubJubAffine {
x: BlsScalar,
y: BlsScalar,
}
impl Neg for JubJubAffine {
type Output = JubJubAffine;
#[inline]
fn neg(self) -> JubJubAffine {
JubJubAffine {
x: -self.x,
y: self.y,
}
}
}
impl ConstantTimeEq for JubJubAffine {
fn ct_eq(&self, other: &Self) -> Choice {
self.x.ct_eq(&other.x) & self.y.ct_eq(&other.y)
}
}
impl PartialEq for JubJubAffine {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).unwrap_u8() == 1
}
}
impl ConditionallySelectable for JubJubAffine {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
JubJubAffine {
x: BlsScalar::conditional_select(&a.x, &b.x, choice),
y: BlsScalar::conditional_select(&a.y, &b.y, choice),
}
}
}
pub const GENERATOR: JubJubAffine = JubJubAffine {
x: BlsScalar::from_raw([
0x4df7b7ffec7beaca,
0x2e3ebb21fd6c54ed,
0xf1fbf02d0fd6cce6,
0x3fd2814c43ac65a6,
]),
y: BlsScalar::from_raw([
0x0000000000000012,
000000000000000000,
000000000000000000,
000000000000,
]),
};
pub const GENERATOR_EXTENDED: JubJubExtended = JubJubExtended {
x: GENERATOR.x,
y: GENERATOR.y,
z: BlsScalar::one(),
t1: GENERATOR.x,
t2: GENERATOR.y,
};
pub const GENERATOR_NUMS: JubJubAffine = JubJubAffine {
x: BlsScalar::from_raw([
0x921710179df76377,
0x931e316a39fe4541,
0xbd9514c773fd4456,
0x5e67b8f316f414f7,
]),
y: BlsScalar::from_raw([
0x6705b707162e3ef8,
0x9949ba0f82a5507a,
0x7b162dbeeb3b34fd,
0x43d80eb3b2f3eb1b,
]),
};
pub const GENERATOR_NUMS_EXTENDED: JubJubExtended = JubJubExtended {
x: GENERATOR_NUMS.x,
y: GENERATOR_NUMS.y,
z: BlsScalar::one(),
t1: GENERATOR_NUMS.x,
t2: GENERATOR_NUMS.y,
};
#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "canon", derive(Canon))]
pub struct JubJubExtended {
x: BlsScalar,
y: BlsScalar,
z: BlsScalar,
t1: BlsScalar,
t2: BlsScalar,
}
impl ConstantTimeEq for JubJubExtended {
fn ct_eq(&self, other: &Self) -> Choice {
(&self.x * &other.z).ct_eq(&(&other.x * &self.z))
& (&self.y * &other.z).ct_eq(&(&other.y * &self.z))
}
}
impl ConditionallySelectable for JubJubExtended {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
JubJubExtended {
x: BlsScalar::conditional_select(&a.x, &b.x, choice),
y: BlsScalar::conditional_select(&a.y, &b.y, choice),
z: BlsScalar::conditional_select(&a.z, &b.z, choice),
t1: BlsScalar::conditional_select(&a.t1, &b.t1, choice),
t2: BlsScalar::conditional_select(&a.t2, &b.t2, choice),
}
}
}
impl PartialEq for JubJubExtended {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).unwrap_u8() == 1
}
}
impl Neg for JubJubExtended {
type Output = JubJubExtended;
#[inline]
fn neg(self) -> JubJubExtended {
JubJubExtended {
x: -self.x,
y: self.y,
z: self.z,
t1: -self.t1,
t2: self.t2,
}
}
}
impl From<JubJubAffine> for JubJubExtended {
fn from(affine: JubJubAffine) -> JubJubExtended {
JubJubExtended {
x: affine.x,
y: affine.y,
z: BlsScalar::one(),
t1: affine.x,
t2: affine.y,
}
}
}
impl<'a> From<&'a JubJubExtended> for JubJubAffine {
fn from(extended: &'a JubJubExtended) -> JubJubAffine {
let zinv = extended.z.invert().unwrap();
JubJubAffine {
x: extended.x * &zinv,
y: extended.y * &zinv,
}
}
}
impl From<JubJubExtended> for JubJubAffine {
fn from(extended: JubJubExtended) -> JubJubAffine {
JubJubAffine::from(&extended)
}
}
#[derive(Clone, Copy, Debug)]
pub struct AffineNielsPoint {
y_plus_x: BlsScalar,
y_minus_x: BlsScalar,
t2d: BlsScalar,
}
impl AffineNielsPoint {
pub const fn identity() -> Self {
AffineNielsPoint {
y_plus_x: BlsScalar::one(),
y_minus_x: BlsScalar::one(),
t2d: BlsScalar::zero(),
}
}
#[inline]
fn multiply(&self, by: &[u8; 32]) -> JubJubExtended {
let zero = AffineNielsPoint::identity();
let mut acc = JubJubExtended::identity();
for bit in by
.iter()
.rev()
.flat_map(|byte| {
(0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))
})
.skip(4)
{
acc = acc.double();
acc += AffineNielsPoint::conditional_select(&zero, &self, bit);
}
acc
}
pub fn multiply_bits(&self, by: &[u8; 32]) -> JubJubExtended {
self.multiply(by)
}
}
impl<'a, 'b> Mul<&'b Fr> for &'a AffineNielsPoint {
type Output = JubJubExtended;
fn mul(self, other: &'b Fr) -> JubJubExtended {
self.multiply(&other.to_bytes())
}
}
impl_binops_multiplicative_mixed!(AffineNielsPoint, Fr, JubJubExtended);
impl ConditionallySelectable for AffineNielsPoint {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
AffineNielsPoint {
y_plus_x: BlsScalar::conditional_select(
&a.y_plus_x,
&b.y_plus_x,
choice,
),
y_minus_x: BlsScalar::conditional_select(
&a.y_minus_x,
&b.y_minus_x,
choice,
),
t2d: BlsScalar::conditional_select(&a.t2d, &b.t2d, choice),
}
}
}
#[derive(Clone, Copy, Debug)]
pub struct ExtendedNielsPoint {
y_plus_x: BlsScalar,
y_minus_x: BlsScalar,
z: BlsScalar,
t2d: BlsScalar,
}
impl ConditionallySelectable for ExtendedNielsPoint {
fn conditional_select(a: &Self, b: &Self, choice: Choice) -> Self {
ExtendedNielsPoint {
y_plus_x: BlsScalar::conditional_select(
&a.y_plus_x,
&b.y_plus_x,
choice,
),
y_minus_x: BlsScalar::conditional_select(
&a.y_minus_x,
&b.y_minus_x,
choice,
),
z: BlsScalar::conditional_select(&a.z, &b.z, choice),
t2d: BlsScalar::conditional_select(&a.t2d, &b.t2d, choice),
}
}
}
impl ExtendedNielsPoint {
pub const fn identity() -> Self {
ExtendedNielsPoint {
y_plus_x: BlsScalar::one(),
y_minus_x: BlsScalar::one(),
z: BlsScalar::one(),
t2d: BlsScalar::zero(),
}
}
#[inline]
fn multiply(&self, by: &[u8; 32]) -> JubJubExtended {
let zero = ExtendedNielsPoint::identity();
let mut acc = JubJubExtended::identity();
for bit in by
.iter()
.rev()
.flat_map(|byte| {
(0..8).rev().map(move |i| Choice::from((byte >> i) & 1u8))
})
.skip(4)
{
acc = acc.double();
acc += ExtendedNielsPoint::conditional_select(&zero, &self, bit);
}
acc
}
pub fn multiply_bits(&self, by: &[u8; 32]) -> JubJubExtended {
self.multiply(by)
}
}
impl<'a, 'b> Mul<&'b Fr> for &'a ExtendedNielsPoint {
type Output = JubJubExtended;
fn mul(self, other: &'b Fr) -> JubJubExtended {
self.multiply(&other.to_bytes())
}
}
impl_binops_multiplicative_mixed!(ExtendedNielsPoint, Fr, JubJubExtended);
pub const EDWARDS_D: BlsScalar = BlsScalar::from_raw([
0x01065fd6d6343eb1,
0x292d7f6d37579d26,
0xf5fd9207e6bd7fd4,
0x2a9318e74bfa2b48,
]);
pub const EDWARDS_D2: BlsScalar = BlsScalar::from_raw([
0x020cbfadac687d62,
0x525afeda6eaf3a4c,
0xebfb240fcd7affa8,
0x552631ce97f45691,
]);
impl Serializable<32> for JubJubAffine {
type Error = BytesError;
fn to_bytes(&self) -> [u8; Self::SIZE] {
let mut tmp = self.y.to_bytes();
let x = self.x.to_bytes();
tmp[31] |= x[0] << 7;
tmp
}
fn from_bytes(b: &[u8; Self::SIZE]) -> Result<Self, Self::Error> {
let mut b = b.clone();
let sign = b[31] >> 7;
b[31] &= 0b0111_1111;
let y = BlsScalar::from_bytes(&b)?;
let y2 = y.square();
Option::from(
((y2 - BlsScalar::one())
* ((BlsScalar::one() + EDWARDS_D * &y2)
.invert()
.unwrap_or(BlsScalar::zero())))
.sqrt()
.and_then(|x| {
let flip_sign = Choice::from((x.to_bytes()[0] ^ sign) & 1);
let x_negated = -x;
let final_x =
BlsScalar::conditional_select(&x, &x_negated, flip_sign);
CtOption::new(JubJubAffine { x: final_x, y }, Choice::from(1u8))
}),
)
.ok_or(BytesError::InvalidData)
}
}
impl JubJubAffine {
pub const fn identity() -> Self {
JubJubAffine {
x: BlsScalar::zero(),
y: BlsScalar::one(),
}
}
pub fn mul_by_cofactor(&self) -> JubJubExtended {
JubJubExtended::from(*self).mul_by_cofactor()
}
pub fn is_small_order(&self) -> Choice {
JubJubExtended::from(*self).is_small_order()
}
pub fn is_torsion_free(&self) -> Choice {
JubJubExtended::from(*self).is_torsion_free()
}
pub fn is_prime_order(&self) -> Choice {
let extended = JubJubExtended::from(*self);
extended.is_torsion_free() & (!extended.is_identity())
}
pub fn get_x(&self) -> BlsScalar {
self.x
}
pub fn get_y(&self) -> BlsScalar {
self.y
}
pub const fn to_niels(&self) -> AffineNielsPoint {
AffineNielsPoint {
y_plus_x: BlsScalar::add(&self.y, &self.x),
y_minus_x: BlsScalar::sub(&self.y, &self.x),
t2d: BlsScalar::mul(&BlsScalar::mul(&self.x, &self.y), &EDWARDS_D2),
}
}
pub const fn from_raw_unchecked(
x: BlsScalar,
y: BlsScalar,
) -> JubJubAffine {
JubJubAffine { x, y }
}
#[cfg(test)]
fn is_on_curve_vartime(&self) -> bool {
let x2 = self.x.square();
let y2 = self.y.square();
&y2 - &x2 == BlsScalar::one() + &EDWARDS_D * &x2 * &y2
}
}
impl JubJubExtended {
pub fn get_x(&self) -> BlsScalar {
self.x
}
pub fn get_y(&self) -> BlsScalar {
self.y
}
pub fn get_z(&self) -> BlsScalar {
self.z
}
pub fn get_t1(&self) -> BlsScalar {
self.t1
}
pub fn get_t2(&self) -> BlsScalar {
self.t2
}
pub const fn identity() -> Self {
JubJubExtended {
x: BlsScalar::zero(),
y: BlsScalar::one(),
z: BlsScalar::one(),
t1: BlsScalar::zero(),
t2: BlsScalar::zero(),
}
}
pub fn is_identity(&self) -> Choice {
self.x.ct_eq(&BlsScalar::zero()) & self.y.ct_eq(&self.z)
}
pub fn is_small_order(&self) -> Choice {
self.double().double().x.ct_eq(&BlsScalar::zero())
}
pub fn is_torsion_free(&self) -> Choice {
self.multiply(&FR_MODULUS_BYTES).is_identity()
}
pub fn is_prime_order(&self) -> Choice {
self.is_torsion_free() & (!self.is_identity())
}
pub fn mul_by_cofactor(&self) -> JubJubExtended {
self.double().double().double()
}
pub fn to_niels(&self) -> ExtendedNielsPoint {
ExtendedNielsPoint {
y_plus_x: &self.y + &self.x,
y_minus_x: &self.y - &self.x,
z: self.z,
t2d: &self.t1 * &self.t2 * EDWARDS_D2,
}
}
pub fn to_hash_inputs(&self) -> [BlsScalar; 2] {
let p = JubJubAffine::from(self);
[p.x, p.y]
}
pub fn double(&self) -> JubJubExtended {
let xx = self.x.square();
let yy = self.y.square();
let zz2 = self.z.square().double();
let xy2 = (&self.x + &self.y).square();
let yy_plus_xx = &yy + &xx;
let yy_minus_xx = &yy - &xx;
CompletedPoint {
x: &xy2 - &yy_plus_xx,
y: yy_plus_xx,
z: yy_minus_xx,
t: &zz2 - &yy_minus_xx,
}
.into_extended()
}
#[inline]
fn multiply(self, by: &[u8; 32]) -> Self {
self.to_niels().multiply(by)
}
#[cfg(test)]
fn is_on_curve_vartime(&self) -> bool {
let affine = JubJubAffine::from(*self);
self.z != BlsScalar::zero()
&& affine.is_on_curve_vartime()
&& (affine.x * affine.y * self.z == self.t1 * self.t2)
}
}
impl<'a, 'b> Mul<&'b Fr> for &'a JubJubExtended {
type Output = JubJubExtended;
fn mul(self, other: &'b Fr) -> JubJubExtended {
self.multiply(&other.to_bytes())
}
}
impl_binops_multiplicative!(JubJubExtended, Fr);
impl<'a, 'b> Add<&'b ExtendedNielsPoint> for &'a JubJubExtended {
type Output = JubJubExtended;
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, other: &'b ExtendedNielsPoint) -> JubJubExtended {
let a = (&self.y - &self.x) * &other.y_minus_x;
let b = (&self.y + &self.x) * &other.y_plus_x;
let c = &self.t1 * &self.t2 * &other.t2d;
let d = (&self.z * &other.z).double();
CompletedPoint {
x: &b - &a,
y: &b + &a,
z: &d + &c,
t: &d - &c,
}
.into_extended()
}
}
impl<'a, 'b> Sub<&'b ExtendedNielsPoint> for &'a JubJubExtended {
type Output = JubJubExtended;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, other: &'b ExtendedNielsPoint) -> JubJubExtended {
let a = (&self.y - &self.x) * &other.y_plus_x;
let b = (&self.y + &self.x) * &other.y_minus_x;
let c = &self.t1 * &self.t2 * &other.t2d;
let d = (&self.z * &other.z).double();
CompletedPoint {
x: &b - &a,
y: &b + &a,
z: &d - &c,
t: &d + &c,
}
.into_extended()
}
}
impl_binops_additive!(JubJubExtended, ExtendedNielsPoint);
impl<'a, 'b> Add<&'b AffineNielsPoint> for &'a JubJubExtended {
type Output = JubJubExtended;
#[allow(clippy::suspicious_arithmetic_impl)]
fn add(self, other: &'b AffineNielsPoint) -> JubJubExtended {
let a = (&self.y - &self.x) * &other.y_minus_x;
let b = (&self.y + &self.x) * &other.y_plus_x;
let c = &self.t1 * &self.t2 * &other.t2d;
let d = self.z.double();
CompletedPoint {
x: &b - &a,
y: &b + &a,
z: &d + &c,
t: &d - &c,
}
.into_extended()
}
}
impl<'a, 'b> Sub<&'b AffineNielsPoint> for &'a JubJubExtended {
type Output = JubJubExtended;
#[allow(clippy::suspicious_arithmetic_impl)]
fn sub(self, other: &'b AffineNielsPoint) -> JubJubExtended {
let a = (&self.y - &self.x) * &other.y_plus_x;
let b = (&self.y + &self.x) * &other.y_minus_x;
let c = &self.t1 * &self.t2 * &other.t2d;
let d = self.z.double();
CompletedPoint {
x: &b - &a,
y: &b + &a,
z: &d - &c,
t: &d + &c,
}
.into_extended()
}
}
impl_binops_additive!(JubJubExtended, AffineNielsPoint);
impl<'a, 'b> Add<&'b JubJubExtended> for &'a JubJubExtended {
type Output = JubJubExtended;
#[inline]
fn add(self, other: &'b JubJubExtended) -> JubJubExtended {
self + other.to_niels()
}
}
impl<'a, 'b> Sub<&'b JubJubExtended> for &'a JubJubExtended {
type Output = JubJubExtended;
#[inline]
fn sub(self, other: &'b JubJubExtended) -> JubJubExtended {
self - other.to_niels()
}
}
impl_binops_additive!(JubJubExtended, JubJubExtended);
impl<'a, 'b> Add<&'b JubJubAffine> for &'a JubJubExtended {
type Output = JubJubExtended;
#[inline]
fn add(self, other: &'b JubJubAffine) -> JubJubExtended {
self + other.to_niels()
}
}
impl<'a, 'b> Sub<&'b JubJubAffine> for &'a JubJubExtended {
type Output = JubJubExtended;
#[inline]
fn sub(self, other: &'b JubJubAffine) -> JubJubExtended {
self - other.to_niels()
}
}
impl_binops_additive!(JubJubExtended, JubJubAffine);
struct CompletedPoint {
x: BlsScalar,
y: BlsScalar,
z: BlsScalar,
t: BlsScalar,
}
impl CompletedPoint {
#[inline]
fn into_extended(self) -> JubJubExtended {
JubJubExtended {
x: &self.x * &self.t,
y: &self.y * &self.z,
z: &self.z * &self.t,
t1: self.x,
t2: self.y,
}
}
}
impl Default for JubJubAffine {
fn default() -> JubJubAffine {
JubJubAffine::identity()
}
}
impl Default for JubJubExtended {
fn default() -> JubJubExtended {
JubJubExtended::identity()
}
}
pub fn batch_normalize<'a>(
y: &'a mut [JubJubExtended],
) -> impl Iterator<Item = JubJubAffine> + 'a {
let mut acc = BlsScalar::one();
for p in y.iter_mut() {
p.t1 = acc;
acc *= &p.z;
}
acc = acc.invert().unwrap();
for p in y.iter_mut().rev() {
let mut q = *p;
let tmp = q.t1 * acc;
acc *= &q.z;
q.x *= &tmp;
q.y *= &tmp;
q.z = BlsScalar::one();
q.t1 = q.x;
q.t2 = q.y;
*p = q;
}
y.iter().map(|p| JubJubAffine { x: p.x, y: p.y })
}
#[test]
fn test_is_on_curve_var() {
assert!(JubJubAffine::identity().is_on_curve_vartime());
}
#[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()
);
}
#[test]
fn test_d_is_non_quadratic_residue() {
assert!(EDWARDS_D.sqrt().is_none().unwrap_u8() == 1);
assert!((-EDWARDS_D).sqrt().is_none().unwrap_u8() == 1);
assert!((-EDWARDS_D).invert().unwrap().sqrt().is_none().unwrap_u8() == 1);
}
#[test]
fn test_affine_niels_point_identity() {
assert_eq!(
AffineNielsPoint::identity().y_plus_x,
JubJubAffine::identity().to_niels().y_plus_x
);
assert_eq!(
AffineNielsPoint::identity().y_minus_x,
JubJubAffine::identity().to_niels().y_minus_x
);
assert_eq!(
AffineNielsPoint::identity().t2d,
JubJubAffine::identity().to_niels().t2d
);
}
#[test]
fn test_extended_niels_point_identity() {
assert_eq!(
ExtendedNielsPoint::identity().y_plus_x,
JubJubExtended::identity().to_niels().y_plus_x
);
assert_eq!(
ExtendedNielsPoint::identity().y_minus_x,
JubJubExtended::identity().to_niels().y_minus_x
);
assert_eq!(
ExtendedNielsPoint::identity().z,
JubJubExtended::identity().to_niels().z
);
assert_eq!(
ExtendedNielsPoint::identity().t2d,
JubJubExtended::identity().to_niels().t2d
);
}
#[test]
fn test_assoc() {
let p = JubJubExtended::from(JubJubAffine {
x: BlsScalar::from_raw([
0x81c571e5d883cfb0,
0x049f7a686f147029,
0xf539c860bc3ea21f,
0x4284715b7ccc8162,
]),
y: BlsScalar::from_raw([
0xbf096275684bb8ca,
0xc7ba245890af256d,
0x59119f3e86380eb0,
0x3793de182f9fb1d2,
]),
})
.mul_by_cofactor();
assert!(p.is_on_curve_vartime());
assert_eq!(
(p * Fr::from(1000u64)) * Fr::from(3938u64),
p * (Fr::from(1000u64) * Fr::from(3938u64)),
);
}
#[test]
fn test_batch_normalize() {
let mut p = JubJubExtended::from(JubJubAffine {
x: BlsScalar::from_raw([
0x81c571e5d883cfb0,
0x049f7a686f147029,
0xf539c860bc3ea21f,
0x4284715b7ccc8162,
]),
y: BlsScalar::from_raw([
0xbf096275684bb8ca,
0xc7ba245890af256d,
0x59119f3e86380eb0,
0x3793de182f9fb1d2,
]),
})
.mul_by_cofactor();
let mut y = vec![];
for _ in 0..10 {
y.push(p);
p = p.double();
}
for p in &y {
assert!(p.is_on_curve_vartime());
}
let expected: std::vec::Vec<_> =
y.iter().map(|p| JubJubAffine::from(*p)).collect();
let result1: std::vec::Vec<_> = batch_normalize(&mut y).collect();
for i in 0..10 {
assert!(expected[i] == result1[i]);
assert!(y[i].is_on_curve_vartime());
assert!(JubJubAffine::from(y[i]) == expected[i]);
}
let result2: std::vec::Vec<_> = batch_normalize(&mut y).collect();
for i in 0..10 {
assert!(expected[i] == result2[i]);
assert!(y[i].is_on_curve_vartime());
assert!(JubJubAffine::from(y[i]) == expected[i]);
}
}
#[cfg(test)]
const FULL_GENERATOR: JubJubAffine = JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([
0xe4b3d35df1a7adfe,
0xcaf55d1b29bf81af,
0x8b0f03ddd60a8187,
0x62edcbb8bf3787c8,
]),
BlsScalar::from_raw([0xb, 0x0, 0x0, 0x0]),
);
#[cfg(test)]
const EIGHT_TORSION: [JubJubAffine; 8] = [
JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([
0xd92e6a7927200d43,
0x7aa41ac43dae8582,
0xeaaae086a16618d1,
0x71d4df38ba9e7973,
]),
BlsScalar::from_raw([
0xff0d2068eff496dd,
0x9106ee90f384a4a1,
0x16a13035ad4d7266,
0x4958bdb21966982e,
]),
),
JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([
0xfffeffff00000001,
0x67baa40089fb5bfe,
0xa5e80b39939ed334,
0x73eda753299d7d47,
]),
BlsScalar::from_raw([0x0, 0x0, 0x0, 0x0]),
),
JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([
0xd92e6a7927200d43,
0x7aa41ac43dae8582,
0xeaaae086a16618d1,
0x71d4df38ba9e7973,
]),
BlsScalar::from_raw([
0xf2df96100b6924,
0xc2b6b5720c79b75d,
0x1c98a7d25c54659e,
0x2a94e9a11036e51a,
]),
),
JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([0x0, 0x0, 0x0, 0x0]),
BlsScalar::from_raw([
0xffffffff00000000,
0x53bda402fffe5bfe,
0x3339d80809a1d805,
0x73eda753299d7d48,
]),
),
JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([
0x26d19585d8dff2be,
0xd919893ec24fd67c,
0x488ef781683bbf33,
0x218c81a6eff03d4,
]),
BlsScalar::from_raw([
0xf2df96100b6924,
0xc2b6b5720c79b75d,
0x1c98a7d25c54659e,
0x2a94e9a11036e51a,
]),
),
JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([
0x1000000000000,
0xec03000276030000,
0x8d51ccce760304d0,
0x0,
]),
BlsScalar::from_raw([0x0, 0x0, 0x0, 0x0]),
),
JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([
0x26d19585d8dff2be,
0xd919893ec24fd67c,
0x488ef781683bbf33,
0x218c81a6eff03d4,
]),
BlsScalar::from_raw([
0xff0d2068eff496dd,
0x9106ee90f384a4a1,
0x16a13035ad4d7266,
0x4958bdb21966982e,
]),
),
JubJubAffine::from_raw_unchecked(
BlsScalar::from_raw([0x0, 0x0, 0x0, 0x0]),
BlsScalar::from_raw([0x1, 0x0, 0x0, 0x0]),
),
];
#[test]
fn find_eight_torsion() {
let g = JubJubExtended::from(FULL_GENERATOR);
assert!(g.is_small_order().unwrap_u8() == 0);
let g = g.multiply(&FR_MODULUS_BYTES);
assert!(g.is_small_order().unwrap_u8() == 1);
let mut cur = g;
for (i, point) in EIGHT_TORSION.iter().enumerate() {
let tmp = JubJubAffine::from(cur);
if &tmp != point {
panic!("{}th torsion point should be {:?}", i, tmp);
}
cur += &g;
}
}
#[test]
fn find_curve_generator() {
let mut trial_bytes = [0; 32];
for _ in 0..255 {
let a = JubJubAffine::from_bytes(&trial_bytes);
if a.is_ok() {
let a = a.unwrap();
assert!(a.is_on_curve_vartime());
let b = JubJubExtended::from(a);
let b = b.multiply(&FR_MODULUS_BYTES);
assert!(b.is_small_order().unwrap_u8() == 1);
let b = b.double();
assert!(b.is_small_order().unwrap_u8() == 1);
let b = b.double();
assert!(b.is_small_order().unwrap_u8() == 1);
if b.is_identity().unwrap_u8() == 0 {
let b = b.double();
assert!(b.is_small_order().unwrap_u8() == 1);
assert!(b.is_identity().unwrap_u8() == 1);
assert_eq!(FULL_GENERATOR, a);
assert!(a.mul_by_cofactor().is_torsion_free().unwrap_u8() == 1);
return;
}
}
trial_bytes[0] += 1;
}
panic!("should have found a generator of the curve");
}
#[test]
fn test_small_order() {
for point in EIGHT_TORSION.iter() {
assert!(point.is_small_order().unwrap_u8() == 1);
}
}
#[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 JubJubAffine::from_bytes(&array).is_ok()
&& JubJubAffine::from_bytes(&array)
.unwrap()
.is_prime_order()
.unwrap_u8()
== 1
{
assert!(
GENERATOR_NUMS == JubJubAffine::from_bytes(&array).unwrap()
);
}
counter += 1;
}
}
#[test]
fn test_is_identity() {
let a = EIGHT_TORSION[0].mul_by_cofactor();
let b = EIGHT_TORSION[1].mul_by_cofactor();
assert_eq!(a.x, b.x);
assert_eq!(a.y, a.z);
assert_eq!(b.y, b.z);
assert!(a.y != b.y);
assert!(a.z != b.z);
assert!(a.is_identity().unwrap_u8() == 1);
assert!(b.is_identity().unwrap_u8() == 1);
for point in EIGHT_TORSION.iter() {
assert!(point.mul_by_cofactor().is_identity().unwrap_u8() == 1);
}
}
#[test]
fn test_mul_consistency() {
let a = Fr([
0x21e61211d9934f2e,
0xa52c058a693c3e07,
0x9ccb77bfb12d6360,
0x07df2470ec94398e,
]);
let b = Fr([
0x03336d1cbe19dbe0,
0x0153618f6156a536,
0x2604c9e1fc3c6b15,
0x04ae581ceb028720,
]);
let c = Fr([
0xd7abf5bb24683f4c,
0x9d7712cc274b7c03,
0x973293db9683789f,
0x0b677e29380a97a7,
]);
assert_eq!(a * b, c);
let p = JubJubExtended::from(JubJubAffine {
x: BlsScalar::from_raw([
0x81c571e5d883cfb0,
0x049f7a686f147029,
0xf539c860bc3ea21f,
0x4284715b7ccc8162,
]),
y: BlsScalar::from_raw([
0xbf096275684bb8ca,
0xc7ba245890af256d,
0x59119f3e86380eb0,
0x3793de182f9fb1d2,
]),
})
.mul_by_cofactor();
assert_eq!(p * c, (p * a) * b);
assert_eq!(p * c, (p.to_niels() * a) * b);
assert_eq!(p.to_niels() * c, (p * a) * b);
assert_eq!(p.to_niels() * c, (p.to_niels() * a) * b);
let p_affine_niels = JubJubAffine::from(p).to_niels();
assert_eq!(p * c, (p_affine_niels * a) * b);
assert_eq!(p_affine_niels * c, (p * a) * b);
assert_eq!(p_affine_niels * c, (p_affine_niels * a) * b);
}
#[test]
fn test_serialization_consistency() {
let gen = FULL_GENERATOR.mul_by_cofactor();
let mut p = gen;
let y = vec![
[
203, 85, 12, 213, 56, 234, 12, 193, 19, 132, 128, 64, 142, 110,
170, 185, 179, 108, 97, 63, 13, 211, 247, 120, 79, 219, 110, 234,
131, 123, 19, 215,
],
[
113, 154, 240, 230, 224, 198, 208, 170, 104, 15, 59, 126, 151, 222,
233, 195, 203, 195, 167, 129, 89, 121, 240, 142, 51, 166, 64, 250,
184, 202, 154, 177,
],
[
197, 41, 93, 209, 203, 55, 164, 174, 88, 0, 90, 199, 1, 156, 149,
141, 240, 29, 14, 82, 86, 225, 126, 129, 186, 157, 148, 162, 219,
51, 156, 199,
],
[
182, 117, 250, 241, 81, 196, 199, 227, 151, 74, 243, 17, 221, 97,
200, 139, 192, 83, 231, 35, 214, 14, 95, 69, 130, 201, 4, 116, 177,
19, 179, 0,
],
[
118, 41, 29, 200, 60, 189, 119, 252, 78, 40, 230, 18, 208, 221, 38,
214, 176, 250, 4, 10, 77, 101, 26, 216, 193, 198, 226, 84, 25, 177,
230, 185,
],
[
226, 189, 227, 208, 112, 117, 136, 98, 72, 38, 211, 167, 254, 82,
174, 113, 112, 166, 138, 171, 166, 113, 52, 251, 129, 197, 138, 45,
195, 7, 61, 140,
],
[
38, 198, 156, 196, 146, 225, 55, 163, 138, 178, 157, 128, 115, 135,
204, 215, 0, 33, 171, 20, 60, 32, 142, 209, 33, 233, 125, 146, 207,
12, 16, 24,
],
[
17, 187, 231, 83, 165, 36, 232, 184, 140, 205, 195, 252, 166, 85,
59, 86, 3, 226, 211, 67, 179, 29, 238, 181, 102, 142, 58, 63, 57,
89, 174, 138,
],
[
210, 159, 80, 16, 181, 39, 221, 204, 224, 144, 145, 79, 54, 231, 8,
140, 142, 216, 93, 190, 183, 116, 174, 63, 33, 242, 177, 118, 148,
40, 241, 203,
],
[
0, 143, 107, 102, 149, 187, 27, 124, 18, 10, 98, 28, 113, 123, 121,
185, 29, 152, 14, 130, 149, 28, 87, 35, 135, 135, 153, 54, 112, 53,
54, 68,
],
[
178, 131, 85, 160, 214, 51, 208, 157, 196, 152, 247, 93, 202, 56,
81, 239, 155, 122, 59, 188, 237, 253, 11, 169, 208, 236, 12, 4,
163, 211, 88, 97,
],
[
246, 194, 231, 195, 159, 101, 180, 133, 80, 21, 185, 220, 195, 115,
144, 12, 90, 150, 44, 117, 8, 156, 168, 248, 206, 41, 60, 82, 67,
75, 57, 67,
],
[
212, 205, 171, 153, 113, 16, 194, 241, 224, 43, 177, 110, 190, 248,
22, 201, 208, 166, 2, 83, 134, 130, 85, 129, 166, 136, 185, 191,
163, 38, 54, 10,
],
[
8, 60, 190, 39, 153, 222, 119, 23, 142, 237, 12, 110, 146, 9, 19,
219, 143, 64, 161, 99, 199, 77, 39, 148, 70, 213, 246, 227, 150,
178, 237, 178,
],
[
11, 114, 217, 160, 101, 37, 100, 220, 56, 114, 42, 31, 138, 33, 84,
157, 214, 167, 73, 233, 115, 81, 124, 134, 15, 31, 181, 60, 184,
130, 175, 159,
],
[
141, 238, 235, 202, 241, 32, 210, 10, 127, 230, 54, 31, 146, 80,
247, 9, 107, 124, 0, 26, 203, 16, 237, 34, 214, 147, 133, 15, 29,
236, 37, 88,
],
];
for expected_serialized in y {
assert!(p.is_on_curve_vartime());
let affine = JubJubAffine::from(p);
let serialized = affine.to_bytes();
let deserialized = JubJubAffine::from_bytes(&serialized).unwrap();
assert_eq!(affine, deserialized);
assert_eq!(expected_serialized, serialized);
p = p + &gen;
}
}
pub fn dhke(secret: &Fr, public: &JubJubExtended) -> JubJubAffine {
public.mul(secret).into()
}