diff --git a/include/librustzcash.h b/include/librustzcash.h index 9033dc280..b6e11846f 100644 --- a/include/librustzcash.h +++ b/include/librustzcash.h @@ -6,6 +6,18 @@ extern "C" { uint64_t librustzcash_xor(uint64_t a, uint64_t b); + void librustzcash_to_scalar(const unsigned char *input, unsigned char *result); + + void librustzcash_ask_to_ak(const unsigned char *ask, unsigned char *result); + + void librustzcash_nsk_to_nk(const unsigned char *nsk, unsigned char *result); + + void librustzcash_crh_ivk(const unsigned char *ak, const unsigned char *nk, unsigned char *result); + + bool librustzcash_check_diversifier(const unsigned char *diversifier); + + bool librustzcash_ivk_to_pkd(const unsigned char *ivk, const unsigned char *diversifier, unsigned char *result); + /// Loads the zk-SNARK parameters into memory and saves /// paths as necessary. Only called once. void librustzcash_init_zksnark_params( diff --git a/src/rustzcash.rs b/src/rustzcash.rs index 35c63d391..08fe1f3bb 100644 --- a/src/rustzcash.rs +++ b/src/rustzcash.rs @@ -11,9 +11,9 @@ extern crate lazy_static; use pairing::{BitIterator, Field, PrimeField, PrimeFieldRepr, bls12_381::{Bls12, Fr, FrRepr}}; -use sapling_crypto::{circuit::multipack, - jubjub::{edwards, FixedGenerators, JubjubBls12, JubjubParams, Unknown, - fs::FsRepr}, +use sapling_crypto::{circuit::multipack, constants::CRH_IVK_PERSONALIZATION, + jubjub::{edwards, FixedGenerators, JubjubBls12, JubjubEngine, JubjubParams, + PrimeOrder, ToUniform, Unknown, fs::FsRepr}, pedersen_hash::{pedersen_hash, Personalization}, redjubjub::{self, Signature}}; use sapling_crypto::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}; @@ -21,6 +21,8 @@ use sapling_crypto::circuit::sprout::{self, TREE_DEPTH as SPROUT_TREE_DEPTH}; use bellman::groth16::{create_random_proof, prepare_verifying_key, verify_proof, Parameters, PreparedVerifyingKey, Proof, VerifyingKey}; +use blake2_rfc::blake2s::Blake2s; + use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use rand::OsRng; @@ -71,6 +73,28 @@ fn read_le(from: &[u8]) -> FrRepr { f } +/// Reads an FsRepr from [u8] of length 32 +/// This will panic (abort) if length provided is +/// not correct +fn read_fs(from: &[u8]) -> FsRepr { + assert_eq!(from.len(), 32); + + let mut f = <::Fs as PrimeField>::Repr::default(); + f.read_le(from).expect("length is 32 bytes"); + + 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 { + let f = read_fs(from); + + JUBJUB.generator(p_g).mul(f, &JUBJUB) +} + #[no_mangle] pub extern "system" fn librustzcash_init_zksnark_params( spend_path: *const c_char, @@ -185,6 +209,98 @@ pub extern "system" fn librustzcash_merkle_hash( write_le(tmp, &mut result[..]); } +#[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 = ::Fs::to_uniform(unsafe { &(&*input)[..] }).into_repr(); + + let result = unsafe { &mut *result }; + + scalar + .write_le(&mut result[..]) + .expect("length is 32 bytes"); +} + +#[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"); +} + +#[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); +} + +#[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::(&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::(&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 + } +} + /// XOR two uint64_t values and return the result, used /// as a temporary mechanism for introducing Rust into /// Zcash. diff --git a/src/tests/key_components.rs b/src/tests/key_components.rs index af3278e73..8b72b109d 100644 --- a/src/tests/key_components.rs +++ b/src/tests/key_components.rs @@ -4,6 +4,9 @@ use sapling_crypto::{jubjub::{FixedGenerators, JubjubEngine, JubjubParams, fs::F use super::JUBJUB; +use {librustzcash_ask_to_ak, librustzcash_check_diversifier, librustzcash_crh_ivk, + librustzcash_ivk_to_pkd, librustzcash_nsk_to_nk}; + #[test] fn key_components() { #![allow(dead_code)] @@ -601,6 +604,11 @@ fn key_components() { ak.write(&mut vec).unwrap(); assert_eq!(&vec, &tv.ak); } + { + let mut ak = [0u8; 32]; + librustzcash_ask_to_ak(&tv.ask, &mut ak); + assert_eq!(&ak, &tv.ak); + } let pgk = ProofGenerationKey { ak, nsk }; let fvk = pgk.into_viewing_key(&JUBJUB); @@ -609,20 +617,37 @@ fn key_components() { fvk.nk.write(&mut vec).unwrap(); assert_eq!(&vec, &tv.nk); } + { + let mut nk = [0u8; 32]; + librustzcash_nsk_to_nk(&tv.nsk, &mut nk); + assert_eq!(&nk, &tv.nk); + } { let mut vec = Vec::new(); fvk.ivk().into_repr().write_le(&mut vec).unwrap(); assert_eq!(&vec, &tv.ivk); } + { + let mut ivk = [0u8; 32]; + librustzcash_crh_ivk(&tv.ak, &tv.nk, &mut ivk); + assert_eq!(&ivk, &tv.ivk); + } let diversifier = Diversifier(tv.default_d); + assert!(librustzcash_check_diversifier(&tv.default_d)); + let addr = fvk.into_payment_address(diversifier, &JUBJUB).unwrap(); { let mut vec = Vec::new(); addr.pk_d.write(&mut vec).unwrap(); assert_eq!(&vec, &tv.default_pk_d); } + { + let mut default_pk_d = [0u8; 32]; + librustzcash_ivk_to_pkd(&tv.ivk, &tv.default_d, &mut default_pk_d); + assert_eq!(&default_pk_d, &tv.default_pk_d); + } let mut note_r_repr = FsRepr::default(); note_r_repr.read_le(&tv.note_r[..]).unwrap();