1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136
//! Define a wrapper for `Vec<u8>` that will act as Writer, but zeroize its
//! contents on drop or reallocation.
use crate::Writer;
use zeroize::{Zeroize, ZeroizeOnDrop};
/// A [`Writer`] used for accumulating secret data, which gets cleared on drop.
///
/// Unlike `Zeroizing<Vec<u8>>`, this type makes sure that we always zeroize the
/// contents of the buffer, even if the buffer has to be reallocated in order to
/// grow.
///
/// We use this for cases when we're building the input to a key derivation
/// function (KDF), and want to ensure that we don't expose the values we feed
/// to it.
///
/// This struct is expected to have additional overhead beyond `Vec<u8>` only
/// when it has to grow its capacity.
#[derive(Zeroize, ZeroizeOnDrop, Debug, Clone, Eq, PartialEq)]
pub struct SecretBuf(Vec<u8>);
/// The default size of our buffer.
///
/// This is based on the size of a typical secret input in `tor-proto`.
const DEFAULT_CAPACITY: usize = 384;
impl SecretBuf {
/// Construct a new empty [`SecretBuf`]
pub fn new() -> Self {
Self::with_capacity(DEFAULT_CAPACITY)
}
/// Construct a new empty [`SecretBuf`] with a specified capacity.
///
/// This buffer will not have to be reallocated until it uses `capacity`
/// bytes.
pub fn with_capacity(capacity: usize) -> Self {
Self(Vec::with_capacity(capacity))
}
/// Truncate this buffer to a given length.
pub fn truncate(&mut self, new_len: usize) {
self.0.truncate(new_len);
}
/// Add all the bytes from `slice` to the end of this vector.
pub fn extend_from_slice(&mut self, slice: &[u8]) {
let new_len = self.0.len() + slice.len();
if new_len >= self.0.capacity() {
// We will need to reallocate. But in doing so we might reallocate,
// which neglects to zero the previous contents. So instead,
// explicitly make a new vector and zeroize the old one.
// Make sure we always at least double our capacity.
let new_capacity = std::cmp::max(self.0.capacity() * 2, new_len);
let mut new_vec = Vec::with_capacity(new_capacity);
new_vec.extend_from_slice(&self.0[..]);
let mut old_vec = std::mem::replace(&mut self.0, new_vec);
old_vec.zeroize();
}
self.0.extend_from_slice(slice);
debug_assert_eq!(self.0.len(), new_len);
}
}
impl From<Vec<u8>> for SecretBuf {
fn from(v: Vec<u8>) -> Self {
Self(v)
}
}
impl Default for SecretBuf {
fn default() -> Self {
Self::new()
}
}
impl AsMut<[u8]> for SecretBuf {
fn as_mut(&mut self) -> &mut [u8] {
&mut self.0[..]
}
}
// It's okay to implement `Deref` since all operations taking an _immutable_
// reference are still right here.
impl std::ops::Deref for SecretBuf {
type Target = Vec<u8>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Writer for SecretBuf {
fn write_all(&mut self, b: &[u8]) {
self.extend_from_slice(b);
}
}
#[cfg(test)]
mod test {
// @@ begin test lint list maintained by maint/add_warning @@
#![allow(clippy::bool_assert_comparison)]
#![allow(clippy::clone_on_copy)]
#![allow(clippy::dbg_macro)]
#![allow(clippy::mixed_attributes_style)]
#![allow(clippy::print_stderr)]
#![allow(clippy::print_stdout)]
#![allow(clippy::single_char_pattern)]
#![allow(clippy::unwrap_used)]
#![allow(clippy::unchecked_duration_subtraction)]
#![allow(clippy::useless_vec)]
#![allow(clippy::needless_pass_by_value)]
//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
use super::*;
#[test]
fn simple_case() -> crate::EncodeResult<()> {
// Sadly, there is no way in safe rust to test that the zeroization
// actually happened. All we can test is that the data is correct.
let mut buf1 = SecretBuf::default();
let mut buf2 = Vec::new();
let xyz = b"Nine hundred pounds of sifted flax";
// This is enough to be sure that we'll reallocate.
for _ in 0..200 {
buf1.write(xyz)?;
buf2.write(xyz)?;
}
assert_eq!(&buf1[..], &buf2[..]);
Ok(())
}
}