diff --git a/Cargo.lock b/Cargo.lock index 8887aa87..1c350f38 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -447,6 +447,7 @@ dependencies = [ "uuid", "wasm-bindgen", "zeroize", + "zeroizing-alloc", ] [[package]] @@ -4586,6 +4587,12 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "zeroizing-alloc" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebff5e6b81c1c7dca2d0bd333b2006da48cb37dbcae5a8da888f31fcb3c19934" + [[package]] name = "zxcvbn" version = "3.1.0" diff --git a/crates/bitwarden-crypto/Cargo.toml b/crates/bitwarden-crypto/Cargo.toml index 7671a776..7bf32615 100644 --- a/crates/bitwarden-crypto/Cargo.toml +++ b/crates/bitwarden-crypto/Cargo.toml @@ -48,6 +48,7 @@ uniffi = { workspace = true, optional = true } uuid = { workspace = true } wasm-bindgen = { workspace = true, optional = true } zeroize = { version = ">=1.7.0, <2.0", features = ["derive", "aarch64"] } +zeroizing-alloc = ">=0.1.0, <0.2" [dev-dependencies] criterion = "0.5.1" diff --git a/crates/bitwarden-crypto/src/allocator.rs b/crates/bitwarden-crypto/src/allocator.rs deleted file mode 100644 index 525b150d..00000000 --- a/crates/bitwarden-crypto/src/allocator.rs +++ /dev/null @@ -1,167 +0,0 @@ -use core::slice; -use std::alloc::{GlobalAlloc, Layout}; - -use zeroize::Zeroize; - -/// Custom allocator that zeroizes memory before deallocating it -/// -/// This is highly recommended to be enabled when using the Bitwarden crates to avoid sensitive data -/// persisting in memory after it has been deallocated. -/// -/// This allocator is a decorator around another allocator. -/// -/// # Example -/// -/// This example shows how to use the `ZeroizingAllocator` with the system allocator. -/// -/// ```rust,ignore -/// #[global_allocator] -/// static ALLOC: bitwarden_crypto::ZeroizingAllocator = -/// bitwarden_crypto::ZeroizingAllocator(std::alloc::System); -/// ``` -pub struct ZeroizingAllocator(pub Alloc); - -unsafe impl GlobalAlloc for ZeroizingAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - self.0.alloc(layout) - } - - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - slice::from_raw_parts_mut(ptr, layout.size()).zeroize(); - - self.0.dealloc(ptr, layout); - } - - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - self.0.alloc_zeroed(layout) - } -} - -#[cfg(test)] -mod tests { - #[test] - #[ignore = "It produces inconsistent results on some platforms"] - fn string() { - let s = String::from("hello"); - - let p1 = s.as_str().as_ptr(); - let c1 = s.capacity(); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p1, c1) }, - b"hello", - "String is not at the expected memory location" - ); - - drop(s); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p1, c1) }, - [0, 0, 0, 0, 0], - "memory was not zeroized after dropping the string" - ); - } - - #[test] - #[ignore = "It produces inconsistent results on some platforms"] - fn string_expand() { - let mut s = String::from("hello"); - - let p1 = s.as_str().as_ptr(); - let c1 = s.capacity(); - - assert_eq!(unsafe { std::slice::from_raw_parts(p1, c1) }, b"hello"); - - s.push_str(" world"); - - let p2 = s.as_str().as_ptr(); - let c2 = s.capacity(); - - // We allocated a new string - assert_ne!(p1, p2); - assert_eq!( - unsafe { std::slice::from_raw_parts(p1, c1) }, - [0, 0, 0, 0, 0], - "old string was not zeroized" - ); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p2, c2) }, - b"hello world" - ); - - // Drop the expanded string - drop(s); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p2, c2) }, - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expanded string was not zeroized" - ); - } - - #[test] - #[ignore = "It produces inconsistent results on some platforms"] - fn vec() { - let v = vec![1, 2, 3, 4, 5]; - - let p1 = v.as_slice().as_ptr(); - let c1 = v.capacity(); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p1, c1) }, - [1, 2, 3, 4, 5], - "vec is not at the expected memory location" - ); - - drop(v); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p1, c1) }, - [0, 0, 0, 0, 0], - "vec was not zeroized after dropping" - ); - } - - #[test] - #[ignore = "It produces inconsistent results on some platforms"] - fn vec_expand() { - let mut v = vec![1, 2, 3, 4, 5]; - - let p1 = v.as_slice().as_ptr(); - let c1 = v.capacity(); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p1, c1) }, - [1, 2, 3, 4, 5], - "vec is not at the expected memory location" - ); - - v.extend_from_slice(&[6, 7, 8, 9, 10]); - - let p2 = v.as_slice().as_ptr(); - let c2 = v.capacity(); - - // We allocated a new vector - assert_ne!(p1, p2); - assert_eq!( - unsafe { std::slice::from_raw_parts(p1, c1) }, - [0, 0, 0, 0, 0], - "old vec was not zeroized" - ); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p2, c2) }, - [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] - ); - - // Drop the expanded vector - drop(v); - - assert_eq!( - unsafe { std::slice::from_raw_parts(p2, c2) }, - [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], - "expanded vec was not zeroized" - ); - } -} diff --git a/crates/bitwarden-crypto/src/lib.rs b/crates/bitwarden-crypto/src/lib.rs index 44efaac3..40f7a63c 100644 --- a/crates/bitwarden-crypto/src/lib.rs +++ b/crates/bitwarden-crypto/src/lib.rs @@ -80,8 +80,7 @@ mod util; pub use util::{generate_random_alphanumeric, generate_random_bytes, pbkdf2}; mod wordlist; pub use wordlist::EFF_LONG_WORD_LIST; -mod allocator; -pub use allocator::ZeroizingAllocator; +pub use zeroizing_alloc::ZeroAlloc as ZeroizingAllocator; #[cfg(feature = "uniffi")] uniffi::setup_scaffolding!();