diff --git a/Cargo.lock b/Cargo.lock index 6388659..59d3951 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -121,7 +121,7 @@ dependencies = [ "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", "pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c)", + "sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=c2862a43829632d64f0654e8a9cde9680ba48076)", ] [[package]] @@ -159,7 +159,7 @@ dependencies = [ [[package]] name = "sapling-crypto" version = "0.0.1" -source = "git+https://github.com/zcash-hackworks/sapling-crypto?rev=6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c#6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c" +source = "git+https://github.com/zcash-hackworks/sapling-crypto?rev=c2862a43829632d64f0654e8a9cde9680ba48076#c2862a43829632d64f0654e8a9cde9680ba48076" 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?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)", @@ -214,7 +214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum pairing 0.14.2 (registry+https://github.com/rust-lang/crates.io-index)" = "ceda21136251c6d5a422d3d798d8ac22515a6e8d3521bb60c59a8349d36d0d57" "checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" -"checksum sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c)" = "" +"checksum sapling-crypto 0.0.1 (git+https://github.com/zcash-hackworks/sapling-crypto?rev=c2862a43829632d64f0654e8a9cde9680ba48076)" = "" "checksum typenum 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "612d636f949607bdf9b123b4a6f6d966dedf3ff669f7f045890d3a4a73948169" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" diff --git a/Cargo.toml b/Cargo.toml index 857fec3..d44c4ef 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,7 +27,7 @@ rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9" [dependencies.sapling-crypto] git = "https://github.com/zcash-hackworks/sapling-crypto" -rev = "6abfcca25ae233922ecc18a4d2d0b5cb7aab7c8c" +rev = "c2862a43829632d64f0654e8a9cde9680ba48076" [profile.release] lto = true diff --git a/include/librustzcash.h b/include/librustzcash.h index e2bf929..b48136b 100644 --- a/include/librustzcash.h +++ b/include/librustzcash.h @@ -60,6 +60,68 @@ extern "C" { unsigned char *result ); + /// Computes the signature for each Spend description, given the key + /// `ask`, the re-randomization `ar`, the 32-byte sighash `sighash`, + /// and an output `result` buffer of 64-bytes for the signature. + /// + /// This function will fail if the provided `ask` or `ar` are invalid. + bool librustzcash_sapling_spend_sig( + const unsigned char *ask, + const unsigned char *ar, + const unsigned char *sighash, + unsigned char *result + ); + + /// Creates a Sapling proving context. Please free this when you're done. + void * librustzcash_sapling_proving_ctx_init(); + + /// This function (using the proving context) constructs a Spend proof + /// given the necessary witness information. It outputs `cv` (the value + /// commitment) and `rk` (so that you don't have to compute it) along + /// with the proof. + bool librustzcash_sapling_spend_proof( + void *ctx, + const unsigned char *ak, + const unsigned char *nsk, + const unsigned char *diversifier, + const unsigned char *rcm, + const unsigned char *ar, + const uint64_t value, + const unsigned char *anchor, + const unsigned char *witness, + unsigned char *cv, + unsigned char *rk, + unsigned char *zkproof + ); + + /// This function (using the proving context) constructs an Output + /// proof given the necessary witness information. It outputs `cv` + /// and the `zkproof`. + bool librustzcash_sapling_output_proof( + void *ctx, + const unsigned char *esk, + const unsigned char *diversifier, + const unsigned char *pk_d, + const unsigned char *rcm, + const uint64_t value, + unsigned char *cv, + unsigned char *zkproof + ); + + /// This function (using the proving context) constructs a binding + /// signature. You must provide the intended valueBalance so that + /// we can internally check consistency. + bool librustzcash_sapling_binding_sig( + void *ctx, + int64_t valueBalance, + const unsigned char *sighash, + unsigned char *result + ); + + /// Frees a Sapling proving context returned from + /// `librustzcash_sapling_proving_ctx_init`. + void librustzcash_sapling_proving_ctx_free(void *); + /// Creates a Sapling verification context. Please free this /// when you're done. void * librustzcash_sapling_verification_ctx_init(); diff --git a/src/rustzcash.rs b/src/rustzcash.rs index e4ae6ea..537fb13 100644 --- a/src/rustzcash.rs +++ b/src/rustzcash.rs @@ -23,6 +23,8 @@ use sapling_crypto::{ }; use sapling_crypto::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}; +// TODO: make these consistent +const SAPLING_TREE_DEPTH: usize = 32; use bellman::groth16::{ create_random_proof, prepare_verifying_key, verify_proof, Parameters, PreparedVerifyingKey, @@ -33,7 +35,7 @@ use blake2_rfc::blake2s::Blake2s; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use rand::{OsRng, Rng}; +use rand::{OsRng, Rng, Rand}; use std::io::BufReader; use libc::{c_char, c_uchar, int64_t, size_t, uint32_t, uint64_t}; @@ -41,7 +43,7 @@ use std::ffi::CStr; use std::fs::File; use std::slice; -use sapling_crypto::primitives::ViewingKey; +use sapling_crypto::primitives::{ValueCommitment, ViewingKey, ProofGenerationKey}; pub mod equihash; @@ -1019,3 +1021,462 @@ pub extern "system" fn librustzcash_sprout_verify( _ => false, } } + +pub struct SaplingProvingContext { + bsk: Fs, + bvk: edwards::Point +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_output_proof( + ctx: *mut SaplingProvingContext, + esk: *const [c_uchar; 32], + diversifier: *const [c_uchar; 11], + pk_d: *const [c_uchar; 32], + rcm: *const [c_uchar; 32], + value: uint64_t, + cv: *mut [c_uchar; 32], + zkproof: *mut [c_uchar; GROTH_PROOF_SIZE] +) -> bool +{ + // Grab `esk`, which the caller should have constructed for the DH key exchange. + let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // Grab the diversifier from the caller. + let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier }); + + // Grab pk_d from the caller. + let pk_d = match edwards::Point::::read(&(unsafe { &*pk_d })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // pk_d should be prime order. + let pk_d = match pk_d.as_prime_order(&JUBJUB) { + Some(p) => p, + None => return false + }; + + // Construct a payment address + let payment_address = sapling_crypto::primitives::PaymentAddress { + pk_d: pk_d, + diversifier: diversifier + }; + + // Initialize secure RNG + let mut rng = OsRng::new().expect("should be able to construct RNG"); + + // The caller provides the commitment randomness for the output note + let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // We construct ephemeral randomness for the value commitment. This + // randomness is not given back to the caller, but the synthetic + // blinding factor `bsk` is accumulated in the context. + let rcv = Fs::rand(&mut rng); + + // Accumulate the value commitment randomness in the context + { + let mut tmp = rcv.clone(); + tmp.negate(); // Outputs subtract from the total. + tmp.add_assign(&unsafe { &*ctx }.bsk); + + // Update the context + unsafe { &mut *ctx }.bsk = tmp; + } + + // Construct the value commitment for the proof instance + let value_commitment = sapling_crypto::primitives::ValueCommitment:: { + value: value, + randomness: rcv + }; + + // We now have a full witness for the output proof. + let instance = sapling_crypto::circuit::sapling::Output { + params: &*JUBJUB, + value_commitment: Some(value_commitment.clone()), + payment_address: Some(payment_address.clone()), + commitment_randomness: Some(rcm), + esk: Some(esk.clone()) + }; + + // Create proof + let proof = create_random_proof(instance, unsafe {SAPLING_OUTPUT_PARAMS.as_ref()}.unwrap(), &mut rng).expect("proving should not fail"); + + // Write the proof out to the caller + proof + .write(&mut (unsafe { &mut *zkproof })[..]) + .expect("should be able to serialize a proof"); + + // Compute the value commitment + let value_commitment: edwards::Point = value_commitment.cm(&JUBJUB).into(); + + // Accumulate the value commitment in the context. We do this to check internal consistency. + { + let mut tmp = value_commitment.clone(); + tmp = tmp.negate(); // Outputs subtract from the total. + tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB); + + // Update the context + unsafe { &mut *ctx }.bvk = tmp; + } + + // Write the value commitment to the caller + value_commitment + .write(&mut (unsafe { &mut *cv })[..]) + .expect("should be able to serialize rcv"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_spend_sig( + ask: *const [c_uchar; 32], + ar: *const [c_uchar; 32], + sighash: *const [c_uchar; 32], + result: *mut [c_uchar; 64], +) -> bool { + + // The caller provides the re-randomization of `ak`. + let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // The caller provides `ask`, the spend authorizing key. + let ask = match redjubjub::PrivateKey::::read(&(unsafe { &*ask })[..]) { + Ok(p) => p, + Err(_) => return false, + }; + + // We compute `rsk`... + let rsk = ask.randomize(ar); + + // We compute `rk` from there (needed for key prefixing) + let rk = redjubjub::PublicKey::from_private(&rsk, FixedGenerators::SpendingKeyGenerator, &JUBJUB); + + // Compute the signature's message for rk/spend_auth_sig + let mut data_to_be_signed = [0u8; 64]; + rk.0.write(&mut data_to_be_signed[0..32]).expect("message buffer should be 32 bytes"); + (&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash })[..]); + + // Do the signing + let mut rng = OsRng::new().expect("should be able to construct RNG"); + let sig = rsk.sign(&data_to_be_signed, &mut rng, FixedGenerators::SpendingKeyGenerator, &JUBJUB); + + // Write out the signature + sig.write(&mut(unsafe { &mut *result })[..]).expect("result should be 64 bytes"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_binding_sig( + ctx: *mut SaplingProvingContext, + value_balance: int64_t, + sighash: *const [c_uchar; 32], + result: *mut [c_uchar; 64], +) -> bool +{ + // Grab the current `bsk` from the context + let bsk = redjubjub::PrivateKey::(unsafe { &*ctx }.bsk); + + // Grab the `bvk` using DerivePublic. + let bvk = redjubjub::PublicKey::from_private(&bsk, FixedGenerators::ValueCommitmentRandomness, &JUBJUB); + + // In order to check internal consistency, let's use the accumulated value + // commitments (as the verifier would) and apply valuebalance to compare + // against our derived bvk. + { + // Compute value balance + let mut value_balance = match compute_value_balance(value_balance) { + Some(a) => a, + None => return false, + }; + + // Subtract value_balance from current bvk to get final bvk + value_balance = value_balance.negate(); + let mut tmp = unsafe { &*ctx }.bvk.clone(); + tmp = tmp.add(&value_balance, &JUBJUB); + + // The result should be the same, unless the provided valueBalance is wrong. + if bvk.0 != tmp { + return false; + } + } + + // Construct signature message + let mut data_to_be_signed = [0u8; 64]; + bvk.0.write(&mut data_to_be_signed[0..32]).expect("message buffer should be 32 bytes"); + (&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash })[..]); + + // Sign + let mut rng = OsRng::new().expect("should be able to construct RNG"); + let sig = bsk.sign(&data_to_be_signed, &mut rng, FixedGenerators::ValueCommitmentRandomness, &JUBJUB); + + // Write out signature + sig.write(&mut(unsafe { &mut *result })[..]).expect("result should be 64 bytes"); + + true +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_spend_proof( + ctx: *mut SaplingProvingContext, + ak: *const [c_uchar; 32], + nsk: *const [c_uchar; 32], + diversifier: *const [c_uchar; 11], + rcm: *const [c_uchar; 32], + ar: *const [c_uchar; 32], + value: uint64_t, + anchor: *const [c_uchar; 32], + witness: *const [c_uchar; 1 + 33 * SAPLING_TREE_DEPTH + 8], + cv: *mut [c_uchar; 32], + rk_out: *mut [c_uchar; 32], + zkproof: *mut [c_uchar; GROTH_PROOF_SIZE], +) -> bool { + let mut rng = OsRng::new().expect("should be able to construct RNG"); + + // We create the randomness of the value commitment + let rcv = Fs::rand(&mut rng); + + // Accumulate the value commitment randomness in the context + { + let mut tmp = rcv.clone(); + tmp.add_assign(&unsafe { &*ctx }.bsk); + + // Update the context + unsafe { &mut *ctx }.bsk = tmp; + } + + // Construct the value commitment + let value_commitment = ValueCommitment:: { + value: value, + randomness: rcv, + }; + + // Grab `ak` from the caller, which should be a point. + let ak = match edwards::Point::::read(&(unsafe { &*ak })[..], &JUBJUB) { + Ok(p) => p, + Err(_) => return false, + }; + + // `ak` should be prime order. + let ak = match ak.as_prime_order(&JUBJUB) { + Some(p) => p, + None => return false, + }; + + // Grab `nsk` from the caller + let nsk = match Fs::from_repr(read_fs(&(unsafe { &*nsk })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // Construct the proof generation key + let proof_generation_key = ProofGenerationKey { + ak: ak.clone(), + nsk, + }; + + // Construct the viewing key + let viewing_key = proof_generation_key.into_viewing_key(&JUBJUB); + + // Grab the diversifier from the caller + let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier }); + + // Construct the payment address with the viewing key / diversifier + let payment_address = match viewing_key.into_payment_address(diversifier, &JUBJUB) { + Some(p) => p, + None => return false, + }; + + // The caller chooses the note randomness + let rcm = match Fs::from_repr(read_fs(&(unsafe { &*rcm })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // The caller also chooses the re-randomization of ak + let ar = match Fs::from_repr(read_fs(&(unsafe { &*ar })[..])) { + Ok(p) => p, + Err(_) => return false, + }; + + // This is the result of the re-randomization, we compute it for the caller + let rk = redjubjub::PublicKey::(ak.into()).randomize(ar, FixedGenerators::SpendingKeyGenerator, &JUBJUB); + + // Write out `rk` to the caller + rk.write(&mut unsafe { &mut *rk_out }[..]).expect("should be able to write to rk_out"); + + // We need to compute the anchor of the Spend. + let anchor = match Fr::from_repr(read_le(unsafe { &(&*anchor)[..] })) { + Ok(p) => p, + Err(_) => return false, + }; + + // The witness contains the incremental tree witness information, in a + // weird serialized format. + let mut witness = unsafe { &(&*witness)[..] }; + + // Skip the first byte, which should be "32" to signify the length of + // the following vector of Pedersen hashes. + assert_eq!(witness[0], SAPLING_TREE_DEPTH as u8); + witness = &witness[1..]; + + // Begin to construct the authentication path + let mut auth_path = vec![None; SAPLING_TREE_DEPTH]; + + // The vector works in reverse + for i in (0..SAPLING_TREE_DEPTH).rev() { + // skip length of inner vector + assert_eq!(witness[0], 32); // the length of a pedersen hash + witness = &witness[1..]; + + // Grab the sibling node at this depth in the tree + let mut sibling = [0u8; 32]; + sibling.copy_from_slice(&witness[0..32]); + witness = &witness[32..]; + + // Sibling node should be an element of Fr + let sibling = match Fr::from_repr(read_le(&sibling)) { + Ok(p) => p, + Err(_) => return false, + }; + + // Set the value in the auth path; we put false here + // for now (signifying the position bit) which we'll + // fill in later. + auth_path[i] = Some((sibling, false)); + } + + // Read the position from the witness + let mut position = witness + .read_u64::() + .expect("should have had index at the end"); + + // Let's compute the nullifier while we have the position + let note = sapling_crypto::primitives::Note { + value: value, + g_d: diversifier.g_d::(&JUBJUB).expect("was a valid diversifier before"), + pk_d: payment_address.pk_d.clone(), + r: rcm + }; + + let nullifier = note.nf(&viewing_key, position, &JUBJUB); + + // Given the position, let's finish constructing the authentication + // path + for i in 0..SAPLING_TREE_DEPTH { + auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1); + + position >>= 1; + } + + // The witness should be empty now; if it wasn't, the caller would + // have provided more information than they should have, indicating + // a bug downstream + assert_eq!(witness.len(), 0); + + // We now have the full witness for our circuit + let instance = sapling_crypto::circuit::sapling::Spend { + params: &*JUBJUB, + value_commitment: Some(value_commitment.clone()), + proof_generation_key: Some(proof_generation_key), + payment_address: Some(payment_address), + commitment_randomness: Some(rcm), + ar: Some(ar), + auth_path: auth_path, + anchor: Some(anchor), + }; + + // Create proof + let proof = create_random_proof(instance, unsafe {SAPLING_SPEND_PARAMS.as_ref()}.unwrap(), &mut rng).expect("proving should not fail"); + + // Try to verify the proof: + // Construct public input for circuit + let mut public_input = [Fr::zero(); 7]; + { + let (x, y) = rk.0.into_xy(); + public_input[0] = x; + public_input[1] = y; + } + { + let (x, y) = value_commitment.cm(&JUBJUB).into_xy(); + public_input[2] = x; + public_input[3] = y; + } + public_input[4] = anchor; + + // Add the nullifier through multiscalar packing + { + let nullifier = multipack::bytes_to_bits_le(&nullifier); + let nullifier = multipack::compute_multipacking::(&nullifier); + + assert_eq!(nullifier.len(), 2); + + public_input[5] = nullifier[0]; + public_input[6] = nullifier[1]; + } + + // Verify the proof + match verify_proof( + unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(), + &proof, + &public_input[..], + ) { + // No error, and proof verification successful + Ok(true) => {}, + + // Any other case + _ => {return false;}, + } + + // Compute value commitment + let value_commitment: edwards::Point = value_commitment.cm(&JUBJUB).into(); + + // Accumulate the value commitment in the context + { + let mut tmp = value_commitment.clone(); + tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB); + + // Update the context + unsafe { &mut *ctx }.bvk = tmp; + } + + // Write value commitment to caller + value_commitment + .write(&mut unsafe { &mut *cv }[..]) + .expect("should be able to serialize cv"); + + // Write proof out to caller + proof + .write(&mut (unsafe { &mut *zkproof })[..]) + .expect("should be able to serialize a proof"); + + true +} + + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_proving_ctx_init( +) -> *mut SaplingProvingContext { + let ctx = Box::new(SaplingProvingContext { + bsk: Fs::zero(), + bvk: edwards::Point::zero() + }); + + Box::into_raw(ctx) +} + +#[no_mangle] +pub extern "system" fn librustzcash_sapling_proving_ctx_free( + ctx: *mut SaplingProvingContext, +) { + drop(unsafe { Box::from_raw(ctx) }); +}