From 6dcb4040afc5fb04258e43f5fdd490b739ed1274 Mon Sep 17 00:00:00 2001 From: Jack Grigg Date: Thu, 11 Apr 2019 06:33:41 -0700 Subject: [PATCH] Switch to crypto_api_chachapoly crate This crate exposes both the ChaCha20Poly1305 IETF construction, and the underlying ChaCha20 IETF primitive, removing the need for depending on our own fork of the previous chacha20-poly1305-aead crate. --- Cargo.lock | 26 +++-- zcash_primitives/Cargo.toml | 5 +- zcash_primitives/src/lib.rs | 2 +- zcash_primitives/src/note_encryption.rs | 146 +++++++++++++----------- 4 files changed, 98 insertions(+), 81 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 1597920..ea0af88 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -114,14 +114,6 @@ 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" @@ -132,6 +124,19 @@ name = "crossbeam" version = "0.3.2" 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]] name = "digest" version = "0.7.2" @@ -508,7 +513,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)", + "crypto_api_chachapoly 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)", "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)", @@ -563,9 +568,10 @@ 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)" = "" "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 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.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" diff --git a/zcash_primitives/Cargo.toml b/zcash_primitives/Cargo.toml index aabac79..644c7eb 100644 --- a/zcash_primitives/Cargo.toml +++ b/zcash_primitives/Cargo.toml @@ -7,6 +7,7 @@ authors = [ [dependencies] byteorder = "1" +crypto_api_chachapoly = "0.1" ff = { path = "../ff" } hex = "0.3" lazy_static = "1" @@ -18,7 +19,3 @@ 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" diff --git a/zcash_primitives/src/lib.rs b/zcash_primitives/src/lib.rs index 8da6c23..38ac7a4 100644 --- a/zcash_primitives/src/lib.rs +++ b/zcash_primitives/src/lib.rs @@ -3,7 +3,7 @@ extern crate lazy_static; extern crate blake2_rfc; extern crate byteorder; -extern crate chacha20_poly1305_aead; +extern crate crypto_api_chachapoly; extern crate ff; extern crate hex; extern crate pairing; diff --git a/zcash_primitives/src/note_encryption.rs b/zcash_primitives/src/note_encryption.rs index 3fb2c65..b1c5a36 100644 --- a/zcash_primitives/src/note_encryption.rs +++ b/zcash_primitives/src/note_encryption.rs @@ -1,6 +1,6 @@ use blake2_rfc::blake2b::{Blake2b, Blake2bResult}; use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; -use chacha20_poly1305_aead::{self, as_bytes::AsBytes, chacha20::ChaCha20}; +use crypto_api_chachapoly::{ChaCha20Ietf, ChachaPolyIetf}; use ff::{PrimeField, PrimeFieldRepr}; use pairing::bls12_381::{Bls12, Fr}; use rand::{OsRng, Rng}; @@ -240,19 +240,14 @@ impl SaplingNoteEncryption { input.extend_from_slice(&self.memo.0); assert_eq!(input.len(), NOTE_PLAINTEXT_SIZE); - let mut ciphertext = Vec::with_capacity(NOTE_PLAINTEXT_SIZE); - let tag = chacha20_poly1305_aead::encrypt( - &key.as_bytes(), - &[0u8; 12], - &[], - &input, - &mut ciphertext, - ) - .unwrap(); - let mut output = [0u8; ENC_CIPHERTEXT_SIZE]; - output[0..NOTE_PLAINTEXT_SIZE].copy_from_slice(&ciphertext); - output[NOTE_PLAINTEXT_SIZE..ENC_CIPHERTEXT_SIZE].copy_from_slice(&tag); + assert_eq!( + ChachaPolyIetf::aead_cipher() + .seal_to(&mut output, &input, &[], &key.as_bytes(), &[0u8; 12]) + .unwrap(), + ENC_CIPHERTEXT_SIZE + ); + output } @@ -263,23 +258,26 @@ impl SaplingNoteEncryption { ) -> [u8; OUT_CIPHERTEXT_SIZE] { let key = prf_ock(&self.ovk, &cv, &cmu, &self.epk); - let mut input = [0u8; OUT_PLAINTEXT_SIZE]; - self.note.pk_d.write(&mut input[0..32]).unwrap(); + let mut buf = [0u8; OUT_CIPHERTEXT_SIZE]; + self.note.pk_d.write(&mut buf[0..32]).unwrap(); self.esk .into_repr() - .write_le(&mut input[32..OUT_PLAINTEXT_SIZE]) + .write_le(&mut buf[32..OUT_PLAINTEXT_SIZE]) .unwrap(); - let mut buffer = Vec::with_capacity(OUT_PLAINTEXT_SIZE); - let tag = - chacha20_poly1305_aead::encrypt(key.as_bytes(), &[0u8; 12], &[], &input, &mut buffer) - .unwrap(); - - let mut output = [0u8; OUT_CIPHERTEXT_SIZE]; - output[0..OUT_PLAINTEXT_SIZE].copy_from_slice(&buffer); - output[OUT_PLAINTEXT_SIZE..OUT_CIPHERTEXT_SIZE].copy_from_slice(&tag[..]); - - output + assert_eq!( + ChachaPolyIetf::aead_cipher() + .seal( + &mut buf, + OUT_PLAINTEXT_SIZE, + &[], + key.as_bytes(), + &[0u8; 12] + ) + .unwrap(), + OUT_CIPHERTEXT_SIZE + ); + buf } } @@ -329,16 +327,19 @@ pub fn try_sapling_note_decryption( let shared_secret = sapling_ka_agree(ivk, epk); let key = kdf_sapling(&shared_secret, &epk); - let mut plaintext = Vec::with_capacity(NOTE_PLAINTEXT_SIZE); - chacha20_poly1305_aead::decrypt( - key.as_bytes(), - &[0u8; 12], - &[], - &enc_ciphertext[..NOTE_PLAINTEXT_SIZE], - &enc_ciphertext[NOTE_PLAINTEXT_SIZE..], - &mut plaintext, - ) - .ok()?; + let mut plaintext = vec![0; ENC_CIPHERTEXT_SIZE]; + assert_eq!( + ChachaPolyIetf::aead_cipher() + .open_to( + &mut plaintext, + &enc_ciphertext, + &[], + key.as_bytes(), + &[0u8; 12] + ) + .ok()?, + NOTE_PLAINTEXT_SIZE + ); let (note, to) = parse_note_plaintext_minus_memo(ivk, cmu, &plaintext)?; @@ -364,18 +365,24 @@ pub fn try_sapling_compact_note_decryption( let shared_secret = sapling_ka_agree(ivk, epk); let key = kdf_sapling(&shared_secret, &epk); - let mut chacha20 = ChaCha20::new(key.as_bytes(), &[0u8; 12]); - // Skip over Poly1305 keying output - chacha20.next(); - - let mut plaintext = Vec::with_capacity(COMPACT_NOTE_SIZE); + // Prefix plaintext with 64 zero-bytes to skip over Poly1305 keying output + const CHACHA20_BLOCK_SIZE: usize = 64; + let mut plaintext = Vec::with_capacity(CHACHA20_BLOCK_SIZE + COMPACT_NOTE_SIZE); + plaintext.extend_from_slice(&[0; CHACHA20_BLOCK_SIZE]); plaintext.extend_from_slice(&enc_ciphertext[0..COMPACT_NOTE_SIZE]); - let keystream = chacha20.next(); - for i in 0..COMPACT_NOTE_SIZE { - plaintext[i] ^= keystream.as_bytes()[i]; - } + assert_eq!( + ChaCha20Ietf::cipher() + .decrypt( + &mut plaintext, + CHACHA20_BLOCK_SIZE + COMPACT_NOTE_SIZE, + key.as_bytes(), + &[0u8; 12], + ) + .ok()?, + CHACHA20_BLOCK_SIZE + COMPACT_NOTE_SIZE + ); - parse_note_plaintext_minus_memo(ivk, cmu, &plaintext) + parse_note_plaintext_minus_memo(ivk, cmu, &plaintext[CHACHA20_BLOCK_SIZE..]) } /// Attempts to decrypt and validate the given `enc_ciphertext` using the given `ovk`. @@ -396,16 +403,13 @@ pub fn try_sapling_output_recovery( let ock = prf_ock(&ovk, &cv, &cmu, &epk); - let mut op = Vec::with_capacity(OUT_PLAINTEXT_SIZE); - chacha20_poly1305_aead::decrypt( - ock.as_bytes(), - &[0u8; 12], - &[], - &out_ciphertext[..OUT_PLAINTEXT_SIZE], - &out_ciphertext[OUT_PLAINTEXT_SIZE..], - &mut op, - ) - .ok()?; + let mut op = vec![0; OUT_CIPHERTEXT_SIZE]; + assert_eq!( + ChachaPolyIetf::aead_cipher() + .open_to(&mut op, &out_ciphertext, &[], ock.as_bytes(), &[0u8; 12]) + .ok()?, + OUT_PLAINTEXT_SIZE + ); let pk_d = edwards::Point::::read(&op[0..32], &JUBJUB) .ok()? @@ -418,16 +422,19 @@ pub fn try_sapling_output_recovery( let shared_secret = sapling_ka_agree(&esk, &pk_d); let key = kdf_sapling(&shared_secret, &epk); - let mut plaintext = Vec::with_capacity(NOTE_PLAINTEXT_SIZE); - chacha20_poly1305_aead::decrypt( - key.as_bytes(), - &[0u8; 12], - &[], - &enc_ciphertext[..NOTE_PLAINTEXT_SIZE], - &enc_ciphertext[NOTE_PLAINTEXT_SIZE..], - &mut plaintext, - ) - .ok()?; + let mut plaintext = vec![0; ENC_CIPHERTEXT_SIZE]; + assert_eq!( + ChachaPolyIetf::aead_cipher() + .open_to( + &mut plaintext, + &enc_ciphertext, + &[], + key.as_bytes(), + &[0u8; 12] + ) + .ok()?, + NOTE_PLAINTEXT_SIZE + ); let mut d = [0u8; 11]; d.copy_from_slice(&plaintext[1..12]); @@ -631,6 +638,13 @@ mod tests { let out_ciphertext = ne.encrypt_outgoing_plaintext(&cv, &cmu); assert!(try_sapling_note_decryption(&ivk, epk, &cmu, &enc_ciphertext).is_some()); + assert!(try_sapling_compact_note_decryption( + &ivk, + epk, + &cmu, + &enc_ciphertext[..COMPACT_NOTE_SIZE] + ) + .is_some()); assert!(try_sapling_output_recovery( &ovk, &cv,