22
33use crate :: digest:: Digest ;
44use core:: fmt:: Debug ;
5+ use subtle:: ConstantTimeEq ;
56use zerocopy:: IntoBytes ;
7+ use zeroize:: { Zeroize , ZeroizeOnDrop } ;
68
7- /// Common error kinds for MAC operations (reused from digest operations).
9+ /// Secure wrapper for MAC keys that automatically zeros on drop.
10+ ///
11+ /// This wrapper ensures that cryptographic keys are securely erased from memory
12+ /// when no longer needed, preventing key material from remaining in memory.
13+ #[ derive( Clone , Zeroize , ZeroizeOnDrop ) ]
14+ pub struct SecureKey < const N : usize > {
15+ /// The actual key bytes, zeroized on drop
16+ bytes : [ u8 ; N ] ,
17+ }
18+
19+ impl < const N : usize > SecureKey < N > {
20+ /// Create a new secure key from a byte array.
21+ ///
22+ /// # Security
23+ /// The input array will be zeroized after copying to prevent key material
24+ /// from remaining in multiple memory locations.
25+ pub fn new ( mut key_bytes : [ u8 ; N ] ) -> Self {
26+ let key = Self { bytes : key_bytes } ;
27+ key_bytes. zeroize ( ) ;
28+ key
29+ }
30+
31+ /// Create a new secure key from a byte slice.
32+ ///
33+ /// # Returns
34+ /// - `Ok(SecureKey)` if the slice length matches the key size
35+ /// - `Err(ErrorKind::InvalidInputLength)` if the slice is the wrong size
36+ pub fn from_slice ( key_slice : & [ u8 ] ) -> Result < Self , ErrorKind > {
37+ if key_slice. len ( ) != N {
38+ return Err ( ErrorKind :: InvalidInputLength ) ;
39+ }
40+
41+ let mut key_bytes = [ 0u8 ; N ] ;
42+ key_bytes. copy_from_slice ( key_slice) ;
43+ Ok ( Self :: new ( key_bytes) )
44+ }
45+
46+ /// Get a reference to the key bytes.
47+ ///
48+ /// # Security
49+ /// Use this sparingly and ensure the returned reference doesn't outlive
50+ /// the SecureKey instance.
51+ pub fn as_bytes ( & self ) -> & [ u8 ; N ] {
52+ & self . bytes
53+ }
54+
55+ /// Verify a MAC tag using constant-time comparison.
56+ ///
57+ /// # Security
58+ /// This function uses constant-time comparison to prevent timing attacks
59+ /// that could reveal information about the expected MAC value.
60+ pub fn verify_mac ( & self , computed_mac : & [ u8 ] , expected_mac : & [ u8 ] ) -> bool {
61+ if computed_mac. len ( ) != expected_mac. len ( ) {
62+ return false ;
63+ }
64+ computed_mac. ct_eq ( expected_mac) . into ( )
65+ }
66+ }
67+
68+ impl < const N : usize > Debug for SecureKey < N > {
69+ fn fmt ( & self , f : & mut core:: fmt:: Formatter < ' _ > ) -> core:: fmt:: Result {
70+ f. debug_struct ( "SecureKey" )
71+ . field ( "len" , & N )
72+ . field ( "bytes" , & "[REDACTED]" )
73+ . finish ( )
74+ }
75+ }
76+
77+ impl < const N : usize > PartialEq for SecureKey < N > {
78+ fn eq ( & self , other : & Self ) -> bool {
79+ self . bytes . ct_eq ( & other. bytes ) . into ( )
80+ }
81+ }
82+
83+ impl < const N : usize > Eq for SecureKey < N > { }
84+
85+ /// Common error kinds for MAC operations.
886#[ derive( Debug , Copy , Clone , Eq , PartialEq , Ord , PartialOrd , Hash ) ]
987#[ non_exhaustive]
1088pub enum ErrorKind {
@@ -30,6 +108,8 @@ pub enum ErrorKind {
30108 PermissionDenied ,
31109 /// The MAC computation context has not been initialized.
32110 NotInitialized ,
111+ /// MAC verification failed - computed MAC does not match expected value.
112+ VerificationFailed ,
33113}
34114
35115/// Trait for converting implementation-specific errors into a common error kind.
@@ -116,6 +196,31 @@ pub trait MacOp: ErrorType {
116196 fn finalize ( self ) -> Result < Self :: Output , Self :: Error > ;
117197}
118198
199+ /// Utility function for constant-time MAC verification.
200+ ///
201+ /// This function provides a secure way to verify MAC values using constant-time
202+ /// comparison to prevent timing attacks.
203+ ///
204+ /// # Parameters
205+ ///
206+ /// - `computed_mac`: The computed MAC bytes
207+ /// - `expected_mac`: The expected MAC bytes to verify against
208+ ///
209+ /// # Returns
210+ ///
211+ /// `true` if the MACs match, `false` otherwise
212+ ///
213+ /// # Security
214+ ///
215+ /// This function uses constant-time comparison to prevent timing attacks
216+ /// that could reveal information about the expected MAC value.
217+ pub fn verify_mac_constant_time ( computed_mac : & [ u8 ] , expected_mac : & [ u8 ] ) -> bool {
218+ if computed_mac. len ( ) != expected_mac. len ( ) {
219+ return false ;
220+ }
221+ computed_mac. ct_eq ( expected_mac) . into ( )
222+ }
223+
119224// =============================================================================
120225// MAC Algorithm Marker Types
121226// =============================================================================
@@ -133,7 +238,7 @@ pub struct HmacSha2_256;
133238impl MacAlgorithm for HmacSha2_256 {
134239 const OUTPUT_BITS : usize = 256 ;
135240 type MacOutput = Digest < { Self :: OUTPUT_BITS / 32 } > ;
136- type Key = [ u8 ; 32 ] ;
241+ type Key = SecureKey < 32 > ;
137242}
138243
139244/// HMAC-SHA-384 MAC algorithm marker type.
@@ -149,7 +254,7 @@ pub struct HmacSha2_384;
149254impl MacAlgorithm for HmacSha2_384 {
150255 const OUTPUT_BITS : usize = 384 ;
151256 type MacOutput = Digest < { Self :: OUTPUT_BITS / 32 } > ;
152- type Key = [ u8 ; 48 ] ;
257+ type Key = SecureKey < 48 > ;
153258}
154259
155260/// HMAC-SHA-512 MAC algorithm marker type.
@@ -165,5 +270,5 @@ pub struct HmacSha2_512;
165270impl MacAlgorithm for HmacSha2_512 {
166271 const OUTPUT_BITS : usize = 512 ;
167272 type MacOutput = Digest < { Self :: OUTPUT_BITS / 32 } > ;
168- type Key = [ u8 ; 64 ] ;
273+ type Key = SecureKey < 64 > ;
169274}
0 commit comments