From 065154cdd1b1abcb27a099ffe5aa48ea7aa3bdc8 Mon Sep 17 00:00:00 2001 From: Sean Bowe Date: Tue, 12 Jun 2018 15:32:20 -0600 Subject: [PATCH] Implementation of Sapling key agreement. --- include/librustzcash.h | 24 ++++++++++++- src/rustzcash.rs | 69 +++++++++++++++++++++++++++++++---- src/tests/key_agreement.rs | 74 ++++++++++++++++++++++++++++++++++++++ src/tests/mod.rs | 1 + 4 files changed, 161 insertions(+), 7 deletions(-) create mode 100644 src/tests/key_agreement.rs diff --git a/include/librustzcash.h b/include/librustzcash.h index 2a222a8..e2bf929 100644 --- a/include/librustzcash.h +++ b/include/librustzcash.h @@ -131,7 +131,29 @@ extern "C" { unsigned char *result ); - /// Generate uniform Sapling commitment randomness `r`. + /// Compute [sk] [8] P for some 32-byte + /// point P, and 32-byte Fs. If P or sk + /// are invalid, returns false. Otherwise, + /// the result is written to the 32-byte + /// `result` buffer. + bool librustzcash_sapling_ka_agree( + const unsigned char *p, + const unsigned char *sk, + unsigned char *result + ); + + /// Compute g_d = GH(diversifier) and returns + /// false if the diversifier is invalid. + /// Computes [esk] g_d and writes the result + /// to the 32-byte `result` buffer. Returns + /// false if `esk` is not a valid scalar. + bool librustzcash_sapling_ka_derivepublic( + const unsigned char *diversifier, + const unsigned char *esk, + unsigned char *result + ); + + /// Generate uniformly random scalar in Jubjub. /// The result is of length 32. void librustzcash_sapling_generate_r( unsigned char *result diff --git a/src/rustzcash.rs b/src/rustzcash.rs index 619955c..2a09c0e 100644 --- a/src/rustzcash.rs +++ b/src/rustzcash.rs @@ -322,7 +322,7 @@ fn test_gen_r() { let _ = Fs::from_repr(repr).unwrap(); } -/// Return 32 byte randomness, uniform, to be used for a Sapling commitment. +/// Return 32 byte random scalar, uniformly. #[no_mangle] pub extern "system" fn librustzcash_sapling_generate_r(result: *mut [c_uchar; 32]) { // create random 64 byte buffer @@ -364,11 +364,8 @@ fn priv_get_note( }; // Deserialize randomness - let r = unsafe { *r }; - let mut repr = FsRepr::default(); - repr.read_le(&r[..]).expect("length is not 32 bytes"); - let r = match Fs::from_repr(repr) { - Ok(p) => p, + let r = match Fs::from_repr(read_fs(&(unsafe { &*r })[..])) { + Ok(r) => r, Err(_) => return Err(()), }; @@ -447,6 +444,66 @@ pub extern "system" fn librustzcash_sapling_compute_cm( true } + +#[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::::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, + Err(_) => return false + }; + + // 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::(&JUBJUB) { + Some(g) => g, + None => return false + }; + + // 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 +} + #[no_mangle] pub extern "system" fn librustzcash_eh_isvalid( n: uint32_t, diff --git a/src/tests/key_agreement.rs b/src/tests/key_agreement.rs new file mode 100644 index 0000000..01657d1 --- /dev/null +++ b/src/tests/key_agreement.rs @@ -0,0 +1,74 @@ +use pairing::bls12_381::Bls12; +use pairing::{PrimeField, PrimeFieldRepr}; +use rand::{OsRng, Rng}; +use sapling_crypto::jubjub::{edwards, JubjubBls12}; +use sapling_crypto::primitives::{Diversifier, ViewingKey}; + +use { + librustzcash_sapling_generate_r, librustzcash_sapling_ka_agree, + librustzcash_sapling_ka_derivepublic, +}; + +#[test] +fn test_key_agreement() { + let params = JubjubBls12::new(); + let mut rng = OsRng::new().unwrap(); + + // Create random viewing key + let vk = ViewingKey:: { + ak: edwards::Point::rand(&mut rng, ¶ms).mul_by_cofactor(¶ms), + nk: edwards::Point::rand(&mut rng, ¶ms).mul_by_cofactor(¶ms), + }; + + // Create a random address with the viewing key + let addr = loop { + match vk.into_payment_address(Diversifier(rng.gen()), ¶ms) { + Some(a) => break a, + None => {} + } + }; + + // Grab ivk from our viewing key in serialized form + let ivk = vk.ivk(); + let mut ivk_serialized = [0u8; 32]; + ivk.into_repr().write_le(&mut ivk_serialized[..]).unwrap(); + + // Create random esk + let mut esk = [0u8; 32]; + librustzcash_sapling_generate_r(&mut esk); + + // The sender will create a shared secret with the recipient + // by multiplying the pk_d from their address with the esk + // we randomly generated + let mut shared_secret_sender = [0u8; 32]; + + // Serialize pk_d for the call to librustzcash_sapling_ka_agree + let mut addr_pk_d = [0u8; 32]; + addr.pk_d.write(&mut addr_pk_d[..]).unwrap(); + + assert!(librustzcash_sapling_ka_agree( + &addr_pk_d, + &esk, + &mut shared_secret_sender + )); + + // Create epk for the recipient, placed in the transaction. Computed + // using the diversifier and esk. + let mut epk = [0u8; 32]; + assert!(librustzcash_sapling_ka_derivepublic( + &addr.diversifier.0, + &esk, + &mut epk + )); + + // Create sharedSecret with ephemeral key + let mut shared_secret_recipient = [0u8; 32]; + assert!(librustzcash_sapling_ka_agree( + &epk, + &ivk_serialized, + &mut shared_secret_recipient + )); + + assert!(!shared_secret_sender.iter().all(|&v| v == 0)); + assert_eq!(shared_secret_sender, shared_secret_recipient); +} diff --git a/src/tests/mod.rs b/src/tests/mod.rs index ad5ca41..1e55955 100644 --- a/src/tests/mod.rs +++ b/src/tests/mod.rs @@ -5,6 +5,7 @@ use super::JUBJUB; mod notes; mod key_components; mod signatures; +mod key_agreement; #[test] fn sapling_generators() {