Skip to main content

cloud_sdk_sanitization/
lib.rs

1#![no_std]
2#![doc = include_str!("../README.md")]
3
4#[cfg(feature = "std")]
5extern crate std;
6
7/// Volatile-clears an ordinary caller-owned byte buffer.
8///
9/// This delegates to the reviewed `sanitization` crate so the clear cannot be
10/// removed as an ordinary dead store.
11#[inline]
12pub fn sanitize_bytes(bytes: &mut [u8]) {
13    sanitization::sanitize_bytes(bytes);
14}
15
16/// Caller-owned byte buffer that is volatile-cleared when dropped.
17///
18/// The full borrowed slice is cleared on success, error, or early return. This
19/// does not clear the source value or copies made by transports, operating
20/// systems, crash handlers, or remote services.
21pub struct SecretBuffer<'a> {
22    bytes: &'a mut [u8],
23}
24
25impl<'a> SecretBuffer<'a> {
26    /// Borrows a mutable byte slice until the guard is dropped.
27    #[must_use]
28    pub const fn new(bytes: &'a mut [u8]) -> Self {
29        Self { bytes }
30    }
31
32    /// Returns the guarded bytes for request construction.
33    #[must_use]
34    pub fn as_mut_slice(&mut self) -> &mut [u8] {
35        self.bytes
36    }
37
38    /// Returns the guarded bytes for transport.
39    #[must_use]
40    pub fn as_slice(&self) -> &[u8] {
41        self.bytes
42    }
43}
44
45impl Drop for SecretBuffer<'_> {
46    fn drop(&mut self) {
47        sanitize_bytes(self.bytes);
48    }
49}
50
51impl core::fmt::Debug for SecretBuffer<'_> {
52    fn fmt(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
53        formatter.write_str("SecretBuffer([redacted])")
54    }
55}
56
57#[cfg(test)]
58mod tests {
59    use super::{SecretBuffer, sanitize_bytes};
60
61    #[test]
62    fn explicit_sanitization_clears_every_byte() {
63        let mut bytes = [0xa5_u8; 8];
64        sanitize_bytes(&mut bytes);
65        assert_eq!(bytes, [0; 8]);
66    }
67
68    #[test]
69    fn guard_clears_its_full_buffer_on_drop() {
70        let mut bytes = [0xa5_u8; 8];
71        {
72            let mut guarded = SecretBuffer::new(&mut bytes);
73            if let Some(first) = guarded.as_mut_slice().first_mut() {
74                *first = 0x42;
75            }
76            assert_eq!(guarded.as_slice().first(), Some(&0x42));
77        }
78        assert_eq!(bytes, [0; 8]);
79    }
80
81    #[test]
82    fn guard_clears_after_an_early_error() {
83        fn write_then_fail(output: &mut [u8]) -> Result<(), ()> {
84            let mut guarded = SecretBuffer::new(output);
85            if let Some(first) = guarded.as_mut_slice().first_mut() {
86                *first = 0x42;
87            }
88            Err(())
89        }
90
91        let mut bytes = [0xa5_u8; 8];
92        assert_eq!(write_then_fail(&mut bytes), Err(()));
93        assert_eq!(bytes, [0; 8]);
94    }
95}