Browse Source

Implement Sapling note encryption

hush
George Tankersley 6 years ago
committed by Jack Grigg
parent
commit
65bbe7daed
No known key found for this signature in database GPG Key ID: 9E8255172BBF9898
  1. 10
      Cargo.lock
  2. 1
      sapling-crypto/src/primitives/mod.rs
  3. 4
      zcash_primitives/Cargo.toml
  4. 2
      zcash_primitives/src/lib.rs
  5. 154
      zcash_primitives/src/note_encryption.rs

10
Cargo.lock

@ -114,6 +114,14 @@ name = "byteorder"
version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
[[package]]
name = "chacha20-poly1305-aead"
version = "0.1.2"
source = "git+https://github.com/gtank/chacha20-poly1305-aead?rev=aefc71f95e8bc43f2070e3c5b08130d9c86bbf4f#aefc71f95e8bc43f2070e3c5b08130d9c86bbf4f"
dependencies = [
"constant_time_eq 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "constant_time_eq"
version = "0.1.3"
@ -500,6 +508,7 @@ version = "0.0.0"
dependencies = [
"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)",
"chacha20-poly1305-aead 0.1.2 (git+https://github.com/gtank/chacha20-poly1305-aead?rev=aefc71f95e8bc43f2070e3c5b08130d9c86bbf4f)",
"ff 0.4.0",
"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)",
@ -554,6 +563,7 @@ dependencies = [
"checksum byte-tools 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "560c32574a12a89ecd91f5e742165893f86e3ab98d21f8ea548658eb9eef5f40"
"checksum byte-tools 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "980479e6fde23246dfb54d47580d66b4e99202e7579c5eaa9fe10ecb5ebd2182"
"checksum byteorder 1.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "73b5bdfe7ee3ad0b99c9801d58807a9dbc9e09196365b0203853b99889ab3c87"
"checksum chacha20-poly1305-aead 0.1.2 (git+https://github.com/gtank/chacha20-poly1305-aead?rev=aefc71f95e8bc43f2070e3c5b08130d9c86bbf4f)" = "<none>"
"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 digest 0.7.2 (registry+https://github.com/rust-lang/crates.io-index)" = "00a49051fef47a72c9623101b19bd71924a45cca838826caae3eaa4d00772603"

1
sapling-crypto/src/primitives/mod.rs

@ -162,6 +162,7 @@ impl<E: JubjubEngine> PaymentAddress<E> {
}
}
#[derive(Clone)]
pub struct Note<E: JubjubEngine> {
/// The value of the note
pub value: u64,

4
zcash_primitives/Cargo.toml

@ -18,3 +18,7 @@ sha2 = "0.8"
[dependencies.blake2-rfc]
git = "https://github.com/gtank/blake2-rfc"
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
[dependencies.chacha20-poly1305-aead]
git = "https://github.com/gtank/chacha20-poly1305-aead"
rev = "aefc71f95e8bc43f2070e3c5b08130d9c86bbf4f"

2
zcash_primitives/src/lib.rs

@ -3,6 +3,7 @@ extern crate lazy_static;
extern crate blake2_rfc;
extern crate byteorder;
extern crate chacha20_poly1305_aead;
extern crate ff;
extern crate hex;
extern crate pairing;
@ -14,6 +15,7 @@ use sapling_crypto::jubjub::JubjubBls12;
pub mod block;
pub mod keys;
pub mod note_encryption;
pub mod sapling;
mod serialize;
pub mod transaction;

154
zcash_primitives/src/note_encryption.rs

@ -0,0 +1,154 @@
use blake2_rfc::blake2b::{Blake2b, Blake2bResult};
use byteorder::{LittleEndian, WriteBytesExt};
use chacha20_poly1305_aead;
use ff::{PrimeField, PrimeFieldRepr};
use pairing::bls12_381::{Bls12, Fr};
use rand::{OsRng, Rng};
use sapling_crypto::{
jubjub::{edwards, fs::Fs, PrimeOrder, ToUniform, Unknown},
primitives::{Note, PaymentAddress},
};
use crate::{keys::OutgoingViewingKey, JUBJUB};
pub const KDF_SAPLING_PERSONALIZATION: &'static [u8; 16] = b"Zcash_SaplingKDF";
pub const PRF_OCK_PERSONALIZATION: &'static [u8; 16] = b"Zcash_Derive_ock";
pub struct Memo([u8; 512]);
impl Default for Memo {
fn default() -> Self {
// Empty memo field indication per ZIP 302
let mut memo = [0u8; 512];
memo[0] = 0xF6;
Memo(memo)
}
}
fn generate_esk() -> Fs {
// 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
Fs::to_uniform(&buffer[..])
}
fn sapling_ka_agree(esk: &Fs, pk_d: &edwards::Point<Bls12, PrimeOrder>) -> Vec<u8> {
let ka = pk_d
.mul(esk.into_repr(), &JUBJUB)
.double(&JUBJUB)
.double(&JUBJUB)
.double(&JUBJUB);
let mut result = Vec::with_capacity(32);
ka.write(&mut result).expect("length is not 32 bytes");
result
}
fn kdf_sapling(dhsecret: &[u8], epk: &edwards::Point<Bls12, PrimeOrder>) -> Blake2bResult {
let mut input = [0u8; 64];
input[0..32].copy_from_slice(&dhsecret);
epk.write(&mut input[32..64]).unwrap();
let mut h = Blake2b::with_params(32, &[], &[], KDF_SAPLING_PERSONALIZATION);
h.update(&input);
h.finalize()
}
pub struct SaplingNoteEncryption {
epk: edwards::Point<Bls12, PrimeOrder>,
esk: Fs,
note: Note<Bls12>,
to: PaymentAddress<Bls12>,
memo: Memo,
ovk: OutgoingViewingKey,
}
impl SaplingNoteEncryption {
pub fn new(
ovk: OutgoingViewingKey,
note: Note<Bls12>,
to: PaymentAddress<Bls12>,
memo: Memo,
) -> SaplingNoteEncryption {
let esk = generate_esk();
let epk = note.g_d.mul(esk, &JUBJUB);
SaplingNoteEncryption {
epk,
esk,
note,
to,
memo,
ovk,
}
}
pub fn esk(&self) -> &Fs {
&self.esk
}
pub fn epk(&self) -> &edwards::Point<Bls12, PrimeOrder> {
&self.epk
}
pub fn encrypt_note_plaintext(&self) -> [u8; 580] {
let shared_secret = sapling_ka_agree(&self.esk, &self.to.pk_d);
let key = kdf_sapling(&shared_secret, &self.epk);
let nonce = [0u8; 12];
let mut input = Vec::with_capacity(564);
input.push(1);
input.extend_from_slice(&self.to.diversifier.0);
(&mut input)
.write_u64::<LittleEndian>(self.note.value)
.unwrap();
self.note.r.into_repr().write_le(&mut input).unwrap();
input.extend_from_slice(&self.memo.0);
let mut ciphertext = Vec::with_capacity(564);
let tag =
chacha20_poly1305_aead::encrypt(&key.as_bytes(), &nonce, &[], &input, &mut ciphertext)
.unwrap();
let mut output = [0u8; 580];
output[0..564].copy_from_slice(&ciphertext);
output[564..580].copy_from_slice(&tag);
output
}
pub fn encrypt_outgoing_plaintext(
&self,
cv: &edwards::Point<Bls12, Unknown>,
cmu: &Fr,
) -> [u8; 80] {
let mut ock_input = [0u8; 128];
ock_input[0..32].copy_from_slice(&self.ovk.0);
cv.write(&mut ock_input[32..64]).unwrap();
cmu.into_repr().write_le(&mut ock_input[64..96]).unwrap();
self.epk.write(&mut ock_input[96..128]).unwrap();
let mut h = Blake2b::with_params(32, &[], &[], PRF_OCK_PERSONALIZATION);
h.update(&ock_input);
let key = h.finalize();
let mut input = [0u8; 64];
self.note.pk_d.write(&mut input[0..32]).unwrap();
self.esk.into_repr().write_le(&mut input[32..64]).unwrap();
let mut buffer = Vec::with_capacity(64);
let nonce = [0u8; 12];
let tag = chacha20_poly1305_aead::encrypt(key.as_bytes(), &nonce, &[], &input, &mut buffer)
.unwrap();
let mut output = [0u8; 80];
output[0..64].copy_from_slice(&buffer);
output[64..80].copy_from_slice(&tag[..]);
output
}
}
Loading…
Cancel
Save