sapling-crypto/src/jubjub/mod.rs

292 lines
9.9 KiB
Rust

//! Jubjub is an elliptic curve defined over the BLS12-381 scalar field, Fr.
//! It is a Montgomery curve that takes the form `y^2 = x^3 + Ax^2 + x` where
//! `A = 40962`. This is the smallest integer choice of A such that:
//!
//! * `(A - 2) / 4` is a small integer (`10240`).
//! * `A^2 - 4` is quadratic residue.
//! * 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.)
//!
//! This curve is birationally equivalent to a twisted Edwards curve of the
//! form `-x^2 + y^2 = 1 + dx^2y^2` with `d = -(10240/10241)`. In fact, this equivalence
//! forms a group isomorphism, so points can be freely converted between the Montgomery
//! and twisted Edwards forms.
use pairing::{
Engine,
Field,
PrimeField,
SqrtField
};
use super::group_hash::group_hash;
use pairing::bls12_381::{
Bls12,
Fr
};
pub mod edwards;
pub mod montgomery;
#[cfg(test)]
pub mod tests;
/// 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.
ProvingPublicKey = 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
}
/// 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 {
type Fs: PrimeField + SqrtField;
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 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 magntitudes of some
/// fixed generator.
fn circuit_generators(&self, FixedGenerators) -> &[Vec<(E::Fr, E::Fr)>];
}
/// Point of unknown order.
pub enum Unknown { }
/// Point of prime order.
pub enum PrimeOrder { }
pub mod fs;
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_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_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][..]
}
}
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 = 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(),
pedersen_hash_generators: vec![],
pedersen_circuit_generators: vec![],
fixed_base_generators: vec![],
fixed_base_circuit_generators: vec![],
};
// Create the bases for the Pedersen hashes
{
let mut cur = 0;
let mut pedersen_hash_generators = vec![];
while pedersen_hash_generators.len() < 10 {
let gh = group_hash(&[cur], ::PEDERSEN_HASH_GENERATORS_PERSONALIZATION, &tmp);
// We don't want to overflow and start reusing generators
assert!(cur != u8::max_value());
cur += 1;
if let Some(gh) = gh {
pedersen_hash_generators.push(gh);
}
}
tmp.pedersen_hash_generators = pedersen_hash_generators;
}
// Create the bases for other parts of the protocol
{
let mut cur = 0;
let mut fixed_base_generators = vec![];
while fixed_base_generators.len() < (FixedGenerators::Max as usize) {
let gh = group_hash(&[cur], ::OTHER_PERSONALIZATION, &tmp);
// We don't want to overflow and start reusing generators
assert!(cur != u8::max_value());
cur += 1;
if let Some(gh) = gh {
fixed_base_generators.push(gh);
}
}
tmp.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![];
for mut gen in tmp.pedersen_hash_generators.iter().cloned() {
let mut gen = montgomery::Point::from_edwards(&gen, &tmp);
let mut windows = vec![];
for _ in 0..tmp.pedersen_hash_chunks_per_generator() {
let mut coeffs = vec![];
let mut g = gen.clone();
for _ in 0..4 {
coeffs.push(g.into_xy().expect("cannot produce O"));
g = g.add(&gen, &tmp);
}
windows.push(coeffs);
for _ in 0..4 {
gen = gen.double(&tmp);
}
}
pedersen_circuit_generators.push(windows);
}
tmp.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.fixed_base_generators.iter().cloned() {
let mut windows = vec![];
for _ in 0..tmp.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);
}
windows.push(coeffs);
gen = g;
}
fixed_base_circuit_generators.push(windows);
}
tmp.fixed_base_circuit_generators = fixed_base_circuit_generators;
}
tmp
}
}
#[test]
fn test_jubjub_bls12() {
let params = JubjubBls12::new();
tests::test_suite::<Bls12>(&params);
let test_repr = hex!("b9481dd1103b7d1f8578078eb429d3c476472f53e88c0eaefdf51334c7c8b98c");
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);
}