mirror of
https://github.com/Qortal/pirate-librustzcash.git
synced 2025-07-30 20:11:23 +00:00
Implement Sapling note encryption
This commit is contained in:
committed by
Jack Grigg
parent
9b455a12cc
commit
65bbe7daed
10
Cargo.lock
generated
10
Cargo.lock
generated
@@ -114,6 +114,14 @@ name = "byteorder"
|
|||||||
version = "1.2.2"
|
version = "1.2.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
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]]
|
[[package]]
|
||||||
name = "constant_time_eq"
|
name = "constant_time_eq"
|
||||||
version = "0.1.3"
|
version = "0.1.3"
|
||||||
@@ -500,6 +508,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)",
|
||||||
|
"chacha20-poly1305-aead 0.1.2 (git+https://github.com/gtank/chacha20-poly1305-aead?rev=aefc71f95e8bc43f2070e3c5b08130d9c86bbf4f)",
|
||||||
"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)",
|
||||||
@@ -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.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 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 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 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 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"
|
||||||
|
@@ -162,6 +162,7 @@ impl<E: JubjubEngine> PaymentAddress<E> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
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,
|
||||||
|
@@ -18,3 +18,7 @@ sha2 = "0.8"
|
|||||||
[dependencies.blake2-rfc]
|
[dependencies.blake2-rfc]
|
||||||
git = "https://github.com/gtank/blake2-rfc"
|
git = "https://github.com/gtank/blake2-rfc"
|
||||||
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
|
rev = "7a5b5fc99ae483a0043db7547fb79a6fa44b88a9"
|
||||||
|
|
||||||
|
[dependencies.chacha20-poly1305-aead]
|
||||||
|
git = "https://github.com/gtank/chacha20-poly1305-aead"
|
||||||
|
rev = "aefc71f95e8bc43f2070e3c5b08130d9c86bbf4f"
|
||||||
|
@@ -3,6 +3,7 @@ extern crate lazy_static;
|
|||||||
|
|
||||||
extern crate blake2_rfc;
|
extern crate blake2_rfc;
|
||||||
extern crate byteorder;
|
extern crate byteorder;
|
||||||
|
extern crate chacha20_poly1305_aead;
|
||||||
extern crate ff;
|
extern crate ff;
|
||||||
extern crate hex;
|
extern crate hex;
|
||||||
extern crate pairing;
|
extern crate pairing;
|
||||||
@@ -14,6 +15,7 @@ use sapling_crypto::jubjub::JubjubBls12;
|
|||||||
|
|
||||||
pub mod block;
|
pub mod block;
|
||||||
pub mod keys;
|
pub mod keys;
|
||||||
|
pub mod note_encryption;
|
||||||
pub mod sapling;
|
pub mod sapling;
|
||||||
mod serialize;
|
mod serialize;
|
||||||
pub mod transaction;
|
pub mod transaction;
|
||||||
|
154
zcash_primitives/src/note_encryption.rs
Normal file
154
zcash_primitives/src/note_encryption.rs
Normal file
@@ -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
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user