diff --git a/Cargo.lock b/Cargo.lock index 918764b7..5004588f 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -132,7 +132,7 @@ dependencies = [ "bellman 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", "blake2b_simd 0.4.1 (git+https://github.com/oconnor663/blake2b_simd.git)", - "bn 0.4.4 (git+https://github.com/paritytech/bn)", + "bn 0.4.4 (git+https://github.com/paritytech/bn?branch=upgrade)", "lazy_static 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "primitives 0.1.0", @@ -174,7 +174,7 @@ dependencies = [ [[package]] name = "bn" version = "0.4.4" -source = "git+https://github.com/paritytech/bn#2a71dbde5ca93451c8da2135767896a64483759e" +source = "git+https://github.com/paritytech/bn?branch=upgrade#e9f64f518d0ae64cb4f9a8c7c544e3ff0ef82584" dependencies = [ "byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "crunchy 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2010,7 +2010,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc.git?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)" = "" "checksum blake2b_simd 0.4.1 (git+https://github.com/oconnor663/blake2b_simd.git)" = "" -"checksum bn 0.4.4 (git+https://github.com/paritytech/bn)" = "" +"checksum bn 0.4.4 (git+https://github.com/paritytech/bn?branch=upgrade)" = "" "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" "checksum bytes 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "40ade3d27603c2cb345eb0912aec461a6dec7e06a4ae48589904e808335c7afa" "checksum cc 1.0.28 (registry+https://github.com/rust-lang/crates.io-index)" = "bb4a8b715cb4597106ea87c7c84b2f1d452c7492033765df7f32651e66fcf749" diff --git a/crypto/Cargo.toml b/crypto/Cargo.toml index ef322600..4a328180 100644 --- a/crypto/Cargo.toml +++ b/crypto/Cargo.toml @@ -12,7 +12,7 @@ sapling-crypto = { git = "https://github.com/zcash-hackworks/sapling-crypto.git" serde_json = "1.0" siphasher = "0.1.1" primitives = { path = "../primitives" } -bn = { git = "https://github.com/paritytech/bn" } +bn = { git = "https://github.com/paritytech/bn", branch = "upgrade" } serde = "1.0" serde_derive = "1.0" rustc-hex = "2" diff --git a/crypto/src/pghr13.rs b/crypto/src/pghr13.rs index 9c6615ca..f7890167 100644 --- a/crypto/src/pghr13.rs +++ b/crypto/src/pghr13.rs @@ -1,5 +1,6 @@ -pub use bn::{Fr, G1, G2, Group}; +pub use bn::{Fr, Fq, G1, G2, Group, arith::U256, AffineG1}; use bn::pairing; +use std::ops::Neg; #[derive(Clone)] pub struct VerifyingKey { @@ -19,6 +20,13 @@ impl ::std::fmt::Debug for VerifyingKey { } } +#[derive(Debug)] +pub enum Error { + InvalidFieldElement, + InvalidCurvePoint, + InvalidRawInput, +} + #[derive(Clone)] pub struct Proof { pub a: G1, @@ -31,6 +39,57 @@ pub struct Proof { pub h: G1, } +lazy_static! { + pub static ref FQ: U256 = U256::from([ + 0x3c208c16d87cfd47, + 0x97816a916871ca8d, + 0xb85045b68181585d, + 0x30644e72e131a029 + ]); + + pub static ref G1_B: Fq = Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed"); + + pub static ref FQ_MINUS3_DIV4: Fq = + Fq::from_u256(3.into()).expect("3 is a valid field element and static; qed").neg() * + Fq::from_u256(4.into()).expect("4 is a valid field element and static; qed").inverse() + .expect("4 has inverse in Fq and is static; qed"); +} + +// Shanks’s algorithm for q ≡ 3 (mod 4) +// (FQ mod 4 = 3) +fn fq_sqrt(a: Fq) -> Option { + let a1 = a.pow(*FQ_MINUS3_DIV4); + let a1a = a1 * a; + let a0 = a1 * (a1a); + + let mut am1 = *FQ; + am1.sub(&1.into(), &*FQ); + + if a0 == Fq::from_u256(am1).unwrap() { + None + } else { + Some(a1a) + } +} + +fn g1_from_compressed(data: &[u8]) -> Result { + if data.len() != 33 { return Err(Error::InvalidRawInput); } + + let sign = data[0]; + let fq = deseerialize_fq(&data[1..])?; + let x = fq; + let y_squared = (fq * fq * fq) + *G1_B; + let mut y = fq_sqrt(y_squared).ok_or(Error::InvalidFieldElement)?; + + if sign == 2 { y = y.neg(); } + + AffineG1::new(x, y).map_err(|_| Error::InvalidCurvePoint).map(Into::into) +} + +fn deseerialize_fq(data: &[u8]) -> Result { + Ok(Fq::from_slice(data).map_err(|_| Error::InvalidRawInput)?) +} + pub fn verify(vk: &VerifyingKey, primary_input: &[Fr], proof: &Proof) -> bool { let p2 = G2::one(); @@ -47,8 +106,34 @@ pub fn verify(vk: &VerifyingKey, primary_input: &[Fr], proof: &Proof) -> bool { // 3. check same coefficients were used: pairing(proof.k, vk.gamma) == - pairing(acc + proof.a + proof.c, vk.gamma_beta_2) * pairing(vk.gamma_beta_1, proof.b) && + pairing(acc + proof.a + proof.c, vk.gamma_beta_2) * pairing(vk.gamma_beta_1, proof.b) && + // 4. check QAP divisibility + pairing(acc + proof.a, proof.b) == pairing(proof.h, vk.z) * pairing(proof.c, p2) +} - // 4. check QAP divisibility - pairing(acc + proof.a, proof.b) == pairing(proof.h, vk.z) * pairing(proof.c, p2) +#[cfg(test)] +mod tests { + + use super::*; + + fn hex(s: &'static str) -> Vec { + use hex::FromHex; + s.from_hex().unwrap() + } + + #[test] + fn sqrt() { + let fq1 = Fq::from_str("5204065062716160319596273903996315000119019512886596366359652578430118331601").unwrap(); + let fq2 = Fq::from_str("348579348568").unwrap(); + + assert_eq!(fq1, fq_sqrt(fq2).expect("348579348568 is quadratic residue")); + } + + #[test] + fn g1_deserialize() { + let g1 = g1_from_compressed(&hex("0230644e72e131a029b85045b68181585d97816a916871ca8d3c208c16d87cfd46")).expect("Invalid g1 decompress result"); + assert_eq!(g1.x(), Fq::from_str("21888242871839275222246405745257275088696311157297823662689037894645226208582").unwrap()); + assert_eq!(g1.y(), Fq::from_str("3969792565221544645472939191694882283483352126195956956354061729942568608776").unwrap()); + assert_eq!(g1.z(), Fq::one()); + } } \ No newline at end of file