Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions include/librustzcash.h
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
122 changes: 119 additions & 3 deletions src/rustzcash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ 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};

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;
Expand Down Expand Up @@ -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 {
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will read from bytes into an Fs in little-endian bit order, which is what we want, but the function librustzcash_crh_ivk produces a little-endian byte order result and writes that out. Then, when you call librustzcash_ivk_to_pkd, it's not going to work correctly.

Copy link
Copy Markdown
Contributor

@str4d str4d May 16, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it? I thought the intention was that the output of BLAKE2s was exactly the representation we wanted. More precisely, I think this is fine, because:

  • Here we are reading a little-endian byte array in big-endian, and then flipping, which means we need to flip both the u64s and their bits.
  • In ViewingKey.ivk() we flip the little-endian byte array before reading it as big endian, and since the endianness of the bits within each byte doesn't matter for reading, that should mean the result is the same as we get here by flipping the bits above.

I'll try and generate some test vectors to confirm.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Now that #12 has been merged, this function needs to be updated (to use read_le and not swap bits).

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");

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);

JUBJUB.generator(p_g).mul(f, &JUBJUB)
}

#[no_mangle]
pub extern "system" fn librustzcash_init_zksnark_params(
spend_path: *const c_char,
Expand Down Expand Up @@ -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
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment is out-of-date (I think it might be intended to apply to the unsafe conversion of result).

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");
}

#[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(
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can see why you might not want to use it though. It looks like you do the right thing here, so meh, it's okay.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, kinda. I think you should do what my code does, interpret it as a scalar (it should always work) and then use a write_fs afterwards, which should be implemented to write out in little endian bit order.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using that API requires six additional conversion read/write operations (to convert ak and nk to scalars and back, and to convert ivk to a Point and back). It didn't seem worth the cycles.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once this has been rebased, we should test this function against the test vectors.

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::<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
}
}

/// XOR two uint64_t values and return the result, used
/// as a temporary mechanism for introducing Rust into
/// Zcash.
Expand Down
25 changes: 25 additions & 0 deletions src/tests/key_components.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -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);
Expand All @@ -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();
Expand Down