shardline_protocol/
security.rs1use std::fmt;
2
3use serde::Deserialize;
4use zeroize::{Zeroize, ZeroizeOnDrop};
5
6#[derive(Clone, PartialEq, Eq, Deserialize, Zeroize, ZeroizeOnDrop)]
8pub struct SecretBytes(Vec<u8>);
9
10impl SecretBytes {
11 #[must_use]
13 pub const fn new(secret: Vec<u8>) -> Self {
14 Self(secret)
15 }
16
17 #[must_use]
19 pub fn from_slice(secret: &[u8]) -> Self {
20 Self(secret.to_vec())
21 }
22
23 #[must_use]
25 pub fn expose_secret(&self) -> &[u8] {
26 &self.0
27 }
28
29 #[must_use]
31 pub const fn len(&self) -> usize {
32 self.0.len()
33 }
34
35 #[must_use]
37 pub const fn is_empty(&self) -> bool {
38 self.0.is_empty()
39 }
40}
41
42impl AsRef<[u8]> for SecretBytes {
43 fn as_ref(&self) -> &[u8] {
44 self.expose_secret()
45 }
46}
47
48impl fmt::Debug for SecretBytes {
49 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
50 formatter.write_str("***")
51 }
52}
53
54#[derive(Clone, PartialEq, Eq, Deserialize, Zeroize, ZeroizeOnDrop)]
56pub struct SecretString(String);
57
58impl SecretString {
59 #[must_use]
61 pub const fn new(secret: String) -> Self {
62 Self(secret)
63 }
64
65 #[must_use]
67 pub fn from_secret(secret: &str) -> Self {
68 Self(secret.to_owned())
69 }
70
71 #[must_use]
73 pub fn expose_secret(&self) -> &str {
74 &self.0
75 }
76
77 #[must_use]
79 pub const fn is_empty(&self) -> bool {
80 self.0.is_empty()
81 }
82}
83
84impl AsRef<str> for SecretString {
85 fn as_ref(&self) -> &str {
86 self.expose_secret()
87 }
88}
89
90impl fmt::Debug for SecretString {
91 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
92 formatter.write_str("***")
93 }
94}
95
96#[cfg(test)]
97mod tests {
98 use super::{SecretBytes, SecretString};
99
100 #[test]
101 fn secret_bytes_debug_redacts_contents() {
102 let secret = SecretBytes::from_slice(b"test-signing-key-32-bytes-long!!");
103
104 assert_eq!(format!("{secret:?}"), "***");
105 }
106
107 #[test]
108 fn secret_bytes_exposes_underlying_bytes() {
109 let secret = SecretBytes::from_slice(b"test-signing-key-32-bytes-long!!");
110
111 assert_eq!(secret.expose_secret(), b"test-signing-key-32-bytes-long!!");
112 }
113
114 #[test]
115 fn secret_string_debug_redacts_contents() {
116 let secret = SecretString::from_secret("bootstrap-token");
117
118 assert_eq!(format!("{secret:?}"), "***");
119 }
120
121 #[test]
122 fn secret_string_exposes_underlying_text() {
123 let secret = SecretString::from_secret("bootstrap-token");
124
125 assert_eq!(secret.expose_secret(), "bootstrap-token");
126 }
127}