@@ -70,7 +70,11 @@ pub struct HsmConfig {
7070}
7171
7272/// HSM authentication credentials
73- #[ derive( Debug , Clone , Default , Serialize , Deserialize ) ]
73+ ///
74+ /// # Security
75+ /// Credentials are automatically zeroed when dropped to prevent
76+ /// sensitive data from remaining in memory.
77+ #[ derive( Debug , Clone , Serialize , Deserialize ) ]
7478pub struct HsmCredentials {
7579 /// API token (for Vault)
7680 #[ serde( skip_serializing) ]
@@ -87,6 +91,46 @@ pub struct HsmCredentials {
8791 pub client_key : Option < String > ,
8892}
8993
94+ impl Default for HsmCredentials {
95+ fn default ( ) -> Self {
96+ Self {
97+ token : None ,
98+ access_key : None ,
99+ secret_key : None ,
100+ client_cert : None ,
101+ client_key : None ,
102+ }
103+ }
104+ }
105+
106+ impl Drop for HsmCredentials {
107+ fn drop ( & mut self ) {
108+ // Securely zero out sensitive credential data
109+ // Note: This provides basic protection but for production use
110+ // consider using the `secrecy` crate for guaranteed secure zeroing
111+ if let Some ( ref mut token) = self . token {
112+ // Safety: We're writing zeros to memory that will be dropped
113+ // This helps prevent secrets from lingering in memory
114+ let bytes = unsafe { token. as_bytes_mut ( ) } ;
115+ for byte in bytes {
116+ unsafe { std:: ptr:: write_volatile ( byte, 0 ) } ;
117+ }
118+ }
119+ if let Some ( ref mut key) = self . access_key {
120+ let bytes = unsafe { key. as_bytes_mut ( ) } ;
121+ for byte in bytes {
122+ unsafe { std:: ptr:: write_volatile ( byte, 0 ) } ;
123+ }
124+ }
125+ if let Some ( ref mut key) = self . secret_key {
126+ let bytes = unsafe { key. as_bytes_mut ( ) } ;
127+ for byte in bytes {
128+ unsafe { std:: ptr:: write_volatile ( byte, 0 ) } ;
129+ }
130+ }
131+ }
132+ }
133+
90134impl HsmConfig {
91135 /// Create configuration for HashiCorp Vault
92136 pub fn vault ( endpoint : & str , token : & str , key_name : & str ) -> Self {
@@ -95,7 +139,10 @@ impl HsmConfig {
95139 endpoint : endpoint. to_string ( ) ,
96140 credentials : HsmCredentials {
97141 token : Some ( token. to_string ( ) ) ,
98- ..Default :: default ( )
142+ access_key : None ,
143+ secret_key : None ,
144+ client_cert : None ,
145+ client_key : None ,
99146 } ,
100147 default_key : key_name. to_string ( ) ,
101148 timeout_secs : 30 ,
@@ -109,9 +156,11 @@ impl HsmConfig {
109156 provider : HsmProvider :: AwsCloudHsm ,
110157 endpoint : endpoint. to_string ( ) ,
111158 credentials : HsmCredentials {
159+ token : None ,
112160 access_key : Some ( access_key. to_string ( ) ) ,
113161 secret_key : Some ( secret_key. to_string ( ) ) ,
114- ..Default :: default ( )
162+ client_cert : None ,
163+ client_key : None ,
115164 } ,
116165 default_key : key_name. to_string ( ) ,
117166 timeout_secs : 30 ,
@@ -124,7 +173,7 @@ impl HsmConfig {
124173 Self {
125174 provider : HsmProvider :: Mock ,
126175 endpoint : "mock://localhost" . to_string ( ) ,
127- credentials : Default :: default ( ) ,
176+ credentials : HsmCredentials :: default ( ) ,
128177 default_key : key_name. to_string ( ) ,
129178 timeout_secs : 5 ,
130179 audit_logging : false ,
@@ -185,6 +234,10 @@ pub trait HsmBackend: Send + Sync {
185234 async fn list_keys ( & self ) -> HsmResult < Vec < String > > ;
186235}
187236
237+ /// Maximum number of audit log entries to keep in memory
238+ /// Older entries are automatically rotated out
239+ const MAX_AUDIT_LOG_ENTRIES : usize = 10_000 ;
240+
188241/// HSM client for secure key management
189242pub struct HsmClient {
190243 config : HsmConfig ,
@@ -309,6 +362,9 @@ impl HsmClient {
309362 }
310363
311364 /// Log an operation
365+ ///
366+ /// The audit log is bounded to MAX_AUDIT_LOG_ENTRIES entries.
367+ /// When the limit is reached, the oldest entries are removed.
312368 async fn log_operation ( & self , operation : & str , key_name : & str , success : bool , error : Option < & HsmError > ) {
313369 if !self . config . audit_logging {
314370 return ;
@@ -325,7 +381,14 @@ impl HsmClient {
325381 error : error. map ( |e| e. to_string ( ) ) ,
326382 } ;
327383
328- self . audit_log . write ( ) . await . push ( entry) ;
384+ let mut log = self . audit_log . write ( ) . await ;
385+ log. push ( entry) ;
386+
387+ // Enforce maximum size by removing oldest entries
388+ if log. len ( ) > MAX_AUDIT_LOG_ENTRIES {
389+ let excess = log. len ( ) - MAX_AUDIT_LOG_ENTRIES ;
390+ log. drain ( 0 ..excess) ;
391+ }
329392 }
330393}
331394
@@ -438,7 +501,9 @@ mod tests {
438501
439502 #[ tokio:: test]
440503 async fn test_mock_hsm_audit_log ( ) {
441- let config = HsmConfig :: mock ( "audit-test" ) ;
504+ // Create mock config with audit logging enabled
505+ let mut config = HsmConfig :: mock ( "audit-test" ) ;
506+ config. audit_logging = true ;
442507 let hsm = HsmClient :: connect ( config) . await . unwrap ( ) ;
443508
444509 // Perform some operations
0 commit comments