Implement Sapling note encryption
This commit is contained in:
parent
9b455a12cc
commit
65bbe7daed
|
@ -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"
|
||||
|
|
|
@ -162,6 +162,7 @@ impl<E: JubjubEngine> PaymentAddress<E> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Note<E: JubjubEngine> {
|
||||
/// The value of the note
|
||||
pub value: u64,
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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…
Reference in New Issue