Add 'sapling-crypto/' from commit '21084bde2019c04bd34208e63c3560fe2c02fb0e'

git-subtree-dir: sapling-crypto
git-subtree-mainline: 9f748554d0
git-subtree-split: 21084bde20
This commit is contained in:
Jack Grigg
2018-08-28 23:04:13 +01:00
39 changed files with 12220 additions and 0 deletions
+523
View File
@@ -0,0 +1,523 @@
use pairing::{
Field,
SqrtField,
PrimeField,
PrimeFieldRepr,
BitIterator
};
use super::{
JubjubEngine,
JubjubParams,
Unknown,
PrimeOrder,
montgomery
};
use rand::{
Rng
};
use std::marker::PhantomData;
use std::io::{
self,
Write,
Read
};
// Represents the affine point (X/Z, Y/Z) via the extended
// twisted Edwards coordinates.
//
// See "Twisted Edwards Curves Revisited"
// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson
pub struct Point<E: JubjubEngine, Subgroup> {
x: E::Fr,
y: E::Fr,
t: E::Fr,
z: E::Fr,
_marker: PhantomData<Subgroup>
}
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
{
Point {
x: from.x,
y: from.y,
t: from.t,
z: from.z,
_marker: PhantomData
}
}
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
{
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
{
convert_subgroup(&p)
}
}
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
{
fn clone(&self) -> Self {
convert_subgroup(self)
}
}
impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
fn eq(&self, other: &Point<E, Subgroup>) -> bool {
// p1 = (x1/z1, y1/z1)
// p2 = (x2/z2, y2/z2)
// Deciding that these two points are equal is a matter of
// determining that x1/z1 = x2/z2, or equivalently that
// x1*z2 = x2*z1, and similarly for y.
let mut x1 = self.x;
x1.mul_assign(&other.z);
let mut y1 = self.y;
y1.mul_assign(&other.z);
let mut x2 = other.x;
x2.mul_assign(&self.z);
let mut y2 = other.y;
y2.mul_assign(&self.z);
x1 == x2 && y1 == y2
}
}
impl<E: JubjubEngine> Point<E, Unknown> {
pub fn read<R: Read>(
reader: R,
params: &E::Params
) -> io::Result<Self>
{
let mut y_repr = <E::Fr as PrimeField>::Repr::default();
y_repr.read_le(reader)?;
let x_sign = (y_repr.as_ref()[3] >> 63) == 1;
y_repr.as_mut()[3] &= 0x7fffffffffffffff;
match E::Fr::from_repr(y_repr) {
Ok(y) => {
match Self::get_for_y(y, x_sign, params) {
Some(p) => Ok(p),
None => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "not on curve"))
}
}
},
Err(_) => {
Err(io::Error::new(io::ErrorKind::InvalidInput, "y is not in field"))
}
}
}
pub fn get_for_y(y: E::Fr, sign: bool, params: &E::Params) -> Option<Self>
{
// Given a y on the curve, x^2 = (y^2 - 1) / (dy^2 + 1)
// This is defined for all valid y-coordinates,
// as dy^2 + 1 = 0 has no solution in Fr.
// tmp1 = y^2
let mut tmp1 = y;
tmp1.square();
// tmp2 = (y^2 * d) + 1
let mut tmp2 = tmp1;
tmp2.mul_assign(params.edwards_d());
tmp2.add_assign(&E::Fr::one());
// tmp1 = y^2 - 1
tmp1.sub_assign(&E::Fr::one());
match tmp2.inverse() {
Some(tmp2) => {
// tmp1 = (y^2 - 1) / (dy^2 + 1)
tmp1.mul_assign(&tmp2);
match tmp1.sqrt() {
Some(mut x) => {
if x.into_repr().is_odd() != sign {
x.negate();
}
let mut t = x;
t.mul_assign(&y);
Some(Point {
x: x,
y: y,
t: t,
z: E::Fr::one(),
_marker: PhantomData
})
},
None => None
}
},
None => None
}
}
/// This guarantees the point is in the prime order subgroup
#[must_use]
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder>
{
let tmp = self.double(params)
.double(params)
.double(params);
convert_subgroup(&tmp)
}
pub fn rand<R: Rng>(rng: &mut R, params: &E::Params) -> Self
{
loop {
let y: E::Fr = rng.gen();
if let Some(p) = Self::get_for_y(y, rng.gen(), params) {
return p;
}
}
}
}
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
pub fn write<W: Write>(
&self,
writer: W
) -> io::Result<()>
{
let (x, y) = self.into_xy();
assert_eq!(E::Fr::NUM_BITS, 255);
let x_repr = x.into_repr();
let mut y_repr = y.into_repr();
if x_repr.is_odd() {
y_repr.as_mut()[3] |= 0x8000000000000000u64;
}
y_repr.write_le(writer)
}
/// Convert from a Montgomery point
pub fn from_montgomery(
m: &montgomery::Point<E, Subgroup>,
params: &E::Params
) -> Self
{
match m.into_xy() {
None => {
// Map the point at infinity to the neutral element.
Point::zero()
},
Some((x, y)) => {
// The map from a Montgomery curve is defined as:
// (x, y) -> (u, v) where
// u = x / y
// v = (x - 1) / (x + 1)
//
// This map is not defined for y = 0 and x = -1.
//
// y = 0 is a valid point only for x = 0:
// y^2 = x^3 + A.x^2 + x
// 0 = x^3 + A.x^2 + x
// 0 = x(x^2 + A.x + 1)
// We have: x = 0 OR x^2 + A.x + 1 = 0
// x^2 + A.x + 1 = 0
// (2.x + A)^2 = A^2 - 4 (Complete the square.)
// The left hand side is a square, and so if A^2 - 4
// is nonsquare, there is no solution. Indeed, A^2 - 4
// is nonsquare.
//
// (0, 0) is a point of order 2, and so we map it to
// (0, -1) in the twisted Edwards curve, which is the
// only point of order 2 that is not the neutral element.
if y.is_zero() {
// This must be the point (0, 0) as above.
let mut neg1 = E::Fr::one();
neg1.negate();
Point {
x: E::Fr::zero(),
y: neg1,
t: E::Fr::zero(),
z: E::Fr::one(),
_marker: PhantomData
}
} else {
// Otherwise, as stated above, the mapping is still
// not defined at x = -1. However, x = -1 is not
// on the curve when A - 2 is nonsquare:
// y^2 = x^3 + A.x^2 + x
// y^2 = (-1) + A + (-1)
// y^2 = A - 2
// Indeed, A - 2 is nonsquare.
//
// We need to map into (projective) extended twisted
// Edwards coordinates (X, Y, T, Z) which represents
// the point (X/Z, Y/Z) with Z nonzero and T = XY/Z.
//
// Thus, we compute...
//
// u = x(x + 1)
// v = y(x - 1)
// t = x(x - 1)
// z = y(x + 1) (Cannot be nonzero, as above.)
//
// ... which represents the point ( x / y , (x - 1) / (x + 1) )
// as required by the mapping and preserves the property of
// the auxiliary coordinate t.
//
// We need to scale the coordinate, so u and t will have
// an extra factor s.
// u = xs
let mut u = x;
u.mul_assign(params.scale());
// v = x - 1
let mut v = x;
v.sub_assign(&E::Fr::one());
// t = xs(x - 1)
let mut t = u;
t.mul_assign(&v);
// z = (x + 1)
let mut z = x;
z.add_assign(&E::Fr::one());
// u = xs(x + 1)
u.mul_assign(&z);
// z = y(x + 1)
z.mul_assign(&y);
// v = y(x - 1)
v.mul_assign(&y);
Point {
x: u,
y: v,
t: t,
z: z,
_marker: PhantomData
}
}
}
}
}
/// Attempts to cast this as a prime order element, failing if it's
/// not in the prime order subgroup.
pub fn as_prime_order(&self, params: &E::Params) -> Option<Point<E, PrimeOrder>> {
if self.mul(E::Fs::char(), params) == Point::zero() {
Some(convert_subgroup(self))
} else {
None
}
}
pub fn zero() -> Self {
Point {
x: E::Fr::zero(),
y: E::Fr::one(),
t: E::Fr::zero(),
z: E::Fr::one(),
_marker: PhantomData
}
}
pub fn into_xy(&self) -> (E::Fr, E::Fr)
{
let zinv = self.z.inverse().unwrap();
let mut x = self.x;
x.mul_assign(&zinv);
let mut y = self.y;
y.mul_assign(&zinv);
(x, y)
}
#[must_use]
pub fn negate(&self) -> Self {
let mut p = self.clone();
p.x.negate();
p.t.negate();
p
}
#[must_use]
pub fn double(&self, _: &E::Params) -> Self {
// See "Twisted Edwards Curves Revisited"
// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson
// Section 3.3
// http://hyperelliptic.org/EFD/g1p/auto-twisted-extended.html#doubling-dbl-2008-hwcd
// A = X1^2
let mut a = self.x;
a.square();
// B = Y1^2
let mut b = self.y;
b.square();
// C = 2*Z1^2
let mut c = self.z;
c.square();
c.double();
// D = a*A
// = -A
let mut d = a;
d.negate();
// E = (X1+Y1)^2 - A - B
let mut e = self.x;
e.add_assign(&self.y);
e.square();
e.add_assign(&d); // -A = D
e.sub_assign(&b);
// G = D+B
let mut g = d;
g.add_assign(&b);
// F = G-C
let mut f = g;
f.sub_assign(&c);
// H = D-B
let mut h = d;
h.sub_assign(&b);
// X3 = E*F
let mut x3 = e;
x3.mul_assign(&f);
// Y3 = G*H
let mut y3 = g;
y3.mul_assign(&h);
// T3 = E*H
let mut t3 = e;
t3.mul_assign(&h);
// Z3 = F*G
let mut z3 = f;
z3.mul_assign(&g);
Point {
x: x3,
y: y3,
t: t3,
z: z3,
_marker: PhantomData
}
}
#[must_use]
pub fn add(&self, other: &Self, params: &E::Params) -> Self
{
// See "Twisted Edwards Curves Revisited"
// Huseyin Hisil, Kenneth Koon-Ho Wong, Gary Carter, and Ed Dawson
// 3.1 Unified Addition in E^e
// A = x1 * x2
let mut a = self.x;
a.mul_assign(&other.x);
// B = y1 * y2
let mut b = self.y;
b.mul_assign(&other.y);
// C = d * t1 * t2
let mut c = params.edwards_d().clone();
c.mul_assign(&self.t);
c.mul_assign(&other.t);
// D = z1 * z2
let mut d = self.z;
d.mul_assign(&other.z);
// H = B - aA
// = B + A
let mut h = b;
h.add_assign(&a);
// E = (x1 + y1) * (x2 + y2) - A - B
// = (x1 + y1) * (x2 + y2) - H
let mut e = self.x;
e.add_assign(&self.y);
{
let mut tmp = other.x;
tmp.add_assign(&other.y);
e.mul_assign(&tmp);
}
e.sub_assign(&h);
// F = D - C
let mut f = d;
f.sub_assign(&c);
// G = D + C
let mut g = d;
g.add_assign(&c);
// x3 = E * F
let mut x3 = e;
x3.mul_assign(&f);
// y3 = G * H
let mut y3 = g;
y3.mul_assign(&h);
// t3 = E * H
let mut t3 = e;
t3.mul_assign(&h);
// z3 = F * G
let mut z3 = f;
z3.mul_assign(&g);
Point {
x: x3,
y: y3,
t: t3,
z: z3,
_marker: PhantomData
}
}
#[must_use]
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
&self,
scalar: S,
params: &E::Params
) -> Self
{
// Standard double-and-add scalar multiplication
let mut res = Self::zero();
for b in BitIterator::new(scalar.into()) {
res = res.double(params);
if b {
res = res.add(self, params);
}
}
res
}
}
File diff suppressed because it is too large Load Diff
+435
View File
@@ -0,0 +1,435 @@
//! Jubjub is a twisted Edwards curve defined over the BLS12-381 scalar
//! field, Fr. It takes the form `-x^2 + y^2 = 1 + dx^2y^2` with
//! `d = -(10240/10241)`. It is birationally equivalent to a Montgomery
//! curve of the form `y^2 = x^3 + Ax^2 + x` with `A = 40962`. This
//! value `A` is the smallest integer choice such that:
//!
//! * `(A - 2) / 4` is a small integer (`10240`).
//! * `A^2 - 4` is quadratic nonresidue.
//! * The group order of the curve and its quadratic twist has a large
//! prime factor.
//!
//! Jubjub has `s = 0x0e7db4ea6533afa906673b0101343b00a6682093ccc81082d0970e5ed6f72cb7`
//! as the prime subgroup order, with cofactor 8. (The twist has
//! cofactor 4.)
//!
//! It is a complete twisted Edwards curve, so the equivalence with
//! the Montgomery curve forms a group isomorphism, allowing points
//! to be freely converted between the two forms.
use pairing::{
Engine,
Field,
PrimeField,
SqrtField
};
use group_hash::group_hash;
use constants;
use pairing::bls12_381::{
Bls12,
Fr
};
/// This is an implementation of the twisted Edwards Jubjub curve.
pub mod edwards;
/// This is an implementation of the birationally equivalent
/// Montgomery curve.
pub mod montgomery;
/// This is an implementation of the scalar field for Jubjub.
pub mod fs;
#[cfg(test)]
pub mod tests;
/// Point of unknown order.
pub enum Unknown { }
/// Point of prime order.
pub enum PrimeOrder { }
/// Fixed generators of the Jubjub curve of unknown
/// exponent.
#[derive(Copy, Clone)]
pub enum FixedGenerators {
/// The prover will demonstrate knowledge of discrete log
/// with respect to this base when they are constructing
/// a proof, in order to authorize proof construction.
ProofGenerationKey = 0,
/// The note commitment is randomized over this generator.
NoteCommitmentRandomness = 1,
/// The node commitment is randomized again by the position
/// in order to supply the nullifier computation with a
/// unique input w.r.t. the note being spent, to prevent
/// Faerie gold attacks.
NullifierPosition = 2,
/// The value commitment is used to check balance between
/// inputs and outputs. The value is placed over this
/// generator.
ValueCommitmentValue = 3,
/// The value commitment is randomized over this generator,
/// for privacy.
ValueCommitmentRandomness = 4,
/// The spender proves discrete log with respect to this
/// base at spend time.
SpendingKeyGenerator = 5,
Max = 6
}
pub trait ToUniform {
fn to_uniform(digest: &[u8]) -> Self;
}
/// This is an extension to the pairing Engine trait which
/// offers a scalar field for the embedded curve (Jubjub)
/// and some pre-computed parameters.
pub trait JubjubEngine: Engine {
/// The scalar field of the Jubjub curve
type Fs: PrimeField + SqrtField + ToUniform;
/// The parameters of Jubjub and the Sapling protocol
type Params: JubjubParams<Self>;
}
/// The pre-computed parameters for Jubjub, including curve
/// constants and various limits and window tables.
pub trait JubjubParams<E: JubjubEngine>: Sized {
/// The `d` constant of the twisted Edwards curve.
fn edwards_d(&self) -> &E::Fr;
/// The `A` constant of the birationally equivalent Montgomery curve.
fn montgomery_a(&self) -> &E::Fr;
/// The `A` constant, doubled.
fn montgomery_2a(&self) -> &E::Fr;
/// The scaling factor used for conversion from the Montgomery form.
fn scale(&self) -> &E::Fr;
/// Returns the generators (for each segment) used in all Pedersen commitments.
fn pedersen_hash_generators(&self) -> &[edwards::Point<E, PrimeOrder>];
/// Returns the exp table for Pedersen hashes.
fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<edwards::Point<E, PrimeOrder>>>];
/// Returns the maximum number of chunks per segment of the Pedersen hash.
fn pedersen_hash_chunks_per_generator(&self) -> usize;
/// Returns the pre-computed window tables [-4, 3, 2, 1, 1, 2, 3, 4] of different
/// magnitudes of the Pedersen hash segment generators.
fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(E::Fr, E::Fr)>>];
/// Returns the number of chunks needed to represent a full scalar during fixed-base
/// exponentiation.
fn fixed_base_chunks_per_generator(&self) -> usize;
/// Returns a fixed generator.
fn generator(&self, base: FixedGenerators) -> &edwards::Point<E, PrimeOrder>;
/// Returns a window table [0, 1, ..., 8] for different magnitudes of some
/// fixed generator.
fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>];
/// Returns the window size for exponentiation of Pedersen hash generators
/// outside the circuit
fn pedersen_hash_exp_window_size() -> u32;
}
impl JubjubEngine for Bls12 {
type Fs = self::fs::Fs;
type Params = JubjubBls12;
}
pub struct JubjubBls12 {
edwards_d: Fr,
montgomery_a: Fr,
montgomery_2a: Fr,
scale: Fr,
pedersen_hash_generators: Vec<edwards::Point<Bls12, PrimeOrder>>,
pedersen_hash_exp: Vec<Vec<Vec<edwards::Point<Bls12, PrimeOrder>>>>,
pedersen_circuit_generators: Vec<Vec<Vec<(Fr, Fr)>>>,
fixed_base_generators: Vec<edwards::Point<Bls12, PrimeOrder>>,
fixed_base_circuit_generators: Vec<Vec<Vec<(Fr, Fr)>>>,
}
impl JubjubParams<Bls12> for JubjubBls12 {
fn edwards_d(&self) -> &Fr { &self.edwards_d }
fn montgomery_a(&self) -> &Fr { &self.montgomery_a }
fn montgomery_2a(&self) -> &Fr { &self.montgomery_2a }
fn scale(&self) -> &Fr { &self.scale }
fn pedersen_hash_generators(&self) -> &[edwards::Point<Bls12, PrimeOrder>] {
&self.pedersen_hash_generators
}
fn pedersen_hash_exp_table(&self) -> &[Vec<Vec<edwards::Point<Bls12, PrimeOrder>>>] {
&self.pedersen_hash_exp
}
fn pedersen_hash_chunks_per_generator(&self) -> usize {
63
}
fn fixed_base_chunks_per_generator(&self) -> usize {
84
}
fn pedersen_circuit_generators(&self) -> &[Vec<Vec<(Fr, Fr)>>] {
&self.pedersen_circuit_generators
}
fn generator(&self, base: FixedGenerators) -> &edwards::Point<Bls12, PrimeOrder>
{
&self.fixed_base_generators[base as usize]
}
fn circuit_generators(&self, base: FixedGenerators) -> &[Vec<(Fr, Fr)>]
{
&self.fixed_base_circuit_generators[base as usize][..]
}
fn pedersen_hash_exp_window_size() -> u32 {
8
}
}
impl JubjubBls12 {
pub fn new() -> Self {
let montgomery_a = Fr::from_str("40962").unwrap();
let mut montgomery_2a = montgomery_a;
montgomery_2a.double();
let mut tmp_params = JubjubBls12 {
// d = -(10240/10241)
edwards_d: Fr::from_str("19257038036680949359750312669786877991949435402254120286184196891950884077233").unwrap(),
// A = 40962
montgomery_a: montgomery_a,
// 2A = 2.A
montgomery_2a: montgomery_2a,
// scaling factor = sqrt(4 / (a - d))
scale: Fr::from_str("17814886934372412843466061268024708274627479829237077604635722030778476050649").unwrap(),
// We'll initialize these below
pedersen_hash_generators: vec![],
pedersen_hash_exp: vec![],
pedersen_circuit_generators: vec![],
fixed_base_generators: vec![],
fixed_base_circuit_generators: vec![],
};
fn find_group_hash<E: JubjubEngine>(
m: &[u8],
personalization: &[u8; 8],
params: &E::Params
) -> edwards::Point<E, PrimeOrder>
{
let mut tag = m.to_vec();
let i = tag.len();
tag.push(0u8);
loop {
let gh = group_hash(
&tag,
personalization,
params
);
// We don't want to overflow and start reusing generators
assert!(tag[i] != u8::max_value());
tag[i] += 1;
if let Some(gh) = gh {
break gh;
}
}
}
// Create the bases for the Pedersen hashes
{
let mut pedersen_hash_generators = vec![];
for m in 0..5 {
use byteorder::{WriteBytesExt, LittleEndian};
let mut segment_number = [0u8; 4];
(&mut segment_number[0..4]).write_u32::<LittleEndian>(m).unwrap();
pedersen_hash_generators.push(
find_group_hash(
&segment_number,
constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION,
&tmp_params
)
);
}
// Check for duplicates, far worse than spec inconsistencies!
for (i, p1) in pedersen_hash_generators.iter().enumerate() {
if p1 == &edwards::Point::zero() {
panic!("Neutral element!");
}
for p2 in pedersen_hash_generators.iter().skip(i+1) {
if p1 == p2 {
panic!("Duplicate generator!");
}
}
}
tmp_params.pedersen_hash_generators = pedersen_hash_generators;
}
// Create the exp table for the Pedersen hash generators
{
let mut pedersen_hash_exp = vec![];
for g in &tmp_params.pedersen_hash_generators {
let mut g = g.clone();
let window = JubjubBls12::pedersen_hash_exp_window_size();
let mut tables = vec![];
let mut num_bits = 0;
while num_bits <= fs::Fs::NUM_BITS {
let mut table = Vec::with_capacity(1 << window);
let mut base = edwards::Point::zero();
for _ in 0..(1 << window) {
table.push(base.clone());
base = base.add(&g, &tmp_params);
}
tables.push(table);
num_bits += window;
for _ in 0..window {
g = g.double(&tmp_params);
}
}
pedersen_hash_exp.push(tables);
}
tmp_params.pedersen_hash_exp = pedersen_hash_exp;
}
// Create the bases for other parts of the protocol
{
let mut fixed_base_generators = vec![edwards::Point::zero(); FixedGenerators::Max as usize];
fixed_base_generators[FixedGenerators::ProofGenerationKey as usize] =
find_group_hash(&[], constants::PROOF_GENERATION_KEY_BASE_GENERATOR_PERSONALIZATION, &tmp_params);
fixed_base_generators[FixedGenerators::NoteCommitmentRandomness as usize] =
find_group_hash(b"r", constants::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp_params);
fixed_base_generators[FixedGenerators::NullifierPosition as usize] =
find_group_hash(&[], constants::NULLIFIER_POSITION_IN_TREE_GENERATOR_PERSONALIZATION, &tmp_params);
fixed_base_generators[FixedGenerators::ValueCommitmentValue as usize] =
find_group_hash(b"v", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
fixed_base_generators[FixedGenerators::ValueCommitmentRandomness as usize] =
find_group_hash(b"r", constants::VALUE_COMMITMENT_GENERATOR_PERSONALIZATION, &tmp_params);
fixed_base_generators[FixedGenerators::SpendingKeyGenerator as usize] =
find_group_hash(&[], constants::SPENDING_KEY_GENERATOR_PERSONALIZATION, &tmp_params);
// Check for duplicates, far worse than spec inconsistencies!
for (i, p1) in fixed_base_generators.iter().enumerate() {
if p1 == &edwards::Point::zero() {
panic!("Neutral element!");
}
for p2 in fixed_base_generators.iter().skip(i+1) {
if p1 == p2 {
panic!("Duplicate generator!");
}
}
}
tmp_params.fixed_base_generators = fixed_base_generators;
}
// Create the 2-bit window table lookups for each 4-bit
// "chunk" in each segment of the Pedersen hash
{
let mut pedersen_circuit_generators = vec![];
// Process each segment
for mut gen in tmp_params.pedersen_hash_generators.iter().cloned() {
let mut gen = montgomery::Point::from_edwards(&gen, &tmp_params);
let mut windows = vec![];
for _ in 0..tmp_params.pedersen_hash_chunks_per_generator() {
// Create (x, y) coeffs for this chunk
let mut coeffs = vec![];
let mut g = gen.clone();
// coeffs = g, g*2, g*3, g*4
for _ in 0..4 {
coeffs.push(g.into_xy().expect("cannot produce O"));
g = g.add(&gen, &tmp_params);
}
windows.push(coeffs);
// Our chunks are separated by 2 bits to prevent overlap.
for _ in 0..4 {
gen = gen.double(&tmp_params);
}
}
pedersen_circuit_generators.push(windows);
}
tmp_params.pedersen_circuit_generators = pedersen_circuit_generators;
}
// Create the 3-bit window table lookups for fixed-base
// exp of each base in the protocol.
{
let mut fixed_base_circuit_generators = vec![];
for mut gen in tmp_params.fixed_base_generators.iter().cloned() {
let mut windows = vec![];
for _ in 0..tmp_params.fixed_base_chunks_per_generator() {
let mut coeffs = vec![(Fr::zero(), Fr::one())];
let mut g = gen.clone();
for _ in 0..7 {
coeffs.push(g.into_xy());
g = g.add(&gen, &tmp_params);
}
windows.push(coeffs);
// gen = gen * 8
gen = g;
}
fixed_base_circuit_generators.push(windows);
}
tmp_params.fixed_base_circuit_generators = fixed_base_circuit_generators;
}
tmp_params
}
}
#[test]
fn test_jubjub_bls12() {
let params = JubjubBls12::new();
tests::test_suite::<Bls12>(&params);
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139d31");
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap();
let q = edwards::Point::<Bls12, _>::get_for_y(
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
false,
&params
).unwrap();
assert!(p == q);
// Same thing, but sign bit set
let test_repr = hex!("9d12b88b08dcbef8a11ee0712d94cb236ee2f4ca17317075bfafc82ce3139db1");
let p = edwards::Point::<Bls12, _>::read(&test_repr[..], &params).unwrap();
let q = edwards::Point::<Bls12, _>::get_for_y(
Fr::from_str("22440861827555040311190986994816762244378363690614952020532787748720529117853").unwrap(),
true,
&params
).unwrap();
assert!(p == q);
}
+358
View File
@@ -0,0 +1,358 @@
use pairing::{
Field,
SqrtField,
PrimeField,
PrimeFieldRepr,
BitIterator
};
use super::{
JubjubEngine,
JubjubParams,
Unknown,
PrimeOrder,
edwards
};
use rand::{
Rng
};
use std::marker::PhantomData;
// Represents the affine point (X, Y)
pub struct Point<E: JubjubEngine, Subgroup> {
x: E::Fr,
y: E::Fr,
infinity: bool,
_marker: PhantomData<Subgroup>
}
fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2>
{
Point {
x: from.x,
y: from.y,
infinity: from.infinity,
_marker: PhantomData
}
}
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
{
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
{
convert_subgroup(&p)
}
}
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
{
fn clone(&self) -> Self {
convert_subgroup(self)
}
}
impl<E: JubjubEngine, Subgroup> PartialEq for Point<E, Subgroup> {
fn eq(&self, other: &Point<E, Subgroup>) -> bool {
match (self.infinity, other.infinity) {
(true, true) => true,
(true, false) | (false, true) => false,
(false, false) => {
self.x == other.x && self.y == other.y
}
}
}
}
impl<E: JubjubEngine> Point<E, Unknown> {
pub fn get_for_x(x: E::Fr, sign: bool, params: &E::Params) -> Option<Self>
{
// Given an x on the curve, y = sqrt(x^3 + A*x^2 + x)
let mut x2 = x;
x2.square();
let mut rhs = x2;
rhs.mul_assign(params.montgomery_a());
rhs.add_assign(&x);
x2.mul_assign(&x);
rhs.add_assign(&x2);
match rhs.sqrt() {
Some(mut y) => {
if y.into_repr().is_odd() != sign {
y.negate();
}
return Some(Point {
x: x,
y: y,
infinity: false,
_marker: PhantomData
})
},
None => None
}
}
/// This guarantees the point is in the prime order subgroup
#[must_use]
pub fn mul_by_cofactor(&self, params: &E::Params) -> Point<E, PrimeOrder>
{
let tmp = self.double(params)
.double(params)
.double(params);
convert_subgroup(&tmp)
}
pub fn rand<R: Rng>(rng: &mut R, params: &E::Params) -> Self
{
loop {
let x: E::Fr = rng.gen();
match Self::get_for_x(x, rng.gen(), params) {
Some(p) => {
return p
},
None => {}
}
}
}
}
impl<E: JubjubEngine, Subgroup> Point<E, Subgroup> {
/// Convert from an Edwards point
pub fn from_edwards(
e: &edwards::Point<E, Subgroup>,
params: &E::Params
) -> Self
{
let (x, y) = e.into_xy();
if y == E::Fr::one() {
// The only solution for y = 1 is x = 0. (0, 1) is
// the neutral element, so we map this to the point
// at infinity.
Point::zero()
} else {
// The map from a twisted Edwards curve is defined as
// (x, y) -> (u, v) where
// u = (1 + y) / (1 - y)
// v = u / x
//
// This mapping is not defined for y = 1 and for x = 0.
//
// We have that y != 1 above. If x = 0, the only
// solutions for y are 1 (contradiction) or -1.
if x.is_zero() {
// (0, -1) is the point of order two which is not
// the neutral element, so we map it to (0, 0) which is
// the only affine point of order 2.
Point {
x: E::Fr::zero(),
y: E::Fr::zero(),
infinity: false,
_marker: PhantomData
}
} else {
// The mapping is defined as above.
//
// (x, y) -> (u, v) where
// u = (1 + y) / (1 - y)
// v = u / x
let mut u = E::Fr::one();
u.add_assign(&y);
{
let mut tmp = E::Fr::one();
tmp.sub_assign(&y);
u.mul_assign(&tmp.inverse().unwrap())
}
let mut v = u;
v.mul_assign(&x.inverse().unwrap());
// Scale it into the correct curve constants
v.mul_assign(params.scale());
Point {
x: u,
y: v,
infinity: false,
_marker: PhantomData
}
}
}
}
/// Attempts to cast this as a prime order element, failing if it's
/// not in the prime order subgroup.
pub fn as_prime_order(&self, params: &E::Params) -> Option<Point<E, PrimeOrder>> {
if self.mul(E::Fs::char(), params) == Point::zero() {
Some(convert_subgroup(self))
} else {
None
}
}
pub fn zero() -> Self {
Point {
x: E::Fr::zero(),
y: E::Fr::zero(),
infinity: true,
_marker: PhantomData
}
}
pub fn into_xy(&self) -> Option<(E::Fr, E::Fr)>
{
if self.infinity {
None
} else {
Some((self.x, self.y))
}
}
#[must_use]
pub fn negate(&self) -> Self {
let mut p = self.clone();
p.y.negate();
p
}
#[must_use]
pub fn double(&self, params: &E::Params) -> Self {
if self.infinity {
return Point::zero();
}
// (0, 0) is the point of order 2. Doubling
// produces the point at infinity.
if self.y == E::Fr::zero() {
return Point::zero();
}
// This is a standard affine point doubling formula
// See 4.3.2 The group law for Weierstrass curves
// Montgomery curves and the Montgomery Ladder
// Daniel J. Bernstein and Tanja Lange
let mut delta = E::Fr::one();
{
let mut tmp = params.montgomery_a().clone();
tmp.mul_assign(&self.x);
tmp.double();
delta.add_assign(&tmp);
}
{
let mut tmp = self.x;
tmp.square();
delta.add_assign(&tmp);
tmp.double();
delta.add_assign(&tmp);
}
{
let mut tmp = self.y;
tmp.double();
delta.mul_assign(&tmp.inverse().expect("y is nonzero so this must be nonzero"));
}
let mut x3 = delta;
x3.square();
x3.sub_assign(params.montgomery_a());
x3.sub_assign(&self.x);
x3.sub_assign(&self.x);
let mut y3 = x3;
y3.sub_assign(&self.x);
y3.mul_assign(&delta);
y3.add_assign(&self.y);
y3.negate();
Point {
x: x3,
y: y3,
infinity: false,
_marker: PhantomData
}
}
#[must_use]
pub fn add(&self, other: &Self, params: &E::Params) -> Self
{
// This is a standard affine point addition formula
// See 4.3.2 The group law for Weierstrass curves
// Montgomery curves and the Montgomery Ladder
// Daniel J. Bernstein and Tanja Lange
match (self.infinity, other.infinity) {
(true, true) => Point::zero(),
(true, false) => other.clone(),
(false, true) => self.clone(),
(false, false) => {
if self.x == other.x {
if self.y == other.y {
self.double(params)
} else {
Point::zero()
}
} else {
let mut delta = other.y;
delta.sub_assign(&self.y);
{
let mut tmp = other.x;
tmp.sub_assign(&self.x);
delta.mul_assign(&tmp.inverse().expect("self.x != other.x, so this must be nonzero"));
}
let mut x3 = delta;
x3.square();
x3.sub_assign(params.montgomery_a());
x3.sub_assign(&self.x);
x3.sub_assign(&other.x);
let mut y3 = x3;
y3.sub_assign(&self.x);
y3.mul_assign(&delta);
y3.add_assign(&self.y);
y3.negate();
Point {
x: x3,
y: y3,
infinity: false,
_marker: PhantomData
}
}
}
}
}
#[must_use]
pub fn mul<S: Into<<E::Fs as PrimeField>::Repr>>(
&self,
scalar: S,
params: &E::Params
) -> Self
{
// Standard double-and-add scalar multiplication
let mut res = Self::zero();
for b in BitIterator::new(scalar.into()) {
res = res.double(params);
if b {
res = res.add(self, params);
}
}
res
}
}
+416
View File
@@ -0,0 +1,416 @@
use super::{
JubjubEngine,
JubjubParams,
PrimeOrder,
montgomery,
edwards
};
use pairing::{
Field,
PrimeField,
PrimeFieldRepr,
SqrtField,
LegendreSymbol
};
use rand::{XorShiftRng, SeedableRng, Rand};
pub fn test_suite<E: JubjubEngine>(params: &E::Params) {
test_back_and_forth::<E>(params);
test_jubjub_params::<E>(params);
test_rand::<E>(params);
test_get_for::<E>(params);
test_identities::<E>(params);
test_addition_associativity::<E>(params);
test_order::<E>(params);
test_mul_associativity::<E>(params);
test_loworder::<E>(params);
test_read_write::<E>(params);
}
fn is_on_mont_curve<E: JubjubEngine, P: JubjubParams<E>>(
x: E::Fr,
y: E::Fr,
params: &P
) -> bool
{
let mut lhs = y;
lhs.square();
let mut x2 = x;
x2.square();
let mut x3 = x2;
x3.mul_assign(&x);
let mut rhs = x2;
rhs.mul_assign(params.montgomery_a());
rhs.add_assign(&x);
rhs.add_assign(&x3);
lhs == rhs
}
fn is_on_twisted_edwards_curve<E: JubjubEngine, P: JubjubParams<E>>(
x: E::Fr,
y: E::Fr,
params: &P
) -> bool
{
let mut x2 = x;
x2.square();
let mut y2 = y;
y2.square();
// -x^2 + y^2
let mut lhs = y2;
lhs.sub_assign(&x2);
// 1 + d x^2 y^2
let mut rhs = y2;
rhs.mul_assign(&x2);
rhs.mul_assign(params.edwards_d());
rhs.add_assign(&E::Fr::one());
lhs == rhs
}
fn test_loworder<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
let inf = montgomery::Point::zero();
// try to find a point of order 8
let p = loop {
let r = montgomery::Point::<E, _>::rand(rng, params).mul(E::Fs::char(), params);
let r2 = r.double(params);
let r4 = r2.double(params);
let r8 = r4.double(params);
if r2 != inf && r4 != inf && r8 == inf {
break r;
}
};
let mut loworder_points = vec![];
{
let mut tmp = p.clone();
for _ in 0..8 {
assert!(!loworder_points.contains(&tmp));
loworder_points.push(tmp.clone());
tmp = tmp.add(&p, params);
}
}
assert!(loworder_points[7] == inf);
}
fn test_mul_associativity<E: JubjubEngine>(params: &E::Params) {
use self::edwards::Point;
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..100 {
// Pick a random point and multiply it by the cofactor
let base = Point::<E, _>::rand(rng, params).mul_by_cofactor(params);
let mut a = E::Fs::rand(rng);
let b = E::Fs::rand(rng);
let c = E::Fs::rand(rng);
let res1 = base.mul(a, params).mul(b, params).mul(c, params);
let res2 = base.mul(b, params).mul(c, params).mul(a, params);
let res3 = base.mul(c, params).mul(a, params).mul(b, params);
a.mul_assign(&b);
a.mul_assign(&c);
let res4 = base.mul(a, params);
assert!(res1 == res2);
assert!(res2 == res3);
assert!(res3 == res4);
let (x, y) = res1.into_xy();
assert!(is_on_twisted_edwards_curve(x, y, params));
let (x, y) = res2.into_xy();
assert!(is_on_twisted_edwards_curve(x, y, params));
let (x, y) = res3.into_xy();
assert!(is_on_twisted_edwards_curve(x, y, params));
}
}
fn test_order<E: JubjubEngine>(params: &E::Params) {
use self::edwards::Point;
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
// The neutral element is in the prime order subgroup.
assert!(Point::<E, PrimeOrder>::zero().as_prime_order(params).is_some());
for _ in 0..50 {
// Pick a random point and multiply it by the cofactor
let base = Point::<E, _>::rand(rng, params).mul_by_cofactor(params);
// Any point multiplied by the cofactor will be in the prime
// order subgroup
assert!(base.as_prime_order(params).is_some());
}
// It's very likely that at least one out of 50 random points on the curve
// is not in the prime order subgroup.
let mut at_least_one_not_in_prime_order_subgroup = false;
for _ in 0..50 {
// Pick a random point.
let base = Point::<E, _>::rand(rng, params);
at_least_one_not_in_prime_order_subgroup |= base.as_prime_order(params).is_none();
}
assert!(at_least_one_not_in_prime_order_subgroup);
}
fn test_addition_associativity<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
use self::montgomery::Point;
let a = Point::<E, _>::rand(rng, params);
let b = Point::<E, _>::rand(rng, params);
let c = Point::<E, _>::rand(rng, params);
assert!(a.add(&b, &params).add(&c, &params) == c.add(&a, &params).add(&b, &params));
}
for _ in 0..1000 {
use self::edwards::Point;
let a = Point::<E, _>::rand(rng, params);
let b = Point::<E, _>::rand(rng, params);
let c = Point::<E, _>::rand(rng, params);
assert!(a.add(&b, &params).add(&c, &params) == c.add(&a, &params).add(&b, &params));
}
}
fn test_identities<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
{
use self::edwards::Point;
let z = Point::<E, PrimeOrder>::zero();
assert!(z.double(&params) == z);
assert!(z.negate() == z);
for _ in 0..100 {
let r = Point::<E, _>::rand(rng, params);
assert!(r.add(&Point::zero(), &params) == r);
assert!(r.add(&r.negate(), &params) == Point::zero());
}
}
{
use self::montgomery::Point;
let z = Point::<E, PrimeOrder>::zero();
assert!(z.double(&params) == z);
assert!(z.negate() == z);
for _ in 0..100 {
let r = Point::<E, _>::rand(rng, params);
assert!(r.add(&Point::zero(), &params) == r);
assert!(r.add(&r.negate(), &params) == Point::zero());
}
}
}
fn test_get_for<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let y = E::Fr::rand(rng);
let sign = bool::rand(rng);
if let Some(mut p) = edwards::Point::<E, _>::get_for_y(y, sign, params) {
assert!(p.into_xy().0.into_repr().is_odd() == sign);
p = p.negate();
assert!(
edwards::Point::<E, _>::get_for_y(y, !sign, params).unwrap()
==
p
);
}
}
}
fn test_read_write<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let e = edwards::Point::<E, _>::rand(rng, params);
let mut v = vec![];
e.write(&mut v).unwrap();
let e2 = edwards::Point::read(&v[..], params).unwrap();
assert!(e == e2);
}
}
fn test_rand<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([0x3dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let p = montgomery::Point::<E, _>::rand(rng, params);
let e = edwards::Point::<E, _>::rand(rng, params);
{
let (x, y) = p.into_xy().unwrap();
assert!(is_on_mont_curve(x, y, params));
}
{
let (x, y) = e.into_xy();
assert!(is_on_twisted_edwards_curve(x, y, params));
}
}
}
fn test_back_and_forth<E: JubjubEngine>(params: &E::Params) {
let rng = &mut XorShiftRng::from_seed([0x5dbe6259, 0x8d313d76, 0x3237db17, 0xe5bc0654]);
for _ in 0..1000 {
let s = E::Fs::rand(rng);
let edwards_p1 = edwards::Point::<E, _>::rand(rng, params);
let mont_p1 = montgomery::Point::from_edwards(&edwards_p1, params);
let mont_p2 = montgomery::Point::<E, _>::rand(rng, params);
let edwards_p2 = edwards::Point::from_montgomery(&mont_p2, params);
let mont = mont_p1.add(&mont_p2, params).mul(s, params);
let edwards = edwards_p1.add(&edwards_p2, params).mul(s, params);
assert!(
montgomery::Point::from_edwards(&edwards, params) == mont
);
assert!(
edwards::Point::from_montgomery(&mont, params) == edwards
);
}
}
fn test_jubjub_params<E: JubjubEngine>(params: &E::Params) {
// a = -1
let mut a = E::Fr::one();
a.negate();
{
// Check that 2A is consistent with A
let mut tmp = *params.montgomery_a();
tmp.double();
assert_eq!(&tmp, params.montgomery_2a());
}
{
// The twisted Edwards addition law is complete when d is nonsquare
// and a is square.
assert!(params.edwards_d().legendre() == LegendreSymbol::QuadraticNonResidue);
assert!(a.legendre() == LegendreSymbol::QuadraticResidue);
}
{
// Other convenient sanity checks regarding d
// tmp = d
let mut tmp = *params.edwards_d();
// 1 / d is nonsquare
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
// tmp = -d
tmp.negate();
// -d is nonsquare
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
// 1 / -d is nonsquare
assert!(tmp.inverse().unwrap().legendre() == LegendreSymbol::QuadraticNonResidue);
}
{
// Check that A^2 - 4 is nonsquare:
let mut tmp = params.montgomery_a().clone();
tmp.square();
tmp.sub_assign(&E::Fr::from_str("4").unwrap());
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
}
{
// Check that A - 2 is nonsquare:
let mut tmp = params.montgomery_a().clone();
tmp.sub_assign(&E::Fr::from_str("2").unwrap());
assert!(tmp.legendre() == LegendreSymbol::QuadraticNonResidue);
}
{
// Check the validity of the scaling factor
let mut tmp = a;
tmp.sub_assign(&params.edwards_d());
tmp = tmp.inverse().unwrap();
tmp.mul_assign(&E::Fr::from_str("4").unwrap());
tmp = tmp.sqrt().unwrap();
assert_eq!(&tmp, params.scale());
}
{
// Check that the number of windows per generator
// in the Pedersen hash does not allow for collisions
let mut cur = E::Fs::one().into_repr();
let mut max = E::Fs::char();
{
max.sub_noborrow(&E::Fs::one().into_repr());
max.div2();
}
let mut pacc = E::Fs::zero().into_repr();
let mut nacc = E::Fs::char();
for _ in 0..params.pedersen_hash_chunks_per_generator()
{
// tmp = cur * 4
let mut tmp = cur;
tmp.mul2();
tmp.mul2();
pacc.add_nocarry(&tmp);
nacc.sub_noborrow(&tmp);
assert!(pacc < max);
assert!(pacc < nacc);
// cur = cur * 16
for _ in 0..4 {
cur.mul2();
}
}
}
{
// Check that the number of windows for fixed-base
// scalar multiplication is sufficient for all scalars.
assert!(params.fixed_base_chunks_per_generator() * 3 >= E::Fs::NUM_BITS as usize);
// ... and that it's *just* efficient enough.
assert!((params.fixed_base_chunks_per_generator() - 1) * 3 < E::Fs::NUM_BITS as usize);
}
}