2018-04-17 14:22:35 -06:00
|
|
|
extern crate bellman;
|
2017-12-30 23:36:23 +01:00
|
|
|
extern crate blake2_rfc;
|
2018-05-08 16:58:43 -06:00
|
|
|
extern crate byteorder;
|
2017-03-17 11:07:23 -06:00
|
|
|
extern crate libc;
|
2018-04-11 21:51:30 -06:00
|
|
|
extern crate pairing;
|
2018-05-08 16:58:43 -06:00
|
|
|
extern crate rand;
|
2018-04-17 14:22:35 -06:00
|
|
|
extern crate sapling_crypto;
|
2018-04-09 18:28:42 -06:00
|
|
|
|
2018-08-01 18:28:13 -06:00
|
|
|
mod hashreader;
|
|
|
|
|
2018-04-12 18:38:25 -06:00
|
|
|
#[macro_use]
|
|
|
|
extern crate lazy_static;
|
|
|
|
|
2018-06-12 15:32:57 -06:00
|
|
|
use pairing::{
|
2018-07-31 21:25:39 +01:00
|
|
|
bls12_381::{Bls12, Fr, FrRepr},
|
|
|
|
BitIterator, Field, PrimeField, PrimeFieldRepr,
|
2018-06-12 15:32:57 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
use sapling_crypto::{
|
2018-07-31 21:25:39 +01:00
|
|
|
circuit::multipack,
|
|
|
|
constants::CRH_IVK_PERSONALIZATION,
|
2018-06-12 15:32:57 -06:00
|
|
|
jubjub::{
|
2018-07-31 21:25:39 +01:00
|
|
|
edwards,
|
|
|
|
fs::{Fs, FsRepr},
|
|
|
|
FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, PrimeOrder, ToUniform, Unknown,
|
2018-06-12 15:32:57 -06:00
|
|
|
},
|
2018-07-31 21:25:39 +01:00
|
|
|
pedersen_hash::{pedersen_hash, Personalization},
|
|
|
|
redjubjub::{self, Signature},
|
2018-06-12 15:32:57 -06:00
|
|
|
};
|
2018-04-17 14:16:14 -06:00
|
|
|
|
2018-05-08 16:58:43 -06:00
|
|
|
use sapling_crypto::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH};
|
2018-07-17 16:46:10 -06:00
|
|
|
// TODO: make these consistent
|
|
|
|
const SAPLING_TREE_DEPTH: usize = 32;
|
2018-05-08 16:58:43 -06:00
|
|
|
|
2018-06-12 15:32:57 -06:00
|
|
|
use bellman::groth16::{
|
|
|
|
create_random_proof, prepare_verifying_key, verify_proof, Parameters, PreparedVerifyingKey,
|
|
|
|
Proof, VerifyingKey,
|
|
|
|
};
|
2018-05-08 16:58:43 -06:00
|
|
|
|
2018-05-14 21:23:21 -07:00
|
|
|
use blake2_rfc::blake2s::Blake2s;
|
|
|
|
|
2018-05-08 16:58:43 -06:00
|
|
|
use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt};
|
|
|
|
|
2018-07-17 17:02:10 -06:00
|
|
|
use rand::{OsRng, Rand, Rng};
|
2018-08-01 18:28:13 -06:00
|
|
|
use std::io::{self, BufReader};
|
2018-04-17 14:22:35 -06:00
|
|
|
|
2018-06-12 15:32:57 -06:00
|
|
|
use libc::{c_char, c_uchar, int64_t, size_t, uint32_t, uint64_t};
|
2018-04-17 14:16:14 -06:00
|
|
|
use std::ffi::CStr;
|
|
|
|
use std::fs::File;
|
2017-12-30 23:36:23 +01:00
|
|
|
use std::slice;
|
|
|
|
|
2018-07-17 17:02:10 -06:00
|
|
|
use sapling_crypto::primitives::{ProofGenerationKey, ValueCommitment, ViewingKey};
|
2018-06-07 10:36:52 -07:00
|
|
|
|
2017-12-30 23:36:23 +01:00
|
|
|
pub mod equihash;
|
2018-04-11 21:51:30 -06:00
|
|
|
|
2018-05-18 19:59:23 +12:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests;
|
|
|
|
|
2018-04-12 18:38:25 -06:00
|
|
|
lazy_static! {
|
2018-04-17 14:22:35 -06:00
|
|
|
static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() };
|
2018-04-11 21:51:30 -06:00
|
|
|
}
|
|
|
|
|
2018-04-17 14:16:14 -06:00
|
|
|
static mut SAPLING_SPEND_VK: Option<PreparedVerifyingKey<Bls12>> = None;
|
|
|
|
static mut SAPLING_OUTPUT_VK: Option<PreparedVerifyingKey<Bls12>> = None;
|
|
|
|
static mut SPROUT_GROTH16_VK: Option<PreparedVerifyingKey<Bls12>> = None;
|
|
|
|
|
|
|
|
static mut SAPLING_SPEND_PARAMS: Option<Parameters<Bls12>> = None;
|
|
|
|
static mut SAPLING_OUTPUT_PARAMS: Option<Parameters<Bls12>> = None;
|
|
|
|
static mut SPROUT_GROTH16_PARAMS_PATH: Option<String> = None;
|
|
|
|
|
2018-05-17 01:22:15 -06:00
|
|
|
fn is_small_order<Order>(p: &edwards::Point<Bls12, Order>) -> bool {
|
2018-05-14 16:23:34 -06:00
|
|
|
p.double(&JUBJUB).double(&JUBJUB).double(&JUBJUB) == edwards::Point::zero()
|
|
|
|
}
|
|
|
|
|
2018-05-07 18:06:53 -06:00
|
|
|
/// Writes an FrRepr to [u8] of length 32
|
2018-05-17 01:22:15 -06:00
|
|
|
fn write_le(f: FrRepr, to: &mut [u8]) {
|
2018-05-07 18:06:53 -06:00
|
|
|
assert_eq!(to.len(), 32);
|
|
|
|
|
2018-05-17 01:22:15 -06:00
|
|
|
f.write_le(to).expect("length is 32 bytes");
|
2018-05-07 18:06:53 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads an FrRepr from a [u8] of length 32.
|
|
|
|
/// This will panic (abort) if length provided is
|
|
|
|
/// not correct.
|
2018-05-07 19:22:07 -06:00
|
|
|
fn read_le(from: &[u8]) -> FrRepr {
|
2018-05-07 18:06:53 -06:00
|
|
|
assert_eq!(from.len(), 32);
|
|
|
|
|
|
|
|
let mut f = FrRepr::default();
|
2018-05-17 01:22:15 -06:00
|
|
|
f.read_le(from).expect("length is 32 bytes");
|
2018-05-07 18:06:53 -06:00
|
|
|
|
|
|
|
f
|
|
|
|
}
|
|
|
|
|
2018-05-14 20:40:35 -07:00
|
|
|
/// Reads an FsRepr from [u8] of length 32
|
|
|
|
/// This will panic (abort) if length provided is
|
|
|
|
/// not correct
|
2018-05-14 22:34:27 -07:00
|
|
|
fn read_fs(from: &[u8]) -> FsRepr {
|
2018-05-14 20:40:35 -07:00
|
|
|
assert_eq!(from.len(), 32);
|
|
|
|
|
|
|
|
let mut f = <<Bls12 as JubjubEngine>::Fs as PrimeField>::Repr::default();
|
|
|
|
f.read_le(from).expect("length is 32 bytes");
|
|
|
|
|
2018-05-14 22:34:27 -07:00
|
|
|
f
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads an FsRepr from [u8] of length 32
|
|
|
|
/// and multiplies it by the given base.
|
|
|
|
/// This will panic (abort) if length provided is
|
|
|
|
/// not correct
|
|
|
|
fn fixed_scalar_mult(from: &[u8], p_g: FixedGenerators) -> edwards::Point<Bls12, PrimeOrder> {
|
|
|
|
let f = read_fs(from);
|
|
|
|
|
2018-05-14 20:40:35 -07:00
|
|
|
JUBJUB.generator(p_g).mul(f, &JUBJUB)
|
|
|
|
}
|
|
|
|
|
2018-04-17 14:16:14 -06:00
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_init_zksnark_params(
|
|
|
|
spend_path: *const c_char,
|
2018-08-02 12:11:32 -06:00
|
|
|
spend_hash: *const c_char,
|
2018-04-17 14:16:14 -06:00
|
|
|
output_path: *const c_char,
|
2018-08-02 12:11:32 -06:00
|
|
|
output_hash: *const c_char,
|
2018-04-17 14:22:35 -06:00
|
|
|
sprout_path: *const c_char,
|
2018-08-02 12:11:32 -06:00
|
|
|
sprout_hash: *const c_char,
|
2018-04-17 14:22:35 -06:00
|
|
|
) {
|
2018-04-21 17:46:08 -06:00
|
|
|
// Initialize jubjub parameters here
|
|
|
|
lazy_static::initialize(&JUBJUB);
|
|
|
|
|
2018-08-02 12:11:32 -06:00
|
|
|
let spend_hash = unsafe { CStr::from_ptr(spend_hash) }
|
|
|
|
.to_str()
|
|
|
|
.expect("hash should be a valid string")
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
let output_hash = unsafe { CStr::from_ptr(output_hash) }
|
|
|
|
.to_str()
|
|
|
|
.expect("hash should be a valid string")
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
let sprout_hash = unsafe { CStr::from_ptr(sprout_hash) }
|
|
|
|
.to_str()
|
|
|
|
.expect("hash should be a valid string")
|
|
|
|
.to_string();
|
|
|
|
|
2018-04-17 14:16:14 -06:00
|
|
|
// These should be valid CStr's, but the decoding may fail on Windows
|
|
|
|
// so we may need to use OSStr or something.
|
2018-04-17 14:22:35 -06:00
|
|
|
let spend_path = unsafe { CStr::from_ptr(spend_path) }
|
|
|
|
.to_str()
|
|
|
|
.expect("parameter path encoding error")
|
|
|
|
.to_string();
|
|
|
|
let output_path = unsafe { CStr::from_ptr(output_path) }
|
|
|
|
.to_str()
|
|
|
|
.expect("parameter path encoding error")
|
|
|
|
.to_string();
|
|
|
|
let sprout_path = unsafe { CStr::from_ptr(sprout_path) }
|
|
|
|
.to_str()
|
|
|
|
.expect("parameter path encoding error")
|
|
|
|
.to_string();
|
2018-04-17 14:16:14 -06:00
|
|
|
|
|
|
|
// Load from each of the paths
|
2018-08-01 18:28:13 -06:00
|
|
|
let spend_fs = File::open(spend_path).expect("couldn't load Sapling spend parameters file");
|
2018-08-01 19:01:54 -06:00
|
|
|
let output_fs = File::open(output_path).expect("couldn't load Sapling output parameters file");
|
|
|
|
let sprout_fs = File::open(&sprout_path).expect("couldn't load Sprout groth16 parameters file");
|
2018-04-17 14:16:14 -06:00
|
|
|
|
2018-08-01 18:28:13 -06:00
|
|
|
let mut spend_fs = hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, spend_fs));
|
2018-08-01 19:01:54 -06:00
|
|
|
let mut output_fs =
|
|
|
|
hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, output_fs));
|
|
|
|
let mut sprout_fs =
|
|
|
|
hashreader::HashReader::new(BufReader::with_capacity(1024 * 1024, sprout_fs));
|
2018-08-01 18:28:13 -06:00
|
|
|
|
2018-04-17 14:16:14 -06:00
|
|
|
// Deserialize params
|
2018-04-17 14:22:35 -06:00
|
|
|
let spend_params = Parameters::<Bls12>::read(&mut spend_fs, false)
|
|
|
|
.expect("couldn't deserialize Sapling spend parameters file");
|
|
|
|
let output_params = Parameters::<Bls12>::read(&mut output_fs, false)
|
|
|
|
.expect("couldn't deserialize Sapling spend parameters file");
|
2018-08-01 18:28:13 -06:00
|
|
|
|
|
|
|
// We only deserialize the verifying key for the Sprout parameters, which
|
|
|
|
// appears at the beginning of the parameter file. The rest is loaded
|
|
|
|
// during proving time.
|
2018-04-17 14:22:35 -06:00
|
|
|
let sprout_vk = VerifyingKey::<Bls12>::read(&mut sprout_fs)
|
|
|
|
.expect("couldn't deserialize Sprout Groth16 verifying key");
|
2018-04-17 14:16:14 -06:00
|
|
|
|
2018-08-01 18:28:13 -06:00
|
|
|
// There is extra stuff (the transcript) at the end of the parameter file which is
|
|
|
|
// used to verify the parameter validity, but we're not interested in that. We do
|
|
|
|
// want to read it, though, so that the BLAKE2b computed afterward is consistent
|
|
|
|
// with `b2sum` on the files.
|
|
|
|
let mut sink = io::sink();
|
2018-08-01 19:01:54 -06:00
|
|
|
io::copy(&mut spend_fs, &mut sink)
|
|
|
|
.expect("couldn't finish reading Sapling spend parameter file");
|
|
|
|
io::copy(&mut output_fs, &mut sink)
|
|
|
|
.expect("couldn't finish reading Sapling output parameter file");
|
|
|
|
io::copy(&mut sprout_fs, &mut sink)
|
|
|
|
.expect("couldn't finish reading Sprout groth16 parameter file");
|
2018-08-01 18:28:13 -06:00
|
|
|
|
2018-08-02 12:11:32 -06:00
|
|
|
if spend_fs.into_hash() != spend_hash {
|
2018-08-01 18:28:13 -06:00
|
|
|
panic!("Sapling spend parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
|
|
|
|
}
|
|
|
|
|
2018-08-02 12:11:32 -06:00
|
|
|
if output_fs.into_hash() != output_hash {
|
2018-08-01 18:28:13 -06:00
|
|
|
panic!("Sapling output parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
|
|
|
|
}
|
|
|
|
|
2018-08-02 12:11:32 -06:00
|
|
|
if sprout_fs.into_hash() != sprout_hash {
|
2018-08-01 18:28:13 -06:00
|
|
|
panic!("Sprout groth16 parameter file is not correct, please clean your `~/.zcash-params/` and re-run `fetch-params`.");
|
|
|
|
}
|
|
|
|
|
2018-04-17 14:16:14 -06:00
|
|
|
// Prepare verifying keys
|
|
|
|
let spend_vk = prepare_verifying_key(&spend_params.vk);
|
|
|
|
let output_vk = prepare_verifying_key(&output_params.vk);
|
|
|
|
let sprout_vk = prepare_verifying_key(&sprout_vk);
|
|
|
|
|
|
|
|
// Caller is responsible for calling this function once, so
|
|
|
|
// these global mutations are safe.
|
|
|
|
unsafe {
|
|
|
|
SAPLING_SPEND_PARAMS = Some(spend_params);
|
|
|
|
SAPLING_OUTPUT_PARAMS = Some(output_params);
|
|
|
|
SPROUT_GROTH16_PARAMS_PATH = Some(sprout_path);
|
|
|
|
|
|
|
|
SAPLING_SPEND_VK = Some(spend_vk);
|
|
|
|
SAPLING_OUTPUT_VK = Some(output_vk);
|
|
|
|
SPROUT_GROTH16_VK = Some(sprout_vk);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-11 21:51:30 -06:00
|
|
|
#[no_mangle]
|
2018-04-17 14:22:35 -06:00
|
|
|
pub extern "system" fn librustzcash_tree_uncommitted(result: *mut [c_uchar; 32]) {
|
2018-04-12 18:38:25 -06:00
|
|
|
let tmp = sapling_crypto::primitives::Note::<Bls12>::uncommitted().into_repr();
|
2018-04-11 21:51:30 -06:00
|
|
|
|
2018-04-12 18:38:25 -06:00
|
|
|
// Should be okay, caller is responsible for ensuring the pointer
|
|
|
|
// is a valid pointer to 32 bytes that can be mutated.
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
|
2018-05-07 18:06:53 -06:00
|
|
|
write_le(tmp, &mut result[..]);
|
2018-04-11 21:51:30 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_merkle_hash(
|
|
|
|
depth: size_t,
|
|
|
|
a: *const [c_uchar; 32],
|
|
|
|
b: *const [c_uchar; 32],
|
|
|
|
result: *mut [c_uchar; 32],
|
2018-04-17 14:22:35 -06:00
|
|
|
) {
|
2018-04-11 21:51:30 -06:00
|
|
|
// Should be okay, because caller is responsible for ensuring
|
|
|
|
// the pointer is a valid pointer to 32 bytes, and that is the
|
|
|
|
// size of the representation
|
2018-05-07 18:06:53 -06:00
|
|
|
let a_repr = read_le(unsafe { &(&*a)[..] });
|
2018-04-11 21:51:30 -06:00
|
|
|
|
|
|
|
// Should be okay, because caller is responsible for ensuring
|
|
|
|
// the pointer is a valid pointer to 32 bytes, and that is the
|
|
|
|
// size of the representation
|
2018-05-07 18:06:53 -06:00
|
|
|
let b_repr = read_le(unsafe { &(&*b)[..] });
|
2018-04-11 21:51:30 -06:00
|
|
|
|
|
|
|
let mut lhs = [false; 256];
|
|
|
|
let mut rhs = [false; 256];
|
|
|
|
|
|
|
|
for (a, b) in lhs.iter_mut().rev().zip(BitIterator::new(a_repr)) {
|
|
|
|
*a = b;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (a, b) in rhs.iter_mut().rev().zip(BitIterator::new(b_repr)) {
|
|
|
|
*a = b;
|
|
|
|
}
|
2018-04-17 14:22:35 -06:00
|
|
|
|
2018-04-11 21:51:30 -06:00
|
|
|
let tmp = pedersen_hash::<Bls12, _>(
|
|
|
|
Personalization::MerkleTree(depth),
|
2018-04-17 14:22:35 -06:00
|
|
|
lhs.iter()
|
|
|
|
.map(|&x| x)
|
2018-04-11 21:51:30 -06:00
|
|
|
.take(Fr::NUM_BITS as usize)
|
|
|
|
.chain(rhs.iter().map(|&x| x).take(Fr::NUM_BITS as usize)),
|
2018-04-17 14:22:35 -06:00
|
|
|
&JUBJUB,
|
|
|
|
).into_xy()
|
|
|
|
.0
|
|
|
|
.into_repr();
|
2018-04-11 21:51:30 -06:00
|
|
|
|
|
|
|
// Should be okay, caller is responsible for ensuring the pointer
|
2018-04-12 15:01:48 -06:00
|
|
|
// is a valid pointer to 32 bytes that can be mutated.
|
2018-04-11 21:51:30 -06:00
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
|
2018-05-07 18:06:53 -06:00
|
|
|
write_le(tmp, &mut result[..]);
|
2018-04-11 21:51:30 -06:00
|
|
|
}
|
2017-03-17 11:07:23 -06:00
|
|
|
|
2018-05-09 06:51:19 -07:00
|
|
|
#[no_mangle] // ToScalar
|
|
|
|
pub extern "system" fn librustzcash_to_scalar(
|
|
|
|
input: *const [c_uchar; 64],
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) {
|
|
|
|
// Should be okay, because caller is responsible for ensuring
|
|
|
|
// the pointer is a valid pointer to 32 bytes, and that is the
|
|
|
|
// size of the representation
|
|
|
|
let scalar = <Bls12 as JubjubEngine>::Fs::to_uniform(unsafe { &(&*input)[..] }).into_repr();
|
|
|
|
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
|
|
|
|
scalar
|
|
|
|
.write_le(&mut result[..])
|
|
|
|
.expect("length is 32 bytes");
|
|
|
|
}
|
|
|
|
|
2018-05-14 20:40:35 -07:00
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_ask_to_ak(
|
|
|
|
ask: *const [c_uchar; 32],
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) {
|
|
|
|
let ask = unsafe { &*ask };
|
|
|
|
let ak = fixed_scalar_mult(ask, FixedGenerators::SpendingKeyGenerator);
|
|
|
|
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
|
|
|
|
ak.write(&mut result[..]).expect("length is 32 bytes");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_nsk_to_nk(
|
|
|
|
nsk: *const [c_uchar; 32],
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) {
|
|
|
|
let nsk = unsafe { &*nsk };
|
|
|
|
let nk = fixed_scalar_mult(nsk, FixedGenerators::ProofGenerationKey);
|
|
|
|
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
|
|
|
|
nk.write(&mut result[..]).expect("length is 32 bytes");
|
|
|
|
}
|
|
|
|
|
2018-05-14 21:23:21 -07:00
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_crh_ivk(
|
|
|
|
ak: *const [c_uchar; 32],
|
|
|
|
nk: *const [c_uchar; 32],
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) {
|
|
|
|
let ak = unsafe { &*ak };
|
|
|
|
let nk = unsafe { &*nk };
|
|
|
|
|
|
|
|
let mut h = Blake2s::with_params(32, &[], &[], CRH_IVK_PERSONALIZATION);
|
|
|
|
h.update(ak);
|
|
|
|
h.update(nk);
|
|
|
|
let mut h = h.finalize().as_ref().to_vec();
|
|
|
|
|
|
|
|
// Drop the last five bits, so it can be interpreted as a scalar.
|
|
|
|
h[31] &= 0b0000_0111;
|
|
|
|
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
|
|
|
|
result.copy_from_slice(&h);
|
|
|
|
}
|
|
|
|
|
2018-05-14 22:34:27 -07:00
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_check_diversifier(diversifier: *const [c_uchar; 11]) -> bool {
|
|
|
|
let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
|
|
|
|
diversifier.g_d::<Bls12>(&JUBJUB).is_some()
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_ivk_to_pkd(
|
|
|
|
ivk: *const [c_uchar; 32],
|
|
|
|
diversifier: *const [c_uchar; 11],
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) -> bool {
|
|
|
|
let ivk = read_fs(unsafe { &*ivk });
|
|
|
|
let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
|
|
|
|
if let Some(g_d) = diversifier.g_d::<Bls12>(&JUBJUB) {
|
|
|
|
let pk_d = g_d.mul(ivk, &JUBJUB);
|
|
|
|
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
|
|
|
|
pk_d.write(&mut result[..]).expect("length is 32 bytes");
|
|
|
|
|
|
|
|
true
|
|
|
|
} else {
|
|
|
|
false
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-01 12:20:22 -07:00
|
|
|
/// Test generation of commitment randomness
|
|
|
|
#[test]
|
|
|
|
fn test_gen_r() {
|
|
|
|
let mut r1 = [0u8; 32];
|
|
|
|
let mut r2 = [0u8; 32];
|
|
|
|
|
|
|
|
// Verify different r values are generated
|
|
|
|
librustzcash_sapling_generate_r(&mut r1);
|
|
|
|
librustzcash_sapling_generate_r(&mut r2);
|
|
|
|
assert_ne!(r1, r2);
|
|
|
|
|
|
|
|
// Verify r values are valid in the field
|
|
|
|
let mut repr = FsRepr::default();
|
|
|
|
repr.read_le(&r1[..]).expect("length is not 32 bytes");
|
|
|
|
let _ = Fs::from_repr(repr).unwrap();
|
|
|
|
repr.read_le(&r2[..]).expect("length is not 32 bytes");
|
|
|
|
let _ = Fs::from_repr(repr).unwrap();
|
|
|
|
}
|
|
|
|
|
2018-06-12 15:32:20 -06:00
|
|
|
/// Return 32 byte random scalar, uniformly.
|
2018-05-21 14:46:31 -07:00
|
|
|
#[no_mangle]
|
2018-06-01 12:20:22 -07:00
|
|
|
pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) {
|
2018-05-21 14:46:31 -07:00
|
|
|
// create random 64 byte buffer
|
|
|
|
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
|
|
|
let mut buffer = [0u8; 64];
|
|
|
|
for i in 0..buffer.len() {
|
|
|
|
buffer[i] = rng.gen();
|
|
|
|
}
|
|
|
|
|
|
|
|
// reduce to uniform value
|
|
|
|
let r = <Bls12 as JubjubEngine>::Fs::to_uniform(&buffer[..]);
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
r.into_repr()
|
|
|
|
.write_le(&mut result[..])
|
|
|
|
.expect("result must be 32 bytes");
|
|
|
|
}
|
|
|
|
|
2018-06-07 10:36:52 -07:00
|
|
|
// Private utility function to get Note from C parameters
|
|
|
|
fn priv_get_note(
|
2018-05-21 14:46:31 -07:00
|
|
|
diversifier: *const [c_uchar; 11],
|
|
|
|
pk_d: *const [c_uchar; 32],
|
|
|
|
value: uint64_t,
|
|
|
|
r: *const [c_uchar; 32],
|
2018-06-07 10:36:52 -07:00
|
|
|
) -> Result<sapling_crypto::primitives::Note<Bls12>, ()> {
|
2018-05-21 14:46:31 -07:00
|
|
|
let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
|
|
|
|
let g_d = match diversifier.g_d::<Bls12>(&JUBJUB) {
|
|
|
|
Some(g_d) => g_d,
|
2018-06-07 10:36:52 -07:00
|
|
|
None => return Err(()),
|
2018-05-21 14:46:31 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
let pk_d = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*pk_d })[..], &JUBJUB) {
|
|
|
|
Ok(p) => p,
|
2018-06-07 10:36:52 -07:00
|
|
|
Err(_) => return Err(()),
|
2018-05-21 14:46:31 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
let pk_d = match pk_d.as_prime_order(&JUBJUB) {
|
|
|
|
Some(pk_d) => pk_d,
|
2018-06-07 10:36:52 -07:00
|
|
|
None => return Err(()),
|
2018-05-21 14:46:31 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
// Deserialize randomness
|
2018-06-12 15:32:20 -06:00
|
|
|
let r = match Fs::from_repr(read_fs(&(unsafe { &*r })[..])) {
|
|
|
|
Ok(r) => r,
|
2018-06-07 10:36:52 -07:00
|
|
|
Err(_) => return Err(()),
|
2018-05-21 14:46:31 -07:00
|
|
|
};
|
|
|
|
|
|
|
|
let note = sapling_crypto::primitives::Note {
|
|
|
|
value,
|
|
|
|
g_d,
|
|
|
|
pk_d,
|
|
|
|
r,
|
|
|
|
};
|
|
|
|
|
2018-06-07 10:36:52 -07:00
|
|
|
Ok(note)
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute Sapling note nullifier.
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_compute_nf(
|
|
|
|
diversifier: *const [c_uchar; 11],
|
|
|
|
pk_d: *const [c_uchar; 32],
|
|
|
|
value: uint64_t,
|
|
|
|
r: *const [c_uchar; 32],
|
|
|
|
ak: *const [c_uchar; 32],
|
|
|
|
nk: *const [c_uchar; 32],
|
|
|
|
position: uint64_t,
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) -> bool {
|
|
|
|
let note = match priv_get_note(diversifier, pk_d, value, r) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let ak = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*ak })[..], &JUBJUB) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let ak = match ak.as_prime_order(&JUBJUB) {
|
|
|
|
Some(ak) => ak,
|
|
|
|
None => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let nk = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*nk })[..], &JUBJUB) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let nk = match nk.as_prime_order(&JUBJUB) {
|
|
|
|
Some(nk) => nk,
|
|
|
|
None => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let vk = ViewingKey { ak, nk };
|
|
|
|
let nf = note.nf(&vk, position, &JUBJUB);
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
result.copy_from_slice(&nf);
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Compute Sapling note commitment.
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_compute_cm(
|
|
|
|
diversifier: *const [c_uchar; 11],
|
|
|
|
pk_d: *const [c_uchar; 32],
|
|
|
|
value: uint64_t,
|
|
|
|
r: *const [c_uchar; 32],
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) -> bool {
|
|
|
|
let note = match priv_get_note(diversifier, pk_d, value, r) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
2018-05-21 14:46:31 -07:00
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
write_le(note.cm(&JUBJUB).into_repr(), &mut result[..]);
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2018-06-12 15:32:20 -06:00
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_ka_agree(
|
|
|
|
p: *const [c_uchar; 32],
|
|
|
|
sk: *const [c_uchar; 32],
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) -> bool {
|
|
|
|
// Deserialize p
|
|
|
|
let p = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*p })[..], &JUBJUB) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Deserialize sk
|
|
|
|
let sk = match Fs::from_repr(read_fs(&(unsafe { &*sk })[..])) {
|
|
|
|
Ok(p) => p,
|
2018-06-12 15:32:57 -06:00
|
|
|
Err(_) => return false,
|
2018-06-12 15:32:20 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// Multiply by 8
|
|
|
|
let p = p.mul_by_cofactor(&JUBJUB);
|
|
|
|
|
|
|
|
// Multiply by sk
|
|
|
|
let p = p.mul(sk, &JUBJUB);
|
|
|
|
|
|
|
|
// Produce result
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
p.write(&mut result[..]).expect("length is not 32 bytes");
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_ka_derivepublic(
|
|
|
|
diversifier: *const [c_uchar; 11],
|
|
|
|
esk: *const [c_uchar; 32],
|
|
|
|
result: *mut [c_uchar; 32],
|
|
|
|
) -> bool {
|
|
|
|
let diversifier = sapling_crypto::primitives::Diversifier(unsafe { *diversifier });
|
|
|
|
|
|
|
|
// Compute g_d from the diversifier
|
|
|
|
let g_d = match diversifier.g_d::<Bls12>(&JUBJUB) {
|
|
|
|
Some(g) => g,
|
2018-06-12 15:32:57 -06:00
|
|
|
None => return false,
|
2018-06-12 15:32:20 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// Deserialize esk
|
|
|
|
let esk = match Fs::from_repr(read_fs(&(unsafe { &*esk })[..])) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
let p = g_d.mul(esk, &JUBJUB);
|
|
|
|
|
|
|
|
let result = unsafe { &mut *result };
|
|
|
|
p.write(&mut result[..]).expect("length is not 32 bytes");
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
2017-12-30 23:36:23 +01:00
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_eh_isvalid(
|
|
|
|
n: uint32_t,
|
|
|
|
k: uint32_t,
|
|
|
|
input: *const c_uchar,
|
|
|
|
input_len: size_t,
|
|
|
|
nonce: *const c_uchar,
|
|
|
|
nonce_len: size_t,
|
|
|
|
soln: *const c_uchar,
|
|
|
|
soln_len: size_t,
|
|
|
|
) -> bool {
|
|
|
|
if (k >= n) || (n % 8 != 0) || (soln_len != (1 << k) * ((n / (k + 1)) as usize + 1) / 8) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
let rs_input = unsafe { slice::from_raw_parts(input, input_len) };
|
|
|
|
let rs_nonce = unsafe { slice::from_raw_parts(nonce, nonce_len) };
|
|
|
|
let rs_soln = unsafe { slice::from_raw_parts(soln, soln_len) };
|
|
|
|
equihash::is_valid_solution(n, k, rs_input, rs_nonce, rs_soln)
|
|
|
|
}
|
|
|
|
|
2018-05-07 19:22:07 -06:00
|
|
|
pub struct SaplingVerificationContext {
|
|
|
|
bvk: edwards::Point<Bls12, Unknown>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_verification_ctx_init(
|
|
|
|
) -> *mut SaplingVerificationContext {
|
|
|
|
let ctx = Box::new(SaplingVerificationContext {
|
|
|
|
bvk: edwards::Point::zero(),
|
|
|
|
});
|
|
|
|
|
|
|
|
Box::into_raw(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_verification_ctx_free(
|
|
|
|
ctx: *mut SaplingVerificationContext,
|
|
|
|
) {
|
|
|
|
drop(unsafe { Box::from_raw(ctx) });
|
|
|
|
}
|
|
|
|
|
|
|
|
const GROTH_PROOF_SIZE: usize = 48 // π_A
|
|
|
|
+ 96 // π_B
|
|
|
|
+ 48; // π_C
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_check_spend(
|
|
|
|
ctx: *mut SaplingVerificationContext,
|
|
|
|
cv: *const [c_uchar; 32],
|
|
|
|
anchor: *const [c_uchar; 32],
|
|
|
|
nullifier: *const [c_uchar; 32],
|
|
|
|
rk: *const [c_uchar; 32],
|
|
|
|
zkproof: *const [c_uchar; GROTH_PROOF_SIZE],
|
|
|
|
spend_auth_sig: *const [c_uchar; 64],
|
|
|
|
sighash_value: *const [c_uchar; 32],
|
|
|
|
) -> bool {
|
|
|
|
// Deserialize the value commitment
|
|
|
|
let cv = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*cv })[..], &JUBJUB) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
2018-05-14 16:23:34 -06:00
|
|
|
if is_small_order(&cv) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-07 19:22:07 -06:00
|
|
|
// Accumulate the value commitment in the context
|
|
|
|
{
|
|
|
|
let mut tmp = cv.clone();
|
|
|
|
tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB);
|
|
|
|
|
|
|
|
// Update the context
|
|
|
|
unsafe { &mut *ctx }.bvk = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deserialize the anchor, which should be an element
|
|
|
|
// of Fr.
|
|
|
|
let anchor = match Fr::from_repr(read_le(&(unsafe { &*anchor })[..])) {
|
|
|
|
Ok(a) => a,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Grab the nullifier as a sequence of bytes
|
|
|
|
let nullifier = &unsafe { &*nullifier }[..];
|
|
|
|
|
|
|
|
// Compute the signature's message for rk/spend_auth_sig
|
|
|
|
let mut data_to_be_signed = [0u8; 64];
|
|
|
|
(&mut data_to_be_signed[0..32]).copy_from_slice(&(unsafe { &*rk })[..]);
|
|
|
|
(&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash_value })[..]);
|
|
|
|
|
|
|
|
// Deserialize rk
|
|
|
|
let rk = match redjubjub::PublicKey::<Bls12>::read(&(unsafe { &*rk })[..], &JUBJUB) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
2018-05-14 16:23:34 -06:00
|
|
|
if is_small_order(&rk.0) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-07 19:22:07 -06:00
|
|
|
// Deserialize the signature
|
|
|
|
let spend_auth_sig = match Signature::read(&(unsafe { &*spend_auth_sig })[..]) {
|
|
|
|
Ok(sig) => sig,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Verify the spend_auth_sig
|
|
|
|
if !rk.verify(
|
|
|
|
&data_to_be_signed,
|
|
|
|
&spend_auth_sig,
|
|
|
|
FixedGenerators::SpendingKeyGenerator,
|
|
|
|
&JUBJUB,
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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) = cv.into_xy();
|
|
|
|
public_input[2] = x;
|
|
|
|
public_input[3] = y;
|
|
|
|
}
|
|
|
|
public_input[4] = anchor;
|
|
|
|
|
|
|
|
// Add the nullifier through multiscalar packing
|
|
|
|
{
|
2018-05-17 01:22:15 -06:00
|
|
|
let nullifier = multipack::bytes_to_bits_le(nullifier);
|
2018-05-07 19:22:07 -06:00
|
|
|
let nullifier = multipack::compute_multipacking::<Bls12>(&nullifier);
|
|
|
|
|
|
|
|
assert_eq!(nullifier.len(), 2);
|
|
|
|
|
|
|
|
public_input[5] = nullifier[0];
|
|
|
|
public_input[6] = nullifier[1];
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deserialize the proof
|
|
|
|
let zkproof = match Proof::<Bls12>::read(&(unsafe { &*zkproof })[..]) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Verify the proof
|
|
|
|
match verify_proof(
|
|
|
|
unsafe { SAPLING_SPEND_VK.as_ref() }.unwrap(),
|
|
|
|
&zkproof,
|
|
|
|
&public_input[..],
|
|
|
|
) {
|
|
|
|
// No error, and proof verification successful
|
|
|
|
Ok(true) => true,
|
|
|
|
|
|
|
|
// Any other case
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_check_output(
|
|
|
|
ctx: *mut SaplingVerificationContext,
|
|
|
|
cv: *const [c_uchar; 32],
|
|
|
|
cm: *const [c_uchar; 32],
|
|
|
|
epk: *const [c_uchar; 32],
|
|
|
|
zkproof: *const [c_uchar; GROTH_PROOF_SIZE],
|
|
|
|
) -> bool {
|
|
|
|
// Deserialize the value commitment
|
|
|
|
let cv = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*cv })[..], &JUBJUB) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
2018-05-14 16:23:34 -06:00
|
|
|
if is_small_order(&cv) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-07 19:22:07 -06:00
|
|
|
// Accumulate the value commitment in the context
|
|
|
|
{
|
|
|
|
let mut tmp = cv.clone();
|
2018-07-17 17:01:04 -06:00
|
|
|
tmp = tmp.negate(); // Outputs subtract from the total.
|
2018-05-07 19:22:07 -06:00
|
|
|
tmp = tmp.add(&unsafe { &*ctx }.bvk, &JUBJUB);
|
|
|
|
|
|
|
|
// Update the context
|
|
|
|
unsafe { &mut *ctx }.bvk = tmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Deserialize the commitment, which should be an element
|
|
|
|
// of Fr.
|
|
|
|
let cm = match Fr::from_repr(read_le(&(unsafe { &*cm })[..])) {
|
|
|
|
Ok(a) => a,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Deserialize the ephemeral key
|
|
|
|
let epk = match edwards::Point::<Bls12, Unknown>::read(&(unsafe { &*epk })[..], &JUBJUB) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
2018-05-14 16:23:34 -06:00
|
|
|
if is_small_order(&epk) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-07 19:22:07 -06:00
|
|
|
// Construct public input for circuit
|
|
|
|
let mut public_input = [Fr::zero(); 5];
|
|
|
|
{
|
|
|
|
let (x, y) = cv.into_xy();
|
|
|
|
public_input[0] = x;
|
|
|
|
public_input[1] = y;
|
|
|
|
}
|
|
|
|
{
|
|
|
|
let (x, y) = epk.into_xy();
|
|
|
|
public_input[2] = x;
|
|
|
|
public_input[3] = y;
|
|
|
|
}
|
|
|
|
public_input[4] = cm;
|
|
|
|
|
|
|
|
// Deserialize the proof
|
|
|
|
let zkproof = match Proof::<Bls12>::read(&(unsafe { &*zkproof })[..]) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Verify the proof
|
|
|
|
match verify_proof(
|
|
|
|
unsafe { SAPLING_OUTPUT_VK.as_ref() }.unwrap(),
|
|
|
|
&zkproof,
|
|
|
|
&public_input[..],
|
|
|
|
) {
|
|
|
|
// No error, and proof verification successful
|
|
|
|
Ok(true) => true,
|
|
|
|
|
|
|
|
// Any other case
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This function computes `value` in the exponent of the value commitment base
|
|
|
|
fn compute_value_balance(value: int64_t) -> Option<edwards::Point<Bls12, Unknown>> {
|
|
|
|
// Compute the absolute value (failing if -i64::MAX is
|
|
|
|
// the value)
|
|
|
|
let abs = match value.checked_abs() {
|
|
|
|
Some(a) => a as u64,
|
|
|
|
None => return None,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Is it negative? We'll have to negate later if so.
|
|
|
|
let is_negative = value.is_negative();
|
|
|
|
|
|
|
|
// Compute it in the exponent
|
|
|
|
let mut value_balance = JUBJUB
|
|
|
|
.generator(FixedGenerators::ValueCommitmentValue)
|
|
|
|
.mul(FsRepr::from(abs), &JUBJUB);
|
|
|
|
|
|
|
|
// Negate if necessary
|
|
|
|
if is_negative {
|
|
|
|
value_balance = value_balance.negate();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Convert to unknown order point
|
|
|
|
Some(value_balance.into())
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_final_check(
|
|
|
|
ctx: *mut SaplingVerificationContext,
|
|
|
|
value_balance: int64_t,
|
|
|
|
binding_sig: *const [c_uchar; 64],
|
|
|
|
sighash_value: *const [c_uchar; 32],
|
|
|
|
) -> bool {
|
|
|
|
// Obtain current bvk from the context
|
|
|
|
let mut bvk = redjubjub::PublicKey(unsafe { &*ctx }.bvk.clone());
|
|
|
|
|
|
|
|
// 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();
|
|
|
|
bvk.0 = bvk.0.add(&value_balance, &JUBJUB);
|
|
|
|
|
|
|
|
// Compute the signature's message for bvk/binding_sig
|
|
|
|
let mut data_to_be_signed = [0u8; 64];
|
|
|
|
bvk.0
|
|
|
|
.write(&mut data_to_be_signed[0..32])
|
|
|
|
.expect("bvk is 32 bytes");
|
|
|
|
(&mut data_to_be_signed[32..64]).copy_from_slice(&(unsafe { &*sighash_value })[..]);
|
|
|
|
|
|
|
|
// Deserialize the signature
|
|
|
|
let binding_sig = match Signature::read(&(unsafe { &*binding_sig })[..]) {
|
|
|
|
Ok(sig) => sig,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Verify the binding_sig
|
|
|
|
if !bvk.verify(
|
|
|
|
&data_to_be_signed,
|
|
|
|
&binding_sig,
|
|
|
|
FixedGenerators::ValueCommitmentRandomness,
|
|
|
|
&JUBJUB,
|
|
|
|
) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
true
|
|
|
|
}
|
2018-05-08 16:58:43 -06:00
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sprout_prove(
|
|
|
|
proof_out: *mut [c_uchar; GROTH_PROOF_SIZE],
|
|
|
|
|
|
|
|
phi: *const [c_uchar; 32],
|
|
|
|
rt: *const [c_uchar; 32],
|
|
|
|
h_sig: *const [c_uchar; 32],
|
|
|
|
|
|
|
|
// First input
|
|
|
|
in_sk1: *const [c_uchar; 32],
|
|
|
|
in_value1: uint64_t,
|
|
|
|
in_rho1: *const [c_uchar; 32],
|
|
|
|
in_r1: *const [c_uchar; 32],
|
|
|
|
in_auth1: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8],
|
|
|
|
|
|
|
|
// Second input
|
|
|
|
in_sk2: *const [c_uchar; 32],
|
|
|
|
in_value2: uint64_t,
|
|
|
|
in_rho2: *const [c_uchar; 32],
|
|
|
|
in_r2: *const [c_uchar; 32],
|
|
|
|
in_auth2: *const [c_uchar; 1 + 33 * SPROUT_TREE_DEPTH + 8],
|
|
|
|
|
|
|
|
// First output
|
|
|
|
out_pk1: *const [c_uchar; 32],
|
|
|
|
out_value1: uint64_t,
|
|
|
|
out_r1: *const [c_uchar; 32],
|
|
|
|
|
|
|
|
// Second output
|
|
|
|
out_pk2: *const [c_uchar; 32],
|
|
|
|
out_value2: uint64_t,
|
|
|
|
out_r2: *const [c_uchar; 32],
|
|
|
|
|
|
|
|
// Public value
|
|
|
|
vpub_old: uint64_t,
|
|
|
|
vpub_new: uint64_t,
|
|
|
|
) {
|
|
|
|
let phi = unsafe { *phi };
|
|
|
|
let rt = unsafe { *rt };
|
|
|
|
let h_sig = unsafe { *h_sig };
|
|
|
|
let in_sk1 = unsafe { *in_sk1 };
|
|
|
|
let in_rho1 = unsafe { *in_rho1 };
|
|
|
|
let in_r1 = unsafe { *in_r1 };
|
|
|
|
let in_auth1 = unsafe { *in_auth1 };
|
|
|
|
let in_sk2 = unsafe { *in_sk2 };
|
|
|
|
let in_rho2 = unsafe { *in_rho2 };
|
|
|
|
let in_r2 = unsafe { *in_r2 };
|
|
|
|
let in_auth2 = unsafe { *in_auth2 };
|
|
|
|
let out_pk1 = unsafe { *out_pk1 };
|
|
|
|
let out_r1 = unsafe { *out_r1 };
|
|
|
|
let out_pk2 = unsafe { *out_pk2 };
|
|
|
|
let out_r2 = unsafe { *out_r2 };
|
|
|
|
|
|
|
|
let mut inputs = Vec::with_capacity(2);
|
|
|
|
{
|
|
|
|
let mut handle_input = |sk, value, rho, r, mut auth: &[u8]| {
|
|
|
|
let value = Some(value);
|
|
|
|
let rho = Some(sprout::UniqueRandomness(rho));
|
|
|
|
let r = Some(sprout::CommitmentRandomness(r));
|
|
|
|
let a_sk = Some(sprout::SpendingKey(sk));
|
|
|
|
|
|
|
|
// skip the first byte
|
|
|
|
assert_eq!(auth[0], SPROUT_TREE_DEPTH as u8);
|
|
|
|
auth = &auth[1..];
|
|
|
|
|
|
|
|
let mut auth_path = [None; SPROUT_TREE_DEPTH];
|
|
|
|
for i in (0..SPROUT_TREE_DEPTH).rev() {
|
|
|
|
// skip length of inner vector
|
|
|
|
assert_eq!(auth[0], 32);
|
|
|
|
auth = &auth[1..];
|
|
|
|
|
|
|
|
let mut sibling = [0u8; 32];
|
|
|
|
sibling.copy_from_slice(&auth[0..32]);
|
|
|
|
auth = &auth[32..];
|
|
|
|
|
|
|
|
auth_path[i] = Some((sibling, false));
|
|
|
|
}
|
|
|
|
|
2018-06-12 15:32:57 -06:00
|
|
|
let mut position = auth
|
|
|
|
.read_u64::<LittleEndian>()
|
2018-05-08 16:58:43 -06:00
|
|
|
.expect("should have had index at the end");
|
|
|
|
|
|
|
|
for i in 0..SPROUT_TREE_DEPTH {
|
|
|
|
auth_path[i].as_mut().map(|p| p.1 = (position & 1) == 1);
|
|
|
|
|
|
|
|
position >>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
inputs.push(sprout::JSInput {
|
|
|
|
value: value,
|
|
|
|
a_sk: a_sk,
|
|
|
|
rho: rho,
|
|
|
|
r: r,
|
|
|
|
auth_path: auth_path,
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
handle_input(in_sk1, in_value1, in_rho1, in_r1, &in_auth1[..]);
|
|
|
|
handle_input(in_sk2, in_value2, in_rho2, in_r2, &in_auth2[..]);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut outputs = Vec::with_capacity(2);
|
|
|
|
{
|
|
|
|
let mut handle_output = |a_pk, value, r| {
|
|
|
|
outputs.push(sprout::JSOutput {
|
|
|
|
value: Some(value),
|
|
|
|
a_pk: Some(sprout::PayingKey(a_pk)),
|
|
|
|
r: Some(sprout::CommitmentRandomness(r)),
|
|
|
|
});
|
|
|
|
};
|
|
|
|
|
|
|
|
handle_output(out_pk1, out_value1, out_r1);
|
|
|
|
handle_output(out_pk2, out_value2, out_r2);
|
|
|
|
}
|
|
|
|
|
|
|
|
let js = sprout::JoinSplit {
|
|
|
|
vpub_old: Some(vpub_old),
|
|
|
|
vpub_new: Some(vpub_new),
|
|
|
|
h_sig: Some(h_sig),
|
|
|
|
phi: Some(phi),
|
|
|
|
inputs: inputs,
|
|
|
|
outputs: outputs,
|
|
|
|
rt: Some(rt),
|
|
|
|
};
|
|
|
|
|
|
|
|
// Load parameters from disk
|
|
|
|
let sprout_fs = File::open(
|
|
|
|
unsafe { &SPROUT_GROTH16_PARAMS_PATH }
|
|
|
|
.as_ref()
|
|
|
|
.expect("parameters should have been initialized"),
|
|
|
|
).expect("couldn't load Sprout groth16 parameters file");
|
|
|
|
|
|
|
|
let mut sprout_fs = BufReader::with_capacity(1024 * 1024, sprout_fs);
|
|
|
|
|
|
|
|
let params = Parameters::<Bls12>::read(&mut sprout_fs, false)
|
|
|
|
.expect("couldn't deserialize Sprout JoinSplit parameters file");
|
|
|
|
|
|
|
|
drop(sprout_fs);
|
|
|
|
|
|
|
|
// Initialize secure RNG
|
|
|
|
let mut rng = OsRng::new().expect("should be able to construct RNG");
|
|
|
|
|
|
|
|
let proof = create_random_proof(js, ¶ms, &mut rng).expect("proving should not fail");
|
|
|
|
|
|
|
|
proof
|
|
|
|
.write(&mut (unsafe { &mut *proof_out })[..])
|
|
|
|
.expect("should be able to serialize a proof");
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sprout_verify(
|
|
|
|
proof: *const [c_uchar; GROTH_PROOF_SIZE],
|
|
|
|
rt: *const [c_uchar; 32],
|
|
|
|
h_sig: *const [c_uchar; 32],
|
|
|
|
mac1: *const [c_uchar; 32],
|
|
|
|
mac2: *const [c_uchar; 32],
|
|
|
|
nf1: *const [c_uchar; 32],
|
|
|
|
nf2: *const [c_uchar; 32],
|
|
|
|
cm1: *const [c_uchar; 32],
|
|
|
|
cm2: *const [c_uchar; 32],
|
|
|
|
vpub_old: uint64_t,
|
|
|
|
vpub_new: uint64_t,
|
|
|
|
) -> bool {
|
|
|
|
// Prepare the public input for the verifier
|
|
|
|
let mut public_input = Vec::with_capacity((32 * 8) + (8 * 2));
|
|
|
|
public_input.extend(unsafe { &(&*rt)[..] });
|
|
|
|
public_input.extend(unsafe { &(&*h_sig)[..] });
|
|
|
|
public_input.extend(unsafe { &(&*nf1)[..] });
|
|
|
|
public_input.extend(unsafe { &(&*mac1)[..] });
|
|
|
|
public_input.extend(unsafe { &(&*nf2)[..] });
|
|
|
|
public_input.extend(unsafe { &(&*mac2)[..] });
|
|
|
|
public_input.extend(unsafe { &(&*cm1)[..] });
|
|
|
|
public_input.extend(unsafe { &(&*cm2)[..] });
|
|
|
|
public_input.write_u64::<LittleEndian>(vpub_old).unwrap();
|
|
|
|
public_input.write_u64::<LittleEndian>(vpub_new).unwrap();
|
|
|
|
|
|
|
|
let public_input = multipack::bytes_to_bits(&public_input);
|
|
|
|
let public_input = multipack::compute_multipacking::<Bls12>(&public_input);
|
|
|
|
|
|
|
|
let proof = match Proof::read(unsafe { &(&*proof)[..] }) {
|
|
|
|
Ok(p) => p,
|
|
|
|
Err(_) => return false,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Verify the proof
|
|
|
|
match verify_proof(
|
|
|
|
unsafe { SPROUT_GROTH16_VK.as_ref() }.expect("parameters should have been initialized"),
|
|
|
|
&proof,
|
|
|
|
&public_input[..],
|
|
|
|
) {
|
|
|
|
// No error, and proof verification successful
|
|
|
|
Ok(true) => true,
|
|
|
|
|
|
|
|
// Any other case
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
pub struct SaplingProvingContext {
|
|
|
|
bsk: Fs,
|
2018-07-17 17:02:10 -06:00
|
|
|
bvk: edwards::Point<Bls12, Unknown>,
|
2018-07-17 16:46:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
#[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],
|
2018-07-17 17:02:10 -06:00
|
|
|
zkproof: *mut [c_uchar; GROTH_PROOF_SIZE],
|
|
|
|
) -> bool {
|
2018-07-17 16:46:10 -06:00
|
|
|
// 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::<Bls12, Unknown>::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,
|
2018-07-17 17:02:10 -06:00
|
|
|
None => return false,
|
2018-07-17 16:46:10 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// Construct a payment address
|
|
|
|
let payment_address = sapling_crypto::primitives::PaymentAddress {
|
|
|
|
pk_d: pk_d,
|
2018-07-17 17:02:10 -06:00
|
|
|
diversifier: diversifier,
|
2018-07-17 16:46:10 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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::<Bls12> {
|
|
|
|
value: value,
|
2018-07-17 17:02:10 -06:00
|
|
|
randomness: rcv,
|
2018-07-17 16:46:10 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// 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),
|
2018-07-17 17:02:10 -06:00
|
|
|
esk: Some(esk.clone()),
|
2018-07-17 16:46:10 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
// Create proof
|
2018-07-17 17:02:10 -06:00
|
|
|
let proof = create_random_proof(
|
|
|
|
instance,
|
|
|
|
unsafe { SAPLING_OUTPUT_PARAMS.as_ref() }.unwrap(),
|
|
|
|
&mut rng,
|
|
|
|
).expect("proving should not fail");
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// 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<Bls12, Unknown> = 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::<Bls12>::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)
|
2018-07-17 17:02:10 -06:00
|
|
|
let rk =
|
|
|
|
redjubjub::PublicKey::from_private(&rsk, FixedGenerators::SpendingKeyGenerator, &JUBJUB);
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// Compute the signature's message for rk/spend_auth_sig
|
|
|
|
let mut data_to_be_signed = [0u8; 64];
|
2018-07-17 17:02:10 -06:00
|
|
|
rk.0
|
|
|
|
.write(&mut data_to_be_signed[0..32])
|
|
|
|
.expect("message buffer should be 32 bytes");
|
2018-07-17 16:46:10 -06:00
|
|
|
(&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");
|
2018-07-17 17:02:10 -06:00
|
|
|
let sig = rsk.sign(
|
|
|
|
&data_to_be_signed,
|
|
|
|
&mut rng,
|
|
|
|
FixedGenerators::SpendingKeyGenerator,
|
|
|
|
&JUBJUB,
|
|
|
|
);
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// Write out the signature
|
2018-07-17 17:02:10 -06:00
|
|
|
sig.write(&mut (unsafe { &mut *result })[..])
|
|
|
|
.expect("result should be 64 bytes");
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
true
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
|
|
|
pub extern "system" fn librustzcash_sapling_binding_sig(
|
2018-07-18 15:33:19 -06:00
|
|
|
ctx: *const SaplingProvingContext,
|
2018-07-17 16:46:10 -06:00
|
|
|
value_balance: int64_t,
|
|
|
|
sighash: *const [c_uchar; 32],
|
|
|
|
result: *mut [c_uchar; 64],
|
2018-07-17 17:02:10 -06:00
|
|
|
) -> bool {
|
2018-07-17 16:46:10 -06:00
|
|
|
// Grab the current `bsk` from the context
|
|
|
|
let bsk = redjubjub::PrivateKey::<Bls12>(unsafe { &*ctx }.bsk);
|
|
|
|
|
|
|
|
// Grab the `bvk` using DerivePublic.
|
2018-07-17 17:02:10 -06:00
|
|
|
let bvk = redjubjub::PublicKey::from_private(
|
|
|
|
&bsk,
|
|
|
|
FixedGenerators::ValueCommitmentRandomness,
|
|
|
|
&JUBJUB,
|
|
|
|
);
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// 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];
|
2018-07-17 17:02:10 -06:00
|
|
|
bvk.0
|
|
|
|
.write(&mut data_to_be_signed[0..32])
|
|
|
|
.expect("message buffer should be 32 bytes");
|
2018-07-17 16:46:10 -06:00
|
|
|
(&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");
|
2018-07-17 17:02:10 -06:00
|
|
|
let sig = bsk.sign(
|
|
|
|
&data_to_be_signed,
|
|
|
|
&mut rng,
|
|
|
|
FixedGenerators::ValueCommitmentRandomness,
|
|
|
|
&JUBJUB,
|
|
|
|
);
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// Write out signature
|
2018-07-17 17:02:10 -06:00
|
|
|
sig.write(&mut (unsafe { &mut *result })[..])
|
|
|
|
.expect("result should be 64 bytes");
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
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::<Bls12> {
|
|
|
|
value: value,
|
|
|
|
randomness: rcv,
|
|
|
|
};
|
|
|
|
|
|
|
|
// Grab `ak` from the caller, which should be a point.
|
|
|
|
let ak = match edwards::Point::<Bls12, Unknown>::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
|
2018-07-17 17:02:10 -06:00
|
|
|
let rk = redjubjub::PublicKey::<Bls12>(ak.into()).randomize(
|
|
|
|
ar,
|
|
|
|
FixedGenerators::SpendingKeyGenerator,
|
|
|
|
&JUBJUB,
|
|
|
|
);
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// Write out `rk` to the caller
|
2018-07-17 17:02:10 -06:00
|
|
|
rk.write(&mut unsafe { &mut *rk_out }[..])
|
|
|
|
.expect("should be able to write to rk_out");
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// 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::<LittleEndian>()
|
|
|
|
.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,
|
2018-07-17 17:02:10 -06:00
|
|
|
g_d: diversifier
|
|
|
|
.g_d::<Bls12>(&JUBJUB)
|
|
|
|
.expect("was a valid diversifier before"),
|
2018-07-17 16:46:10 -06:00
|
|
|
pk_d: payment_address.pk_d.clone(),
|
2018-07-17 17:02:10 -06:00
|
|
|
r: rcm,
|
2018-07-17 16:46:10 -06:00
|
|
|
};
|
|
|
|
|
|
|
|
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
|
2018-07-17 17:02:10 -06:00
|
|
|
let proof = create_random_proof(
|
|
|
|
instance,
|
|
|
|
unsafe { SAPLING_SPEND_PARAMS.as_ref() }.unwrap(),
|
|
|
|
&mut rng,
|
|
|
|
).expect("proving should not fail");
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// 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::<Bls12>(&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
|
2018-07-17 17:02:10 -06:00
|
|
|
Ok(true) => {}
|
2018-07-17 16:46:10 -06:00
|
|
|
|
|
|
|
// Any other case
|
2018-07-17 17:02:10 -06:00
|
|
|
_ => {
|
|
|
|
return false;
|
|
|
|
}
|
2018-07-17 16:46:10 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
// Compute value commitment
|
|
|
|
let value_commitment: edwards::Point<Bls12, Unknown> = 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]
|
2018-07-17 17:02:10 -06:00
|
|
|
pub extern "system" fn librustzcash_sapling_proving_ctx_init() -> *mut SaplingProvingContext {
|
2018-07-17 16:46:10 -06:00
|
|
|
let ctx = Box::new(SaplingProvingContext {
|
|
|
|
bsk: Fs::zero(),
|
2018-07-17 17:02:10 -06:00
|
|
|
bvk: edwards::Point::zero(),
|
2018-07-17 16:46:10 -06:00
|
|
|
});
|
|
|
|
|
|
|
|
Box::into_raw(ctx)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[no_mangle]
|
2018-07-17 17:02:10 -06:00
|
|
|
pub extern "system" fn librustzcash_sapling_proving_ctx_free(ctx: *mut SaplingProvingContext) {
|
2018-07-17 16:46:10 -06:00
|
|
|
drop(unsafe { Box::from_raw(ctx) });
|
|
|
|
}
|