cloud_sdk_sanitization/
lib.rs1#![no_std]
2#![doc = include_str!("../README.md")]
3
4#[cfg(feature = "std")]
5extern crate std;
6
7#[inline]
12pub fn sanitize_bytes(bytes: &mut [u8]) {
13 sanitization::sanitize_bytes(bytes);
14}
15
16pub struct SecretBuffer<'a> {
22 bytes: &'a mut [u8],
23}
24
25impl<'a> SecretBuffer<'a> {
26 #[must_use]
28 pub const fn new(bytes: &'a mut [u8]) -> Self {
29 Self { bytes }
30 }
31
32 #[must_use]
34 pub fn as_mut_slice(&mut self) -> &mut [u8] {
35 self.bytes
36 }
37
38 #[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}