From eeef9f0b9ee3f9d043a23db2c88a82b332e931ae Mon Sep 17 00:00:00 2001 From: Craig Colegrove <34786857+giarc3@users.noreply.github.com> Date: Thu, 25 Apr 2024 09:36:34 -0700 Subject: [PATCH] Change functions to take mutex (#15) --- Cargo.toml | 2 +- flake.lock | 30 +++++++++++++++--------------- rust-toolchain.toml | 2 +- src/aes.rs | 40 +++++++++++++++++++++++++++++----------- src/lib.rs | 30 +++++++++++++++++++++++++++++- src/v4/aes.rs | 27 +++++++++++++++++---------- src/v5/mod.rs | 8 +++++--- 7 files changed, 97 insertions(+), 42 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 004e6ad..8d58861 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ironcore-documents" description = "A library for working with IronCore Labs documents and header formats." -version = "0.1.1-pre" +version = "0.2.0-pre" edition = "2021" license = "AGPL-3.0-only" documentation = "https://docs.rs/ironcore-documents" diff --git a/flake.lock b/flake.lock index 260077f..2433724 100644 --- a/flake.lock +++ b/flake.lock @@ -5,11 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1710146030, + "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a", "type": "github" }, "original": { @@ -23,11 +23,11 @@ "systems": "systems_2" }, "locked": { - "lastModified": 1681202837, - "narHash": "sha256-H+Rh19JDwRtpVPAWp64F+rlEtxUWBAQW28eAi3SRSzg=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "cfacdce06f30d2b68473a46042957675eebb3401", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -38,11 +38,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1703961334, - "narHash": "sha256-M1mV/Cq+pgjk0rt6VxoyyD+O8cOUiai8t9Q6Yyq4noY=", + "lastModified": 1713895582, + "narHash": "sha256-cfh1hi+6muQMbi9acOlju3V1gl8BEaZBXBR9jQfQi4U=", "owner": "nixos", "repo": "nixpkgs", - "rev": "b0d36bd0a420ecee3bc916c91886caca87c894e9", + "rev": "572af610f6151fd41c212f897c71f7056e3fb518", "type": "github" }, "original": { @@ -54,11 +54,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1681358109, - "narHash": "sha256-eKyxW4OohHQx9Urxi7TQlFBTDWII+F+x2hklDOQPB50=", + "lastModified": 1706487304, + "narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "96ba1c52e54e74c3197f4d43026b3f3d92e83ff9", + "rev": "90f456026d284c22b3e3497be980b2e47d0b28ac", "type": "github" }, "original": { @@ -81,11 +81,11 @@ "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1704075545, - "narHash": "sha256-L3zgOuVKhPjKsVLc3yTm2YJ6+BATyZBury7wnhyc8QU=", + "lastModified": 1713924823, + "narHash": "sha256-kOeyS3GFwgnKvzuBMmFqEAX0xwZ7Nj4/5tXuvpZ0d4U=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a0df72e106322b67e9c6e591fe870380bd0da0d5", + "rev": "8a2edac3ae926a2a6ce60f4595dcc4540fc8cad4", "type": "github" }, "original": { diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0262bdb..98ae255 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,4 +1,4 @@ [toolchain] profile = "default" -channel = "1.75.0" +channel = "1.77.2" components = ["rust-src", "rust-analyzer"] diff --git a/src/aes.rs b/src/aes.rs index bcc3c01..55ad895 100644 --- a/src/aes.rs +++ b/src/aes.rs @@ -1,8 +1,13 @@ // This module is dedicated with things to do with aes encryption/decryption. use super::Error; +use crate::take_lock; use aes_gcm::{aead::Aead, aead::Payload, AeadCore, Aes256Gcm, KeyInit, Nonce}; use bytes::Bytes; use rand::{CryptoRng, RngCore}; +use std::{ + ops::DerefMut, + sync::{Arc, Mutex}, +}; type Result = core::result::Result; pub(crate) const IV_LEN: usize = 12; @@ -76,21 +81,26 @@ pub fn decrypt_document_with_attached_iv( /// Encrypt a document and put the iv on the front of it. pub fn encrypt_document_and_attach_iv( - rng: &mut R, + rng: Arc>, key: EncryptionKey, document: PlaintextDocument, ) -> Result { - let (iv, enc_data) = aes_encrypt(key, &document.0, &[], rng)?; - Ok(IvAndCiphertext([&iv[..], &enc_data.0[..]].concat().into())) + let (iv, mut enc_data) = aes_encrypt(key, &document.0, &[], rng)?; + let mut iv_vec = iv.to_vec(); + iv_vec.append(&mut enc_data.0); + Ok(IvAndCiphertext(iv_vec.into())) } pub(crate) fn aes_encrypt( key: EncryptionKey, plaintext: &[u8], associated_data: &[u8], - rng: &mut R, + rng: Arc>, ) -> Result<([u8; 12], EncryptedDocument)> { - let iv = Aes256Gcm::generate_nonce(rng); + let iv = { + let mut guard = take_lock(&rng); + Aes256Gcm::generate_nonce(&mut *guard.deref_mut()) + }; aes_encrypt_with_iv(key, plaintext, iv.into(), associated_data) } @@ -149,22 +159,30 @@ mod test { "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" )); let plaintext = hex!("112233445566778899aabbccddee"); - let (iv, encrypt_result) = - aes_encrypt(key, &plaintext, &[], &mut rand::thread_rng()).unwrap(); + let (iv, encrypt_result) = aes_encrypt( + key, + &plaintext, + &[], + Arc::new(Mutex::new(rand::thread_rng())), + ) + .unwrap(); let decrypt_result = aes_decrypt_core(&key, iv, &encrypt_result.0, &[]).unwrap(); assert_eq!(decrypt_result, plaintext); } #[test] fn encrypt_decrypt_attached_roundtrip() { - let mut rng = ChaCha20Rng::seed_from_u64(13u64); + let rng = ChaCha20Rng::seed_from_u64(13u64); let key = EncryptionKey(hex!( "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" )); let document = vec![1u8]; - let encrypted = - encrypt_document_and_attach_iv(&mut rng, key, PlaintextDocument(document.clone())) - .unwrap(); + let encrypted = encrypt_document_and_attach_iv( + Arc::new(Mutex::new(rng)), + key, + PlaintextDocument(document.clone()), + ) + .unwrap(); let result = decrypt_document_with_attached_iv(&key, &encrypted).unwrap(); assert_eq!(result.0, document); } diff --git a/src/lib.rs b/src/lib.rs index 43906a2..72ece59 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,7 +4,10 @@ pub mod v3; pub mod v4; pub mod v5; -use std::fmt::{Display, Formatter, Result as DisplayResult}; +use std::{ + fmt::{Display, Formatter, Result as DisplayResult}, + sync::{Mutex, MutexGuard}, +}; use thiserror::Error; use v5::key_id_header::KEY_ID_HEADER_LEN; @@ -65,3 +68,28 @@ impl Display for Error { } } } + +/// Acquire mutex in a blocking fashion. If the Mutex is or becomes poisoned, write out an error +/// message and panic. +/// +/// The lock is released when the returned MutexGuard falls out of scope. +/// +/// # Usage: +/// single statement (mut) +/// `let result = take_lock(&t).deref_mut().call_method_on_t();` +/// +/// multi-statement (mut) +/// ```ignore +/// let t = T {}; +/// let result = { +/// let g = &mut *take_lock(&t); +/// g.call_method_on_t() +/// }; // lock released here +/// ``` +/// +pub fn take_lock(m: &Mutex) -> MutexGuard { + m.lock().unwrap_or_else(|e| { + let error = format!("Error when acquiring lock: {e}"); + panic!("{error}"); + }) +} diff --git a/src/v4/aes.rs b/src/v4/aes.rs index 633c928..f8fb8ff 100644 --- a/src/v4/aes.rs +++ b/src/v4/aes.rs @@ -9,11 +9,15 @@ use crate::{ }, V4DocumentHeader, }, - signing, Error, + signing, take_lock, Error, }; use bytes::Bytes; use protobuf::Message; use rand::{CryptoRng, RngCore}; +use std::{ + ops::DerefMut, + sync::{Arc, Mutex}, +}; type Result = core::result::Result; @@ -21,7 +25,7 @@ type Result = core::result::Result; /// Encrypt the dek using the kek to make an aes edek. The provided id will be put into the Aes256GcmEncryptedDek. /// Returns the dek and Aes256GcmEncryptedDek. fn generate_aes_edek( - rng: &mut R, + rng: Arc>, kek: EncryptionKey, maybe_dek: Option, id: &str, @@ -31,7 +35,7 @@ fn generate_aes_edek( )> { let dek = maybe_dek.unwrap_or_else(|| { let mut buffer = [0u8; 32]; - rng.fill_bytes(&mut buffer); + take_lock(&rng).deref_mut().fill_bytes(&mut buffer); EncryptionKey(buffer) }); let (iv, edek) = aes_encrypt(kek, &dek.0, &[], rng)?; @@ -82,7 +86,7 @@ pub fn create_signed_proto( /// The edek will be placed into a V4DocumentHeader and the signature will be computed. /// The aes dek is the key used to compute the signature. pub fn generate_aes_edek_and_sign( - rng: &mut R, + rng: Arc>, kek: EncryptionKey, maybe_dek: Option, id: &str, @@ -160,24 +164,26 @@ mod test { use rand_chacha::ChaCha20Rng; #[test] fn generate_aes_edek_decrypts() { - let mut rng = ChaCha20Rng::seed_from_u64(203u64); + let rng = ChaCha20Rng::seed_from_u64(203u64); let kek = EncryptionKey(hex!( "aabbccddeefaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" )); let id = "hello"; - let (aes_dek, aes_edek) = generate_aes_edek(&mut rng, kek, None, id).unwrap(); + let (aes_dek, aes_edek) = + generate_aes_edek(Arc::new(Mutex::new(rng)), kek, None, id).unwrap(); let result = decrypt_aes_edek(&kek, &aes_edek).unwrap(); assert_eq!(result, aes_dek); } #[test] fn signed_aes_edek_verifies_and_decrypts() { - let mut rng = ChaCha20Rng::seed_from_u64(203u64); + let rng = ChaCha20Rng::seed_from_u64(203u64); let kek = EncryptionKey(hex!( "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" )); let id = "hello"; - let (aes_dek, v4_document) = generate_aes_edek_and_sign(&mut rng, kek, None, id).unwrap(); + let (aes_dek, v4_document) = + generate_aes_edek_and_sign(Arc::new(Mutex::new(rng)), kek, None, id).unwrap(); let aes_edek = v4_document.signed_payload.0.clone().unwrap().edeks[0].take_aes_256_gcm_edek(); let decrypted_aes_dek = decrypt_aes_edek(&kek, &aes_edek).unwrap(); @@ -188,12 +194,13 @@ mod test { #[test] fn signed_aes_edek_decrypts() { - let mut rng = ChaCha20Rng::seed_from_u64(203u64); + let rng = ChaCha20Rng::seed_from_u64(203u64); let kek = EncryptionKey(hex!( "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" )); let id = "hello"; - let (aes_dek, v4_document) = generate_aes_edek_and_sign(&mut rng, kek, None, id).unwrap(); + let (aes_dek, v4_document) = + generate_aes_edek_and_sign(Arc::new(Mutex::new(rng)), kek, None, id).unwrap(); let aes_edek = v4_document.signed_payload.0.unwrap().edeks[0].take_aes_256_gcm_edek(); let result = decrypt_aes_edek(&kek, &aes_edek).unwrap(); assert_eq!(result, aes_dek); diff --git a/src/v5/mod.rs b/src/v5/mod.rs index 274190d..ff1ff38 100644 --- a/src/v5/mod.rs +++ b/src/v5/mod.rs @@ -10,6 +10,7 @@ use crate::{ use bytes::{Buf, Bytes}; use key_id_header::KeyIdHeader; use rand::{CryptoRng, RngCore}; +use std::sync::{Arc, Mutex}; // The V5 data format is defined by 2 data formats. One for the edek and one for encrypted data encrypted with that edek. // The edek format is a 6 byte key id (see the key_id_header module) followed by a V4DocumentHeader proto. @@ -92,7 +93,7 @@ impl EncryptedPayload { /// Encrypt a document to be used as a detached document. This means it will have a header of `0IRON` as the first /// 5 bytes. pub fn encrypt_detached_document( - rng: &mut R, + rng: Arc>, key: EncryptionKey, document: PlaintextDocument, ) -> Result { @@ -122,12 +123,13 @@ mod test { use rand_chacha::ChaCha20Rng; #[test] fn encrypt_decrypt_detached_document_roundtrips() { - let mut rng = ChaCha20Rng::seed_from_u64(172u64); + let rng = ChaCha20Rng::seed_from_u64(172u64); let key = EncryptionKey(hex!( "fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0f0f1f2f3f4f5f6f7f8f9fafbfcfdfeff" )); let plaintext = PlaintextDocument(vec![100u8, 200u8]); - let encrypted = encrypt_detached_document(&mut rng, key, plaintext.clone()).unwrap(); + let encrypted = + encrypt_detached_document(Arc::new(Mutex::new(rng)), key, plaintext.clone()).unwrap(); let result = encrypted.decrypt(&key).unwrap(); assert_eq!(result, plaintext); }