mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-02-15 19:25:47 +00:00
Merge pull request #69 from str4d/sapling-note-encryption
Sapling note encryption
This commit is contained in:
commit
3b6f5e3d5e
@ -1,6 +1,6 @@
|
|||||||
language: rust
|
language: rust
|
||||||
rust:
|
rust:
|
||||||
- 1.31.0
|
- 1.32.0
|
||||||
|
|
||||||
cache: cargo
|
cache: cargo
|
||||||
|
|
||||||
|
16
Cargo.lock
generated
16
Cargo.lock
generated
@ -124,6 +124,19 @@ name = "crossbeam"
|
|||||||
version = "0.3.2"
|
version = "0.3.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto_api"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crypto_api_chachapoly"
|
||||||
|
version = "0.1.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
dependencies = [
|
||||||
|
"crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "digest"
|
name = "digest"
|
||||||
version = "0.7.2"
|
version = "0.7.2"
|
||||||
@ -500,6 +513,7 @@ version = "0.0.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
|
"blake2-rfc 0.2.18 (git+https://github.com/gtank/blake2-rfc?rev=7a5b5fc99ae483a0043db7547fb79a6fa44b88a9)",
|
||||||
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"crypto_api_chachapoly 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"ff 0.4.0",
|
"ff 0.4.0",
|
||||||
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
"lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
@ -556,6 +570,8 @@ dependencies = [
|
|||||||
"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87"
|
"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87"
|
||||||
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
"checksum constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "8ff012e225ce166d4422e0e78419d901719760f62ae2b7969ca6b564d1b54a9e"
|
||||||
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
|
"checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19"
|
||||||
|
"checksum crypto_api 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2f855e87e75a4799e18b8529178adcde6fd4f97c1449ff4821e747ff728bb102"
|
||||||
|
"checksum crypto_api_chachapoly 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9ee35dbace0831b5fe7cb9b43eb029aa14a10f594a115025d4628a2baa63ab"
|
||||||
"checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603"
|
"checksum digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603"
|
||||||
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
|
"checksum digest 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "05f47366984d3ad862010e22c7ce81a7dbcaebbdfb37241a620f8b6596ee135c"
|
||||||
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
"checksum fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||||
|
@ -58,7 +58,7 @@ use std::ffi::OsString;
|
|||||||
use std::os::windows::ffi::OsStringExt;
|
use std::os::windows::ffi::OsStringExt;
|
||||||
|
|
||||||
use sapling_crypto::primitives::{ProofGenerationKey, ViewingKey};
|
use sapling_crypto::primitives::{ProofGenerationKey, ViewingKey};
|
||||||
use zcash_primitives::{sapling::spend_sig, JUBJUB};
|
use zcash_primitives::{note_encryption::sapling_ka_agree, sapling::spend_sig, JUBJUB};
|
||||||
use zcash_proofs::{
|
use zcash_proofs::{
|
||||||
load_parameters,
|
load_parameters,
|
||||||
sapling::{CommitmentTreeWitness, SaplingProvingContext, SaplingVerificationContext},
|
sapling::{CommitmentTreeWitness, SaplingProvingContext, SaplingVerificationContext},
|
||||||
@ -536,15 +536,12 @@ pub extern "system" fn librustzcash_sapling_ka_agree(
|
|||||||
Err(_) => return false,
|
Err(_) => return false,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Multiply by 8
|
// Compute key agreement
|
||||||
let p = p.mul_by_cofactor(&JUBJUB);
|
let ka = sapling_ka_agree(&sk, &p);
|
||||||
|
|
||||||
// Multiply by sk
|
|
||||||
let p = p.mul(sk, &JUBJUB);
|
|
||||||
|
|
||||||
// Produce result
|
// Produce result
|
||||||
let result = unsafe { &mut *result };
|
let result = unsafe { &mut *result };
|
||||||
p.write(&mut result[..]).expect("length is not 32 bytes");
|
ka.write(&mut result[..]).expect("length is not 32 bytes");
|
||||||
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
@ -45,6 +45,14 @@ fn convert_subgroup<E: JubjubEngine, S1, S2>(from: &Point<E, S1>) -> Point<E, S2
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> From<&Point<E, Unknown>> for Point<E, Unknown>
|
||||||
|
{
|
||||||
|
fn from(p: &Point<E, Unknown>) -> Point<E, Unknown>
|
||||||
|
{
|
||||||
|
p.clone()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
|
impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
|
||||||
{
|
{
|
||||||
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
|
fn from(p: Point<E, PrimeOrder>) -> Point<E, Unknown>
|
||||||
@ -53,6 +61,14 @@ impl<E: JubjubEngine> From<Point<E, PrimeOrder>> for Point<E, Unknown>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> From<&Point<E, PrimeOrder>> for Point<E, Unknown>
|
||||||
|
{
|
||||||
|
fn from(p: &Point<E, PrimeOrder>) -> Point<E, Unknown>
|
||||||
|
{
|
||||||
|
convert_subgroup(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
|
impl<E: JubjubEngine, Subgroup> Clone for Point<E, Subgroup>
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
|
@ -47,6 +47,7 @@ pub mod tests;
|
|||||||
pub enum Unknown { }
|
pub enum Unknown { }
|
||||||
|
|
||||||
/// Point of prime order.
|
/// Point of prime order.
|
||||||
|
#[derive(Debug)]
|
||||||
pub enum PrimeOrder { }
|
pub enum PrimeOrder { }
|
||||||
|
|
||||||
/// Fixed generators of the Jubjub curve of unknown
|
/// Fixed generators of the Jubjub curve of unknown
|
||||||
|
@ -62,6 +62,7 @@ impl<E: JubjubEngine> ProofGenerationKey<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
pub struct ViewingKey<E: JubjubEngine> {
|
pub struct ViewingKey<E: JubjubEngine> {
|
||||||
pub ak: edwards::Point<E, PrimeOrder>,
|
pub ak: edwards::Point<E, PrimeOrder>,
|
||||||
pub nk: edwards::Point<E, PrimeOrder>
|
pub nk: edwards::Point<E, PrimeOrder>
|
||||||
@ -116,7 +117,7 @@ impl<E: JubjubEngine> ViewingKey<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||||
pub struct Diversifier(pub [u8; 11]);
|
pub struct Diversifier(pub [u8; 11]);
|
||||||
|
|
||||||
impl Diversifier {
|
impl Diversifier {
|
||||||
@ -129,12 +130,18 @@ impl Diversifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct PaymentAddress<E: JubjubEngine> {
|
pub struct PaymentAddress<E: JubjubEngine> {
|
||||||
pub pk_d: edwards::Point<E, PrimeOrder>,
|
pub pk_d: edwards::Point<E, PrimeOrder>,
|
||||||
pub diversifier: Diversifier
|
pub diversifier: Diversifier
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> PartialEq for PaymentAddress<E> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.pk_d == other.pk_d && self.diversifier == other.diversifier
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: JubjubEngine> PaymentAddress<E> {
|
impl<E: JubjubEngine> PaymentAddress<E> {
|
||||||
pub fn g_d(
|
pub fn g_d(
|
||||||
&self,
|
&self,
|
||||||
@ -162,6 +169,7 @@ impl<E: JubjubEngine> PaymentAddress<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
pub struct Note<E: JubjubEngine> {
|
pub struct Note<E: JubjubEngine> {
|
||||||
/// The value of the note
|
/// The value of the note
|
||||||
pub value: u64,
|
pub value: u64,
|
||||||
@ -173,6 +181,15 @@ pub struct Note<E: JubjubEngine> {
|
|||||||
pub r: E::Fs
|
pub r: E::Fs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> PartialEq for Note<E> {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
self.value == other.value
|
||||||
|
&& self.g_d == other.g_d
|
||||||
|
&& self.pk_d == other.pk_d
|
||||||
|
&& self.r == other.r
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<E: JubjubEngine> Note<E> {
|
impl<E: JubjubEngine> Note<E> {
|
||||||
pub fn uncommitted() -> E::Fr {
|
pub fn uncommitted() -> E::Fr {
|
||||||
// The smallest u-coordinate that is not on the curve
|
// The smallest u-coordinate that is not on the curve
|
||||||
|
@ -7,6 +7,7 @@ authors = [
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
byteorder = "1"
|
byteorder = "1"
|
||||||
|
crypto_api_chachapoly = "0.1"
|
||||||
ff = { path = "../ff" }
|
ff = { path = "../ff" }
|
||||||
hex = "0.3"
|
hex = "0.3"
|
||||||
lazy_static = "1"
|
lazy_static = "1"
|
||||||
|
218
zcash_primitives/src/keys.rs
Normal file
218
zcash_primitives/src/keys.rs
Normal file
@ -0,0 +1,218 @@
|
|||||||
|
//! Sapling key components.
|
||||||
|
//!
|
||||||
|
//! Implements section 4.2.2 of the Zcash Protocol Specification.
|
||||||
|
|
||||||
|
use blake2_rfc::blake2b::{Blake2b, Blake2bResult};
|
||||||
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
|
use sapling_crypto::{
|
||||||
|
jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown},
|
||||||
|
primitives::{ProofGenerationKey, ViewingKey},
|
||||||
|
};
|
||||||
|
use std::io::{self, Read, Write};
|
||||||
|
|
||||||
|
pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed";
|
||||||
|
|
||||||
|
/// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t)
|
||||||
|
pub fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bResult {
|
||||||
|
prf_expand_vec(sk, &[t])
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> Blake2bResult {
|
||||||
|
let mut h = Blake2b::with_params(64, &[], &[], PRF_EXPAND_PERSONALIZATION);
|
||||||
|
h.update(sk);
|
||||||
|
for t in ts {
|
||||||
|
h.update(t);
|
||||||
|
}
|
||||||
|
h.finalize()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An outgoing viewing key
|
||||||
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
pub struct OutgoingViewingKey(pub [u8; 32]);
|
||||||
|
|
||||||
|
/// A Sapling expanded spending key
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct ExpandedSpendingKey<E: JubjubEngine> {
|
||||||
|
pub ask: E::Fs,
|
||||||
|
pub nsk: E::Fs,
|
||||||
|
pub ovk: OutgoingViewingKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A Sapling full viewing key
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct FullViewingKey<E: JubjubEngine> {
|
||||||
|
pub vk: ViewingKey<E>,
|
||||||
|
pub ovk: OutgoingViewingKey,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> ExpandedSpendingKey<E> {
|
||||||
|
pub fn from_spending_key(sk: &[u8]) -> Self {
|
||||||
|
let ask = E::Fs::to_uniform(prf_expand(sk, &[0x00]).as_bytes());
|
||||||
|
let nsk = E::Fs::to_uniform(prf_expand(sk, &[0x01]).as_bytes());
|
||||||
|
let mut ovk = OutgoingViewingKey([0u8; 32]);
|
||||||
|
ovk.0
|
||||||
|
.copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]);
|
||||||
|
ExpandedSpendingKey { ask, nsk, ovk }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn proof_generation_key(&self, params: &E::Params) -> ProofGenerationKey<E> {
|
||||||
|
ProofGenerationKey {
|
||||||
|
ak: params
|
||||||
|
.generator(FixedGenerators::SpendingKeyGenerator)
|
||||||
|
.mul(self.ask, params),
|
||||||
|
nsk: self.nsk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
||||||
|
let mut ask_repr = <E::Fs as PrimeField>::Repr::default();
|
||||||
|
ask_repr.read_le(&mut reader)?;
|
||||||
|
let ask = E::Fs::from_repr(ask_repr)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
let mut nsk_repr = <E::Fs as PrimeField>::Repr::default();
|
||||||
|
nsk_repr.read_le(&mut reader)?;
|
||||||
|
let nsk = E::Fs::from_repr(nsk_repr)
|
||||||
|
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||||
|
|
||||||
|
let mut ovk = [0; 32];
|
||||||
|
reader.read_exact(&mut ovk)?;
|
||||||
|
|
||||||
|
Ok(ExpandedSpendingKey {
|
||||||
|
ask,
|
||||||
|
nsk,
|
||||||
|
ovk: OutgoingViewingKey(ovk),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
self.ask.into_repr().write_le(&mut writer)?;
|
||||||
|
self.nsk.into_repr().write_le(&mut writer)?;
|
||||||
|
writer.write_all(&self.ovk.0)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self) -> [u8; 96] {
|
||||||
|
let mut result = [0u8; 96];
|
||||||
|
self.write(&mut result[..])
|
||||||
|
.expect("should be able to serialize an ExpandedSpendingKey");
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> Clone for FullViewingKey<E> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
FullViewingKey {
|
||||||
|
vk: ViewingKey {
|
||||||
|
ak: self.vk.ak.clone(),
|
||||||
|
nk: self.vk.nk.clone(),
|
||||||
|
},
|
||||||
|
ovk: self.ovk.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> FullViewingKey<E> {
|
||||||
|
pub fn from_expanded_spending_key(expsk: &ExpandedSpendingKey<E>, params: &E::Params) -> Self {
|
||||||
|
FullViewingKey {
|
||||||
|
vk: ViewingKey {
|
||||||
|
ak: params
|
||||||
|
.generator(FixedGenerators::SpendingKeyGenerator)
|
||||||
|
.mul(expsk.ask, params),
|
||||||
|
nk: params
|
||||||
|
.generator(FixedGenerators::ProofGenerationKey)
|
||||||
|
.mul(expsk.nsk, params),
|
||||||
|
},
|
||||||
|
ovk: expsk.ovk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read<R: Read>(mut reader: R, params: &E::Params) -> io::Result<Self> {
|
||||||
|
let ak = edwards::Point::<E, Unknown>::read(&mut reader, params)?;
|
||||||
|
let ak = match ak.as_prime_order(params) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"ak not in prime-order subgroup",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ak == edwards::Point::zero() {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"ak not of prime order",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
let nk = edwards::Point::<E, Unknown>::read(&mut reader, params)?;
|
||||||
|
let nk = match nk.as_prime_order(params) {
|
||||||
|
Some(p) => p,
|
||||||
|
None => {
|
||||||
|
return Err(io::Error::new(
|
||||||
|
io::ErrorKind::InvalidData,
|
||||||
|
"nk not in prime-order subgroup",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut ovk = [0; 32];
|
||||||
|
reader.read_exact(&mut ovk)?;
|
||||||
|
|
||||||
|
Ok(FullViewingKey {
|
||||||
|
vk: ViewingKey { ak, nk },
|
||||||
|
ovk: OutgoingViewingKey(ovk),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
||||||
|
self.vk.ak.write(&mut writer)?;
|
||||||
|
self.vk.nk.write(&mut writer)?;
|
||||||
|
writer.write_all(&self.ovk.0)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn to_bytes(&self) -> [u8; 96] {
|
||||||
|
let mut result = [0u8; 96];
|
||||||
|
self.write(&mut result[..])
|
||||||
|
.expect("should be able to serialize a FullViewingKey");
|
||||||
|
result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use pairing::bls12_381::Bls12;
|
||||||
|
use sapling_crypto::jubjub::{edwards, FixedGenerators, JubjubParams, PrimeOrder};
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
use super::FullViewingKey;
|
||||||
|
use crate::JUBJUB;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ak_must_be_prime_order() {
|
||||||
|
let mut buf = [0; 96];
|
||||||
|
let identity = edwards::Point::<Bls12, PrimeOrder>::zero();
|
||||||
|
|
||||||
|
// Set both ak and nk to the identity.
|
||||||
|
identity.write(&mut buf[0..32]).unwrap();
|
||||||
|
identity.write(&mut buf[32..64]).unwrap();
|
||||||
|
|
||||||
|
// ak is not allowed to be the identity.
|
||||||
|
assert_eq!(
|
||||||
|
FullViewingKey::<Bls12>::read(&buf[..], &JUBJUB)
|
||||||
|
.unwrap_err()
|
||||||
|
.description(),
|
||||||
|
"ak not of prime order"
|
||||||
|
);
|
||||||
|
|
||||||
|
// Set ak to a basepoint.
|
||||||
|
let basepoint = JUBJUB.generator(FixedGenerators::SpendingKeyGenerator);
|
||||||
|
basepoint.write(&mut buf[0..32]).unwrap();
|
||||||
|
|
||||||
|
// nk is allowed to be the identity.
|
||||||
|
assert!(FullViewingKey::<Bls12>::read(&buf[..], &JUBJUB).is_ok());
|
||||||
|
}
|
||||||
|
}
|
@ -3,6 +3,7 @@ extern crate lazy_static;
|
|||||||
|
|
||||||
extern crate blake2_rfc;
|
extern crate blake2_rfc;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
extern crate crypto_api_chachapoly;
|
||||||
extern crate ff;
|
extern crate ff;
|
||||||
extern crate hex;
|
extern crate hex;
|
||||||
extern crate pairing;
|
extern crate pairing;
|
||||||
@ -13,10 +14,15 @@ extern crate sha2;
|
|||||||
use sapling_crypto::jubjub::JubjubBls12;
|
use sapling_crypto::jubjub::JubjubBls12;
|
||||||
|
|
||||||
pub mod block;
|
pub mod block;
|
||||||
|
pub mod keys;
|
||||||
|
pub mod note_encryption;
|
||||||
pub mod sapling;
|
pub mod sapling;
|
||||||
mod serialize;
|
mod serialize;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test_vectors;
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
pub static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() };
|
pub static ref JUBJUB: JubjubBls12 = { JubjubBls12::new() };
|
||||||
}
|
}
|
||||||
|
1377
zcash_primitives/src/note_encryption.rs
Normal file
1377
zcash_primitives/src/note_encryption.rs
Normal file
File diff suppressed because it is too large
Load Diff
1
zcash_primitives/src/test_vectors.rs
Normal file
1
zcash_primitives/src/test_vectors.rs
Normal file
@ -0,0 +1 @@
|
|||||||
|
pub(crate) mod note_encryption;
|
2046
zcash_primitives/src/test_vectors/note_encryption.rs
Normal file
2046
zcash_primitives/src/test_vectors/note_encryption.rs
Normal file
File diff suppressed because it is too large
Load Diff
269
zip32/src/lib.rs
269
zip32/src/lib.rs
@ -8,228 +8,30 @@ extern crate sapling_crypto;
|
|||||||
extern crate zcash_primitives;
|
extern crate zcash_primitives;
|
||||||
|
|
||||||
use aes::Aes256;
|
use aes::Aes256;
|
||||||
use blake2_rfc::blake2b::{Blake2b, Blake2bResult};
|
use blake2_rfc::blake2b::Blake2b;
|
||||||
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
|
use byteorder::{ByteOrder, LittleEndian, ReadBytesExt, WriteBytesExt};
|
||||||
use ff::{Field, PrimeField, PrimeFieldRepr};
|
use ff::Field;
|
||||||
use fpe::ff1::{BinaryNumeralString, FF1};
|
use fpe::ff1::{BinaryNumeralString, FF1};
|
||||||
use pairing::bls12_381::Bls12;
|
use pairing::bls12_381::Bls12;
|
||||||
use sapling_crypto::{
|
use sapling_crypto::{
|
||||||
jubjub::{edwards, FixedGenerators, JubjubEngine, JubjubParams, ToUniform, Unknown},
|
jubjub::{fs::Fs, FixedGenerators, JubjubEngine, JubjubParams, ToUniform},
|
||||||
primitives::{Diversifier, PaymentAddress, ProofGenerationKey, ViewingKey},
|
primitives::{Diversifier, PaymentAddress, ViewingKey},
|
||||||
};
|
};
|
||||||
use std::io::{self, Read, Write};
|
use std::io::{self, Read, Write};
|
||||||
use zcash_primitives::JUBJUB;
|
use zcash_primitives::{
|
||||||
|
keys::{prf_expand, prf_expand_vec, ExpandedSpendingKey, FullViewingKey, OutgoingViewingKey},
|
||||||
|
JUBJUB,
|
||||||
|
};
|
||||||
|
|
||||||
pub const PRF_EXPAND_PERSONALIZATION: &'static [u8; 16] = b"Zcash_ExpandSeed";
|
|
||||||
pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &'static [u8; 16] = b"ZcashIP32Sapling";
|
pub const ZIP32_SAPLING_MASTER_PERSONALIZATION: &'static [u8; 16] = b"ZcashIP32Sapling";
|
||||||
pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &'static [u8; 16] = b"ZcashSaplingFVFP";
|
pub const ZIP32_SAPLING_FVFP_PERSONALIZATION: &'static [u8; 16] = b"ZcashSaplingFVFP";
|
||||||
|
|
||||||
// Sapling key components
|
// Common helper functions
|
||||||
|
|
||||||
/// PRF^expand(sk, t) := BLAKE2b-512("Zcash_ExpandSeed", sk || t)
|
fn derive_child_ovk(parent: &OutgoingViewingKey, i_l: &[u8]) -> OutgoingViewingKey {
|
||||||
fn prf_expand(sk: &[u8], t: &[u8]) -> Blake2bResult {
|
let mut ovk = [0u8; 32];
|
||||||
prf_expand_vec(sk, &vec![t])
|
ovk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x15], &parent.0]).as_bytes()[..32]);
|
||||||
}
|
OutgoingViewingKey(ovk)
|
||||||
|
|
||||||
fn prf_expand_vec(sk: &[u8], ts: &[&[u8]]) -> Blake2bResult {
|
|
||||||
let mut h = Blake2b::with_params(64, &[], &[], PRF_EXPAND_PERSONALIZATION);
|
|
||||||
h.update(sk);
|
|
||||||
for t in ts {
|
|
||||||
h.update(t);
|
|
||||||
}
|
|
||||||
h.finalize()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An outgoing viewing key
|
|
||||||
#[derive(Clone, Copy, PartialEq)]
|
|
||||||
pub struct OutgoingViewingKey([u8; 32]);
|
|
||||||
|
|
||||||
impl OutgoingViewingKey {
|
|
||||||
fn derive_child(&self, i_l: &[u8]) -> Self {
|
|
||||||
let mut ovk = [0u8; 32];
|
|
||||||
ovk.copy_from_slice(&prf_expand_vec(i_l, &[&[0x15], &self.0]).as_bytes()[..32]);
|
|
||||||
OutgoingViewingKey(ovk)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Sapling expanded spending key
|
|
||||||
#[derive(Clone)]
|
|
||||||
pub struct ExpandedSpendingKey<E: JubjubEngine> {
|
|
||||||
pub ask: E::Fs,
|
|
||||||
nsk: E::Fs,
|
|
||||||
ovk: OutgoingViewingKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Sapling full viewing key
|
|
||||||
pub struct FullViewingKey<E: JubjubEngine> {
|
|
||||||
pub vk: ViewingKey<E>,
|
|
||||||
pub ovk: OutgoingViewingKey,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: JubjubEngine> ExpandedSpendingKey<E> {
|
|
||||||
fn from_spending_key(sk: &[u8]) -> Self {
|
|
||||||
let ask = E::Fs::to_uniform(prf_expand(sk, &[0x00]).as_bytes());
|
|
||||||
let nsk = E::Fs::to_uniform(prf_expand(sk, &[0x01]).as_bytes());
|
|
||||||
let mut ovk = OutgoingViewingKey([0u8; 32]);
|
|
||||||
ovk.0
|
|
||||||
.copy_from_slice(&prf_expand(sk, &[0x02]).as_bytes()[..32]);
|
|
||||||
ExpandedSpendingKey { ask, nsk, ovk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn proof_generation_key(&self, params: &E::Params) -> ProofGenerationKey<E> {
|
|
||||||
ProofGenerationKey {
|
|
||||||
ak: params
|
|
||||||
.generator(FixedGenerators::SpendingKeyGenerator)
|
|
||||||
.mul(self.ask, params),
|
|
||||||
nsk: self.nsk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn derive_child(&self, i_l: &[u8]) -> Self {
|
|
||||||
let mut ask = E::Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes());
|
|
||||||
let mut nsk = E::Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes());
|
|
||||||
ask.add_assign(&self.ask);
|
|
||||||
nsk.add_assign(&self.nsk);
|
|
||||||
let ovk = self.ovk.derive_child(i_l);
|
|
||||||
ExpandedSpendingKey { ask, nsk, ovk }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<R: Read>(mut reader: R) -> io::Result<Self> {
|
|
||||||
let mut ask_repr = <E::Fs as PrimeField>::Repr::default();
|
|
||||||
ask_repr.read_le(&mut reader)?;
|
|
||||||
let ask = E::Fs::from_repr(ask_repr)
|
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
|
||||||
|
|
||||||
let mut nsk_repr = <E::Fs as PrimeField>::Repr::default();
|
|
||||||
nsk_repr.read_le(&mut reader)?;
|
|
||||||
let nsk = E::Fs::from_repr(nsk_repr)
|
|
||||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
|
||||||
|
|
||||||
let mut ovk = [0; 32];
|
|
||||||
reader.read_exact(&mut ovk)?;
|
|
||||||
|
|
||||||
Ok(ExpandedSpendingKey {
|
|
||||||
ask,
|
|
||||||
nsk,
|
|
||||||
ovk: OutgoingViewingKey(ovk),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
|
||||||
self.ask.into_repr().write_le(&mut writer)?;
|
|
||||||
self.nsk.into_repr().write_le(&mut writer)?;
|
|
||||||
writer.write_all(&self.ovk.0)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_bytes(&self) -> [u8; 96] {
|
|
||||||
let mut result = [0u8; 96];
|
|
||||||
self.write(&mut result[..])
|
|
||||||
.expect("should be able to serialize an ExpandedSpendingKey");
|
|
||||||
result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: JubjubEngine> Clone for FullViewingKey<E> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
FullViewingKey {
|
|
||||||
vk: ViewingKey {
|
|
||||||
ak: self.vk.ak.clone(),
|
|
||||||
nk: self.vk.nk.clone(),
|
|
||||||
},
|
|
||||||
ovk: self.ovk.clone(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<E: JubjubEngine> FullViewingKey<E> {
|
|
||||||
fn from_expanded_spending_key(expsk: &ExpandedSpendingKey<E>, params: &E::Params) -> Self {
|
|
||||||
FullViewingKey {
|
|
||||||
vk: ViewingKey {
|
|
||||||
ak: params
|
|
||||||
.generator(FixedGenerators::SpendingKeyGenerator)
|
|
||||||
.mul(expsk.ask, params),
|
|
||||||
nk: params
|
|
||||||
.generator(FixedGenerators::ProofGenerationKey)
|
|
||||||
.mul(expsk.nsk, params),
|
|
||||||
},
|
|
||||||
ovk: expsk.ovk,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn derive_child(&self, i_l: &[u8], params: &E::Params) -> Self {
|
|
||||||
let i_ask = E::Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes());
|
|
||||||
let i_nsk = E::Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes());
|
|
||||||
let ak = params
|
|
||||||
.generator(FixedGenerators::SpendingKeyGenerator)
|
|
||||||
.mul(i_ask, params)
|
|
||||||
.add(&self.vk.ak, params);
|
|
||||||
let nk = params
|
|
||||||
.generator(FixedGenerators::ProofGenerationKey)
|
|
||||||
.mul(i_nsk, params)
|
|
||||||
.add(&self.vk.nk, params);
|
|
||||||
|
|
||||||
FullViewingKey {
|
|
||||||
vk: ViewingKey { ak, nk },
|
|
||||||
ovk: self.ovk.derive_child(i_l),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<R: Read>(mut reader: R, params: &E::Params) -> io::Result<Self> {
|
|
||||||
let ak = edwards::Point::<E, Unknown>::read(&mut reader, params)?;
|
|
||||||
let ak = match ak.as_prime_order(params) {
|
|
||||||
Some(p) => p,
|
|
||||||
None => {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
"ak not of prime order",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let nk = edwards::Point::<E, Unknown>::read(&mut reader, params)?;
|
|
||||||
let nk = match nk.as_prime_order(params) {
|
|
||||||
Some(p) => p,
|
|
||||||
None => {
|
|
||||||
return Err(io::Error::new(
|
|
||||||
io::ErrorKind::InvalidData,
|
|
||||||
"nk not of prime order",
|
|
||||||
));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ovk = [0; 32];
|
|
||||||
reader.read_exact(&mut ovk)?;
|
|
||||||
|
|
||||||
Ok(FullViewingKey {
|
|
||||||
vk: ViewingKey { ak, nk },
|
|
||||||
ovk: OutgoingViewingKey(ovk),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write<W: Write>(&self, mut writer: W) -> io::Result<()> {
|
|
||||||
self.vk.ak.write(&mut writer)?;
|
|
||||||
self.vk.nk.write(&mut writer)?;
|
|
||||||
writer.write_all(&self.ovk.0)?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_bytes(&self) -> [u8; 96] {
|
|
||||||
let mut result = [0u8; 96];
|
|
||||||
self.write(&mut result[..])
|
|
||||||
.expect("should be able to serialize a FullViewingKey");
|
|
||||||
result
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fingerprint(&self) -> FVKFingerprint {
|
|
||||||
let mut h = Blake2b::with_params(32, &[], &[], ZIP32_SAPLING_FVFP_PERSONALIZATION);
|
|
||||||
h.update(&self.to_bytes());
|
|
||||||
let mut fvfp = [0u8; 32];
|
|
||||||
fvfp.copy_from_slice(h.finalize().as_bytes());
|
|
||||||
FVKFingerprint(fvfp)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ZIP 32 structures
|
// ZIP 32 structures
|
||||||
@ -237,6 +39,16 @@ impl<E: JubjubEngine> FullViewingKey<E> {
|
|||||||
/// A Sapling full viewing key fingerprint
|
/// A Sapling full viewing key fingerprint
|
||||||
struct FVKFingerprint([u8; 32]);
|
struct FVKFingerprint([u8; 32]);
|
||||||
|
|
||||||
|
impl<E: JubjubEngine> From<&FullViewingKey<E>> for FVKFingerprint {
|
||||||
|
fn from(fvk: &FullViewingKey<E>) -> Self {
|
||||||
|
let mut h = Blake2b::with_params(32, &[], &[], ZIP32_SAPLING_FVFP_PERSONALIZATION);
|
||||||
|
h.update(&fvk.to_bytes());
|
||||||
|
let mut fvfp = [0u8; 32];
|
||||||
|
fvfp.copy_from_slice(h.finalize().as_bytes());
|
||||||
|
FVKFingerprint(fvfp)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A Sapling full viewing key tag
|
/// A Sapling full viewing key tag
|
||||||
#[derive(Clone, Copy, Debug, PartialEq)]
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
struct FVKTag([u8; 4]);
|
struct FVKTag([u8; 4]);
|
||||||
@ -506,10 +318,17 @@ impl ExtendedSpendingKey {
|
|||||||
|
|
||||||
ExtendedSpendingKey {
|
ExtendedSpendingKey {
|
||||||
depth: self.depth + 1,
|
depth: self.depth + 1,
|
||||||
parent_fvk_tag: fvk.fingerprint().tag(),
|
parent_fvk_tag: FVKFingerprint::from(&fvk).tag(),
|
||||||
child_index: i,
|
child_index: i,
|
||||||
chain_code: ChainCode(c_i),
|
chain_code: ChainCode(c_i),
|
||||||
expsk: self.expsk.derive_child(i_l),
|
expsk: {
|
||||||
|
let mut ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes());
|
||||||
|
let mut nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes());
|
||||||
|
ask.add_assign(&self.expsk.ask);
|
||||||
|
nsk.add_assign(&self.expsk.nsk);
|
||||||
|
let ovk = derive_child_ovk(&self.expsk.ovk, i_l);
|
||||||
|
ExpandedSpendingKey { ask, nsk, ovk }
|
||||||
|
},
|
||||||
dk: self.dk.derive_child(i_l),
|
dk: self.dk.derive_child(i_l),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -583,10 +402,26 @@ impl ExtendedFullViewingKey {
|
|||||||
|
|
||||||
Ok(ExtendedFullViewingKey {
|
Ok(ExtendedFullViewingKey {
|
||||||
depth: self.depth + 1,
|
depth: self.depth + 1,
|
||||||
parent_fvk_tag: self.fvk.fingerprint().tag(),
|
parent_fvk_tag: FVKFingerprint::from(&self.fvk).tag(),
|
||||||
child_index: i,
|
child_index: i,
|
||||||
chain_code: ChainCode(c_i),
|
chain_code: ChainCode(c_i),
|
||||||
fvk: self.fvk.derive_child(i_l, &JUBJUB),
|
fvk: {
|
||||||
|
let i_ask = Fs::to_uniform(prf_expand(i_l, &[0x13]).as_bytes());
|
||||||
|
let i_nsk = Fs::to_uniform(prf_expand(i_l, &[0x14]).as_bytes());
|
||||||
|
let ak = JUBJUB
|
||||||
|
.generator(FixedGenerators::SpendingKeyGenerator)
|
||||||
|
.mul(i_ask, &JUBJUB)
|
||||||
|
.add(&self.fvk.vk.ak, &JUBJUB);
|
||||||
|
let nk = JUBJUB
|
||||||
|
.generator(FixedGenerators::ProofGenerationKey)
|
||||||
|
.mul(i_nsk, &JUBJUB)
|
||||||
|
.add(&self.fvk.vk.nk, &JUBJUB);
|
||||||
|
|
||||||
|
FullViewingKey {
|
||||||
|
vk: ViewingKey { ak, nk },
|
||||||
|
ovk: derive_child_ovk(&self.fvk.ovk, i_l),
|
||||||
|
}
|
||||||
|
},
|
||||||
dk: self.dk.derive_child(i_l),
|
dk: self.dk.derive_child(i_l),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -614,6 +449,8 @@ impl ExtendedFullViewingKey {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
use ff::{PrimeField, PrimeFieldRepr};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn derive_nonhardened_child() {
|
fn derive_nonhardened_child() {
|
||||||
let seed = [0; 32];
|
let seed = [0; 32];
|
||||||
@ -1213,7 +1050,7 @@ mod tests {
|
|||||||
let mut ser = vec![];
|
let mut ser = vec![];
|
||||||
xfvk.write(&mut ser).unwrap();
|
xfvk.write(&mut ser).unwrap();
|
||||||
assert_eq!(&ser[..], &tv.xfvk[..]);
|
assert_eq!(&ser[..], &tv.xfvk[..]);
|
||||||
assert_eq!(xfvk.fvk.fingerprint().0, tv.fp);
|
assert_eq!(FVKFingerprint::from(&xfvk.fvk).0, tv.fp);
|
||||||
|
|
||||||
// d0
|
// d0
|
||||||
let mut di = DiversifierIndex::new();
|
let mut di = DiversifierIndex::new();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user