@@ -77,7 +77,7 @@ impl MacError for CryptoError {
7777 }
7878}
7979
80- /// Simple byte array key wrapper for software implementations
80+ /// Simple byte array key wrapper for software implementations (borrowed data)
8181#[ derive( Debug , Clone ) ]
8282pub struct ByteArrayKey < ' a > ( & ' a [ u8 ] ) ;
8383
@@ -93,6 +93,97 @@ impl<'a> ByteArrayKey<'a> {
9393
9494impl < ' a > KeyHandle for ByteArrayKey < ' a > { }
9595
96+ /// Secure owned key type that owns its data on the stack (no allocation required)
97+ /// Maximum key size is 128 bytes to support all HMAC variants (SHA-512 block size)
98+ /// This solves the lifetime issue where ByteArrayKey<'static> cannot be created from local data
99+ #[ derive( Debug , Clone ) ]
100+ pub struct SecureOwnedKey {
101+ data : [ u8 ; 128 ] , // Fixed size buffer - no allocation needed
102+ len : usize , // Actual key length
103+ }
104+
105+ impl SecureOwnedKey {
106+ /// Maximum supported key length (128 bytes for SHA-512 block size)
107+ pub const MAX_KEY_SIZE : usize = 128 ;
108+
109+ /// Create a new secure key by copying the provided bytes
110+ /// Returns error if key is too large for our fixed buffer
111+ pub fn new ( bytes : & [ u8 ] ) -> Result < Self , CryptoError > {
112+ if bytes. len ( ) > Self :: MAX_KEY_SIZE {
113+ return Err ( CryptoError :: InvalidKeyLength ) ;
114+ }
115+
116+ let mut data = [ 0u8 ; 128 ] ;
117+ data[ ..bytes. len ( ) ] . copy_from_slice ( bytes) ;
118+
119+ Ok ( Self {
120+ data,
121+ len : bytes. len ( ) ,
122+ } )
123+ }
124+
125+ /// Create a secure key from a fixed-size array (zero-copy for arrays up to 128 bytes)
126+ pub fn from_array < const N : usize > ( array : [ u8 ; N ] ) -> Result < Self , CryptoError > {
127+ if N > Self :: MAX_KEY_SIZE {
128+ return Err ( CryptoError :: InvalidKeyLength ) ;
129+ }
130+
131+ let mut data = [ 0u8 ; 128 ] ;
132+ data[ ..N ] . copy_from_slice ( & array) ;
133+
134+ Ok ( Self { data, len : N } )
135+ }
136+
137+ /// Get the key bytes as a slice (only the valid portion)
138+ pub fn as_bytes ( & self ) -> & [ u8 ] {
139+ & self . data [ ..self . len ]
140+ }
141+
142+ /// Get the key length
143+ pub fn len ( & self ) -> usize {
144+ self . len
145+ }
146+
147+ /// Check if the key is empty
148+ pub fn is_empty ( & self ) -> bool {
149+ self . len == 0
150+ }
151+ }
152+
153+ impl KeyHandle for SecureOwnedKey { }
154+
155+ impl TryFrom < & [ u8 ] > for SecureOwnedKey {
156+ type Error = CryptoError ;
157+
158+ fn try_from ( bytes : & [ u8 ] ) -> Result < Self , Self :: Error > {
159+ Self :: new ( bytes)
160+ }
161+ }
162+
163+ impl < const N : usize > TryFrom < [ u8 ; N ] > for SecureOwnedKey {
164+ type Error = CryptoError ;
165+
166+ fn try_from ( array : [ u8 ; N ] ) -> Result < Self , Self :: Error > {
167+ Self :: from_array ( array)
168+ }
169+ }
170+
171+ impl < const N : usize > TryFrom < & [ u8 ; N ] > for SecureOwnedKey {
172+ type Error = CryptoError ;
173+
174+ fn try_from ( array : & [ u8 ; N ] ) -> Result < Self , Self :: Error > {
175+ Self :: new ( array)
176+ }
177+ }
178+
179+ // Implement Drop to securely zero the key data when dropped
180+ impl Drop for SecureOwnedKey {
181+ fn drop ( & mut self ) {
182+ // Securely zero the key data
183+ self . data . fill ( 0 ) ;
184+ }
185+ }
186+
96187/// Digest contexts for different SHA algorithms
97188pub struct DigestContext256 ( Sha256 ) ;
98189pub struct DigestContext384 ( Sha384 ) ;
@@ -270,7 +361,7 @@ impl DigestOp for DigestContext512 {
270361
271362// MAC initialization - creates HMAC-SHA256 context
272363impl MacInit < HmacSha2_256 > for RustCryptoController {
273- type Key = ByteArrayKey < ' static > ;
364+ type Key = SecureOwnedKey ;
274365 type Context = MacContext256 ;
275366 type Output = [ u8 ; 32 ] ; // HMAC-SHA256 output size
276367
@@ -283,7 +374,7 @@ impl MacInit<HmacSha2_256> for RustCryptoController {
283374
284375// MAC initialization - creates HMAC-SHA384 context
285376impl MacInit < HmacSha2_384 > for RustCryptoController {
286- type Key = ByteArrayKey < ' static > ;
377+ type Key = SecureOwnedKey ;
287378 type Context = MacContext384 ;
288379 type Output = [ u8 ; 48 ] ; // HMAC-SHA384 output size
289380
@@ -296,7 +387,7 @@ impl MacInit<HmacSha2_384> for RustCryptoController {
296387
297388// MAC initialization - creates HMAC-SHA512 context
298389impl MacInit < HmacSha2_512 > for RustCryptoController {
299- type Key = ByteArrayKey < ' static > ;
390+ type Key = SecureOwnedKey ;
300391 type Context = MacContext512 ;
301392 type Output = [ u8 ; 64 ] ; // HMAC-SHA512 output size
302393
@@ -430,7 +521,7 @@ mod tests {
430521 #[ test]
431522 #[ allow( clippy:: unwrap_used) ]
432523 fn test_mac_operations ( ) {
433- let key = ByteArrayKey :: new ( b"super secret key" ) ;
524+ let key = SecureOwnedKey :: new ( b"super secret key" ) . unwrap ( ) ;
434525
435526 // Test HMAC-SHA256
436527 let controller = RustCryptoController :: new ( ) ;
@@ -466,7 +557,7 @@ mod tests {
466557 let ( hash256, controller) = digest_ctx. finalize ( ) . unwrap ( ) ;
467558
468559 // Use recovered controller for HMAC-SHA384
469- let key = ByteArrayKey :: new ( b"key" ) ;
560+ let key = SecureOwnedKey :: new ( b"key" ) . unwrap ( ) ;
470561 let mac_ctx = MacInit :: < HmacSha2_384 > :: init ( controller, HmacSha2_384 , key) . unwrap ( ) ;
471562 let mac_ctx = mac_ctx. update ( b"test data" ) . unwrap ( ) ;
472563 let ( mac384, controller) = mac_ctx. finalize ( ) . unwrap ( ) ;
@@ -484,4 +575,49 @@ mod tests {
484575 let hash512_bytes = hash512. as_bytes ( ) ;
485576 assert_eq ! ( hash512_bytes. len( ) , 64 ) ; // SHA-512 output
486577 }
578+
579+ #[ test]
580+ #[ allow( clippy:: unwrap_used) ]
581+ fn test_secure_owned_key ( ) {
582+ // Test creation from slice
583+ let key1 = SecureOwnedKey :: new ( b"test key" ) . unwrap ( ) ;
584+ assert_eq ! ( key1. as_bytes( ) , b"test key" ) ;
585+ assert_eq ! ( key1. len( ) , 8 ) ;
586+ assert ! ( !key1. is_empty( ) ) ;
587+
588+ // Test creation from fixed array
589+ let key2 = SecureOwnedKey :: from_array ( * b"another key" ) . unwrap ( ) ;
590+ assert_eq ! ( key2. as_bytes( ) , b"another key" ) ;
591+ assert_eq ! ( key2. len( ) , 11 ) ;
592+
593+ // Test TryFrom trait implementations
594+ let key3: SecureOwnedKey = b"from slice" . as_slice ( ) . try_into ( ) . unwrap ( ) ;
595+ assert_eq ! ( key3. as_bytes( ) , b"from slice" ) ;
596+
597+ let key4: SecureOwnedKey = ( * b"from fixed array" ) . try_into ( ) . unwrap ( ) ;
598+ assert_eq ! ( key4. as_bytes( ) , b"from fixed array" ) ;
599+
600+ let key5: SecureOwnedKey = b"from array ref" . try_into ( ) . unwrap ( ) ;
601+ assert_eq ! ( key5. as_bytes( ) , b"from array ref" ) ;
602+
603+ // Test cloning
604+ let key6 = key1. clone ( ) ;
605+ assert_eq ! ( key6. as_bytes( ) , key1. as_bytes( ) ) ;
606+
607+ // Test empty key
608+ let empty_key = SecureOwnedKey :: new ( & [ ] ) . unwrap ( ) ;
609+ assert ! ( empty_key. is_empty( ) ) ;
610+ assert_eq ! ( empty_key. len( ) , 0 ) ;
611+
612+ // Test maximum key size (should succeed)
613+ let max_key_data = [ 0u8 ; SecureOwnedKey :: MAX_KEY_SIZE ] ;
614+ let max_key = SecureOwnedKey :: new ( & max_key_data) . unwrap ( ) ;
615+ assert_eq ! ( max_key. len( ) , SecureOwnedKey :: MAX_KEY_SIZE ) ;
616+
617+ // Test oversized key (should fail)
618+ let oversized_data = [ 0u8 ; SecureOwnedKey :: MAX_KEY_SIZE + 1 ] ;
619+ let result = SecureOwnedKey :: new ( & oversized_data) ;
620+ assert ! ( result. is_err( ) ) ;
621+ assert_eq ! ( result. unwrap_err( ) , CryptoError :: InvalidKeyLength ) ;
622+ }
487623}
0 commit comments