From 2e081b6c4b691c17a9ef3e1c00bf950fdb9d2e00 Mon Sep 17 00:00:00 2001 From: Radu Marias Date: Wed, 5 Jun 2024 06:07:16 +0300 Subject: [PATCH] implement our own SecretVec which --- Cargo.lock | 23 ++--------- Cargo.toml | 5 ++- src/lib.rs | 9 +++-- src/secrets.rs | 105 +++++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 116 insertions(+), 26 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index af707ab..bcca88d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -673,11 +673,13 @@ checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rencrypt" -version = "0.3.0" +version = "0.3.1" dependencies = [ "blake3", "criterion", "hex", + "libc", + "libsodium-sys", "numpy", "pyo3", "rand", @@ -685,7 +687,6 @@ dependencies = [ "rand_core", "rayon", "ring", - "secrets", "zeroize", ] @@ -731,18 +732,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "secrets" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51745a213c4a2acabad80cd511e40376996bc83db6ceb4ebc7853d41c597988" -dependencies = [ - "libc", - "libsodium-sys", - "pkg-config", - "vcpkg", -] - [[package]] name = "serde" version = "1.0.203" @@ -831,12 +820,6 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index e74e380..58a53e3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rencrypt" -version = "0.3.0" +version = "0.3.1" edition = "2021" [lib] @@ -21,7 +21,8 @@ criterion = "0.5.1" blake3 = "=0.1.3" hex = "0.4.3" numpy = "0.21" -secrets = { version = "1.2.0", features = ["use-libsodium-sys"] } +libsodium-sys = "0.2.7" +libc = "0.2.155" [dev-dependencies] criterion = { version = "0.5.1", features = ["html_reports"] } diff --git a/src/lib.rs b/src/lib.rs index 3f8194e..5638797 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -10,11 +10,12 @@ use rayon::iter::ParallelIterator; use rayon::prelude::{ParallelSlice, ParallelSliceMut}; use ring::aead::{Aad, AES_256_GCM, BoundKey, CHACHA20_POLY1305, Nonce, NonceSequence, OpeningKey, SealingKey, UnboundKey}; use ring::error::Unspecified; -use secrets::SecretVec; use zeroize::Zeroize; use crate::CipherMeta::Ring; +use crate::secrets::SecretVec; mod cipher; +mod secrets; // 256KB seems to be the optimal block size that offers the max MB/s speed for encryption, // on benchmarks that seem to be the case. @@ -521,7 +522,7 @@ fn create_ring_sealing_key(alg: RingAlgorithm, key: &SecretVec) -> (SealingK let nonce_sequence = nonce_seq.clone(); let nonce_wrapper = RandomNonceSequenceWrapper::new(nonce_seq.clone()); // Create a new AEAD key without a designated role or nonce sequence - let unbound_key = UnboundKey::new(get_ring_algorithm(alg), &*key.borrow()).unwrap(); + let unbound_key = UnboundKey::new(get_ring_algorithm(alg), key.as_ref()).unwrap(); // Create a new AEAD key for encrypting and signing ("sealing"), bound to a nonce sequence // The SealingKey can be used multiple times, each time a new nonce will be used @@ -531,7 +532,7 @@ fn create_ring_sealing_key(alg: RingAlgorithm, key: &SecretVec) -> (SealingK fn create_ring_opening_key(alg: RingAlgorithm, key: &SecretVec) -> (OpeningKey, Arc>>) { let last_nonce = Arc::new(Mutex::new(vec![0_u8; get_ring_algorithm(alg).nonce_len()])); - let unbound_key = UnboundKey::new(get_ring_algorithm(alg), &*key.borrow()).unwrap(); + let unbound_key = UnboundKey::new(get_ring_algorithm(alg), key.as_ref()).unwrap(); let nonce_sequence = ExistingNonceSequence::new(last_nonce.clone()); let opening_key = OpeningKey::new(unbound_key, nonce_sequence); (opening_key, last_nonce) @@ -684,7 +685,7 @@ mod tests { fn test_encrypt_decrypt() { let cipher_meta = Ring { alg: RingAlgorithm::AES256GCM }; let alg = match cipher_meta { Ring { alg } => alg }; - let key = SecretVec::::new(get_ring_algorithm(alg).key_len(), |s| { + let key = SecretVec::new(get_ring_algorithm(alg).key_len(), |s| { create_rng().fill_bytes(s); }); let (sealing_key, nonce_sequence) = create_ring_sealing_key(alg, &key); diff --git a/src/secrets.rs b/src/secrets.rs index e69de29..8aed44b 100644 --- a/src/secrets.rs +++ b/src/secrets.rs @@ -0,0 +1,105 @@ +use std::sync::Once; +use libc::{self, size_t}; +use libsodium_sys::{ + sodium_init + , sodium_mlock + , sodium_munlock, +}; +use zeroize::Zeroize; + +/// The global [`sync::Once`] that ensures we only perform +/// library initialization one time. +static INIT: Once = Once::new(); + +/// A flag that returns whether this library has been safely +/// initialized. +static mut INITIALIZED: bool = false; + +pub struct SecretVec { + secret: Vec, +} + +impl SecretVec { + pub fn new(len: usize, f: F) -> Self + where F: FnOnce(&mut [T]) + { + let v = T::default(); + let mut secret: Vec = vec![v; len]; + unsafe { mlock(secret.as_mut_ptr(), len); } + f(&mut secret); + SecretVec { + secret, + } + } +} + +impl AsRef<[T]> for SecretVec { + fn as_ref(&self) -> &[T] { + &self.secret + } +} + +impl AsMut<[T]> for SecretVec { + fn as_mut(&mut self) -> &mut [T] { + &mut self.secret + } +} + +impl Drop for SecretVec { + fn drop(&mut self) { + self.secret.zeroize(); + unsafe { munlock(self.secret.as_mut_ptr(), self.secret.len()); } + } +} + +/// Initialized libsodium. This function *must* be called at least once +/// prior to using any of the other functions in this library, and +/// callers *must* verify that it returns `true`. If it returns `false`, +/// libsodium was unable to be properly set up and this library *must +/// not* be used. +/// +/// Calling it multiple times is a no-op. +fn init() -> bool { + unsafe { + INIT.call_once(|| { + // NOTE: Calls to transmute fail to compile if the source + // and destination type have a different size. We (ab)use + // this fact to statically assert the size of types at + // compile-time. + // + // We assume that we can freely cast between rust array + // sizes and [`libc::size_t`]. If that's not true, DO NOT + // COMPILE. + #[allow(clippy::useless_transmute)] + let _ = std::mem::transmute::(0); + + let mut failure = false; + + // sodium_init returns 0 on success, -1 on failure, and 1 if + // the library is already initialized; someone else might + // have already initialized it before us, so we only care + // about failure + failure |= sodium_init() == -1; + + INITIALIZED = !failure; + }); + + INITIALIZED + } +} + +/// Calls the platform's underlying `mlock(2)` implementation. +unsafe fn mlock(ptr: *mut T, len: usize) -> bool { + if !init() { + panic!("Failed to initialize libsodium"); + } + sodium_mlock(ptr.cast(), len) == 0 +} + +/// Calls the platform's underlying `munlock(2)` implementation. +unsafe fn munlock(ptr: *mut T, len: usize) -> bool { + if !init() { + panic!("Failed to initialize libsodium"); + } + sodium_munlock(ptr.cast(), len) == 0 +}