diff --git a/crates/bitwarden-crypto/benches/ciphers.rs b/crates/bitwarden-crypto/benches/ciphers.rs index 48cdaa8a..a770f625 100644 --- a/crates/bitwarden-crypto/benches/ciphers.rs +++ b/crates/bitwarden-crypto/benches/ciphers.rs @@ -1,4 +1,6 @@ -use bitwarden_crypto::chacha20::{decrypt_xchacha20_poly1305_blake3_ctx, encrypt_xchacha20_poly1305_blake3_ctx}; +use bitwarden_crypto::chacha20::{ + decrypt_xchacha20_poly1305_blake3_ctx, encrypt_xchacha20_poly1305_blake3_ctx, +}; use criterion::{black_box, criterion_group, criterion_main, Criterion}; pub fn criterion_benchmark(c: &mut Criterion) { @@ -9,11 +11,19 @@ pub fn criterion_benchmark(c: &mut Criterion) { let authenticated_data = authenticated_data.as_slice(); c.bench_function("encrypt_xchacha20_poly1305_blake3_ctx", |b| { - b.iter(|| encrypt_xchacha20_poly1305_blake3_ctx(black_box(&key), black_box(plaintext_secret_data), black_box(authenticated_data))) + b.iter(|| { + encrypt_xchacha20_poly1305_blake3_ctx( + black_box(&key), + black_box(plaintext_secret_data), + black_box(authenticated_data), + ) + }) }); - let encrypted = encrypt_xchacha20_poly1305_blake3_ctx(&key, plaintext_secret_data, authenticated_data).unwrap(); - + let encrypted = + encrypt_xchacha20_poly1305_blake3_ctx(&key, plaintext_secret_data, authenticated_data) + .unwrap(); + c.bench_function("encrypt_xchacha20_poly1305_blake3_ctx", |b| { b.iter(|| decrypt_xchacha20_poly1305_blake3_ctx(black_box(&key), black_box(&encrypted))) }); diff --git a/crates/bitwarden-crypto/src/chacha20.rs b/crates/bitwarden-crypto/src/chacha20.rs index 53faea2a..cf1584e1 100644 --- a/crates/bitwarden-crypto/src/chacha20.rs +++ b/crates/bitwarden-crypto/src/chacha20.rs @@ -1,23 +1,21 @@ -/** -* Note: -* XChaCha20Poly1305-CTX encrypts data, and authenticates associated data using XChaCha20Poly1305 -* Specifically, this uses the CTX construction, proposed here: https://par.nsf.gov/servlets/purl/10391723 -* using blake3 as the cryptographic hash function. This provides not only key-commitment, but full-commitment. -* In total, this scheme prevents attacks such as invisible salamanders. -*/ - -use crate::CryptoError; -use chacha20poly1305::AeadCore; -use chacha20poly1305::AeadInPlace; -use chacha20poly1305::KeyInit; -use chacha20poly1305::XChaCha20Poly1305; -use chacha20::XChaCha20; +use chacha20::{ + cipher::{KeyIvInit, StreamCipher}, + XChaCha20, +}; +use chacha20poly1305::{AeadCore, AeadInPlace, KeyInit, XChaCha20Poly1305}; use generic_array::GenericArray; -use poly1305::Poly1305; +use poly1305::{universal_hash::UniversalHash, Poly1305}; use subtle::ConstantTimeEq; -use chacha20::cipher::{KeyIvInit, StreamCipher}; -use poly1305::universal_hash::UniversalHash; +/** + * Note: + * XChaCha20Poly1305-CTX encrypts data, and authenticates associated data using + * XChaCha20Poly1305 Specifically, this uses the CTX construction, proposed here: https://par.nsf.gov/servlets/purl/10391723 + * using blake3 as the cryptographic hash function. This provides not only key-commitment, + * but full-commitment. In total, this scheme prevents attacks such as invisible + * salamanders. + */ +use crate::CryptoError; pub struct XChaCha20Poly1305CTXCiphetext { nonce: [u8; 24], @@ -32,7 +30,12 @@ pub fn encrypt_xchacha20_poly1305_blake3_ctx( plaintext_secret_data: &[u8], authenticated_data: &[u8], ) -> Result { - encrypt_xchacha20_poly1305_blake3_ctx_internal(rand::thread_rng(), key, plaintext_secret_data, authenticated_data) + encrypt_xchacha20_poly1305_blake3_ctx_internal( + rand::thread_rng(), + key, + plaintext_secret_data, + authenticated_data, + ) } #[allow(dead_code)] @@ -45,11 +48,21 @@ fn encrypt_xchacha20_poly1305_blake3_ctx_internal( let mut buffer = Vec::from(plaintext_secret_data); let cipher = XChaCha20Poly1305::new(&GenericArray::from_slice(key)); let nonce = XChaCha20Poly1305::generate_nonce(rng); - - let poly1305_tag = cipher.encrypt_in_place_detached(&nonce, associated_data, &mut buffer).map_err(|_| CryptoError::InvalidKey)?; - + + let poly1305_tag = cipher + .encrypt_in_place_detached(&nonce, associated_data, &mut buffer) + .map_err(|_| CryptoError::InvalidKey)?; + // T* = H(K, N, A, T ) - let ctx_tag = blake3::hash(&[key, nonce.as_slice(), associated_data, poly1305_tag.as_slice()].concat()); + let ctx_tag = blake3::hash( + &[ + key, + nonce.as_slice(), + associated_data, + poly1305_tag.as_slice(), + ] + .concat(), + ); let ctx_tag = ctx_tag.as_bytes(); Ok(XChaCha20Poly1305CTXCiphetext { @@ -69,17 +82,28 @@ pub fn decrypt_xchacha20_poly1305_blake3_ctx( let associated_data = ctx.authenticated_data.as_slice(); // First, get the original polynomial tag, since this is required to calculate the ctx_tag - let poly1305_tag = get_tag_expected_for_xchacha20_poly1305_ctx(key, &ctx.nonce, associated_data, &buffer); - - let ctx_tag = blake3::hash(&[key, ctx.nonce.as_slice(), associated_data, poly1305_tag.as_slice()].concat()); + let poly1305_tag = + get_tag_expected_for_xchacha20_poly1305_ctx(key, &ctx.nonce, associated_data, &buffer); + + let ctx_tag = blake3::hash( + &[ + key, + ctx.nonce.as_slice(), + associated_data, + poly1305_tag.as_slice(), + ] + .concat(), + ); let ctx_tag = ctx_tag.as_bytes(); if ctx_tag.ct_eq(&ctx.tag).into() { - // At this point the commitment is verified, so we can decrypt the data using regular XChaCha20Poly1305 + // At this point the commitment is verified, so we can decrypt the data using regular + // XChaCha20Poly1305 let cipher = XChaCha20Poly1305::new(&GenericArray::from_slice(key)); let mut buffer = ctx.ciphertext.clone(); let nonce_array = GenericArray::from_slice(&ctx.nonce); - cipher.decrypt_in_place_detached(nonce_array, associated_data, &mut buffer, &poly1305_tag) + cipher + .decrypt_in_place_detached(nonce_array, associated_data, &mut buffer, &poly1305_tag) .map_err(|_| CryptoError::InvalidKey)?; return Ok(buffer); } @@ -87,8 +111,16 @@ pub fn decrypt_xchacha20_poly1305_blake3_ctx( Err(CryptoError::InvalidKey) } -fn get_tag_expected_for_xchacha20_poly1305_ctx(key: &[u8; 32], nonce: &[u8; 24], associated_data: &[u8], buffer: &[u8]) -> chacha20poly1305::Tag { - let mut chacha20 = XChaCha20::new(GenericArray::from_slice(key), GenericArray::from_slice(nonce)); +fn get_tag_expected_for_xchacha20_poly1305_ctx( + key: &[u8; 32], + nonce: &[u8; 24], + associated_data: &[u8], + buffer: &[u8], +) -> chacha20poly1305::Tag { + let mut chacha20 = XChaCha20::new( + GenericArray::from_slice(key), + GenericArray::from_slice(nonce), + ); let mut mac_key = poly1305::Key::default(); chacha20.apply_keystream(&mut *mac_key); let mut mac = Poly1305::new(GenericArray::from_slice(&*mac_key)); @@ -117,8 +149,10 @@ mod tests { let plaintext_secret_data = b"My secret data"; let authenticated_data = b"My authenticated data"; - let encrypted = encrypt_xchacha20_poly1305_blake3_ctx(&key, plaintext_secret_data, authenticated_data).unwrap(); + let encrypted = + encrypt_xchacha20_poly1305_blake3_ctx(&key, plaintext_secret_data, authenticated_data) + .unwrap(); let decrypted = decrypt_xchacha20_poly1305_blake3_ctx(&key, &encrypted).unwrap(); assert_eq!(plaintext_secret_data, decrypted.as_slice()); } -} \ No newline at end of file +}