t_rust_less_lib/memguard/
weak.rs

1//! # Zeroing wrappers
2//!
3//! These are weak variants of SecretBytes. Basically they are just wrappers around Vec<u8> and
4//! String with an addition Drop zeroing their contents. These are mostly used inside the API
5//! structs and do not provide a 100% guaranty that no sensitive data remains in memory.
6//!
7//! After all though: Data send and received via the API is processed by different clients and
8//! most likely copy-pasted by to displayed to the user ... so there is no 100% guaranty anyway
9//! and it would be a waste of effort providing a super-tight security.
10//!
11use super::memory;
12use capnp::message::{AllocationStrategy, Allocator, SUGGESTED_ALLOCATION_STRATEGY, SUGGESTED_FIRST_SEGMENT_WORDS};
13use capnp::Word;
14use log::warn;
15use std::ops::{Deref, DerefMut};
16
17#[derive(Clone, Debug, PartialEq, Eq)]
18pub struct ZeroingWords(Vec<Word>);
19
20impl ZeroingWords {
21  pub fn allocate_zeroed_vec(size: usize) -> ZeroingWords {
22    ZeroingWords(Word::allocate_zeroed_vec(size))
23  }
24
25  pub fn is_empty(&self) -> bool {
26    self.0.is_empty()
27  }
28
29  pub fn len(&self) -> usize {
30    self.0.len()
31  }
32}
33
34impl Drop for ZeroingWords {
35  fn drop(&mut self) {
36    unsafe {
37      memory::memzero(self.0.as_mut_ptr() as *mut u8, self.0.capacity() * 8);
38    }
39  }
40}
41
42impl Deref for ZeroingWords {
43  type Target = [u8];
44
45  fn deref(&self) -> &Self::Target {
46    unsafe { std::slice::from_raw_parts(self.0.as_ptr() as *const u8, self.0.len() * 8) }
47  }
48}
49
50impl DerefMut for ZeroingWords {
51  fn deref_mut(&mut self) -> &mut Self::Target {
52    unsafe { std::slice::from_raw_parts_mut(self.0.as_mut_ptr() as *mut u8, self.0.len() * 8) }
53  }
54}
55
56impl From<&[u8]> for ZeroingWords {
57  fn from(bytes: &[u8]) -> Self {
58    if bytes.len() % 8 != 0 {
59      warn!("Bytes not aligned to 8 bytes. Probably these are not the bytes you are looking for.");
60    }
61    let len = bytes.len() / 8;
62    let mut target = ZeroingWords::allocate_zeroed_vec(len);
63    unsafe {
64      std::ptr::copy_nonoverlapping(bytes.as_ptr(), target.as_mut_ptr(), len * 8);
65    }
66
67    target
68  }
69}
70
71#[derive(Debug)]
72pub struct ZeroingHeapAllocator {
73  owned_memory: Vec<ZeroingWords>,
74  next_size: u32,
75  allocation_strategy: AllocationStrategy,
76}
77
78impl ZeroingHeapAllocator {
79  pub fn first_segment_words(mut self, value: u32) -> ZeroingHeapAllocator {
80    self.next_size = value;
81    self
82  }
83
84  pub fn allocation_strategy(mut self, value: AllocationStrategy) -> ZeroingHeapAllocator {
85    self.allocation_strategy = value;
86    self
87  }
88}
89
90impl Default for ZeroingHeapAllocator {
91  fn default() -> Self {
92    ZeroingHeapAllocator {
93      owned_memory: Vec::new(),
94      next_size: SUGGESTED_FIRST_SEGMENT_WORDS,
95      allocation_strategy: SUGGESTED_ALLOCATION_STRATEGY,
96    }
97  }
98}
99
100unsafe impl Allocator for ZeroingHeapAllocator {
101  fn allocate_segment(&mut self, minimum_size: u32) -> (*mut u8, u32) {
102    let size = ::std::cmp::max(minimum_size, self.next_size);
103    let mut new_words = ZeroingWords::allocate_zeroed_vec(size as usize);
104    let ptr = new_words.as_mut_ptr();
105    self.owned_memory.push(new_words);
106
107    if let AllocationStrategy::GrowHeuristically = self.allocation_strategy {
108      self.next_size += size;
109    }
110    (ptr, size)
111  }
112
113  fn deallocate_segment(&mut self, _ptr: *mut u8, _word_size: u32, _words_used: u32) {
114    self.next_size = SUGGESTED_FIRST_SEGMENT_WORDS;
115  }
116}
117
118#[cfg(test)]
119mod tests {
120  use super::*;
121
122  #[test]
123  pub fn test_zeroing_drop() {
124    {
125      let zeroing = ZeroingWords::allocate_zeroed_vec(200);
126
127      assert_eq!(zeroing.len(), 200);
128
129      for w in zeroing.iter() {
130        assert_eq!(*w, 0);
131      }
132    }
133  }
134}