| // Licensed under the Apache-2.0 license |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| use crate::digest::Digest; |
| use core::fmt::Debug; |
| use subtle::ConstantTimeEq; |
| use zerocopy::IntoBytes; |
| use zeroize::{Zeroize, ZeroizeOnDrop}; |
| |
| /// Secure wrapper for MAC keys that automatically zeros on drop. |
| /// |
| /// This wrapper ensures that cryptographic keys are securely erased from memory |
| /// when no longer needed, preventing key material from remaining in memory. |
| #[derive(Clone, Zeroize, ZeroizeOnDrop)] |
| pub struct SecureKey<const N: usize> { |
| /// The actual key bytes, zeroized on drop |
| bytes: [u8; N], |
| } |
| |
| impl<const N: usize> SecureKey<N> { |
| /// Create a new secure key from a byte array. |
| /// |
| /// # Security |
| /// The input array will be zeroized after copying to prevent key material |
| /// from remaining in multiple memory locations. |
| pub fn new(mut key_bytes: [u8; N]) -> Self { |
| let key = Self { bytes: key_bytes }; |
| key_bytes.zeroize(); |
| key |
| } |
| |
| /// Create a new secure key from a byte slice. |
| /// |
| /// # Returns |
| /// - `Ok(SecureKey)` if the slice length matches the key size |
| /// - `Err(ErrorKind::InvalidInputLength)` if the slice is the wrong size |
| pub fn from_slice(key_slice: &[u8]) -> Result<Self, ErrorKind> { |
| if key_slice.len() != N { |
| return Err(ErrorKind::InvalidInputLength); |
| } |
| |
| let mut key_bytes = [0u8; N]; |
| key_bytes.copy_from_slice(key_slice); |
| Ok(Self::new(key_bytes)) |
| } |
| |
| /// Get a reference to the key bytes. |
| /// |
| /// # Security |
| /// Use this sparingly and ensure the returned reference doesn't outlive |
| /// the SecureKey instance. |
| pub fn as_bytes(&self) -> &[u8; N] { |
| &self.bytes |
| } |
| |
| /// Verify a MAC tag using constant-time comparison. |
| /// |
| /// # Security |
| /// This function uses constant-time comparison to prevent timing attacks |
| /// that could reveal information about the expected MAC value. |
| pub fn verify_mac(&self, computed_mac: &[u8], expected_mac: &[u8]) -> bool { |
| if computed_mac.len() != expected_mac.len() { |
| return false; |
| } |
| computed_mac.ct_eq(expected_mac).into() |
| } |
| } |
| |
| impl<const N: usize> Debug for SecureKey<N> { |
| fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| f.debug_struct("SecureKey") |
| .field("len", &N) |
| .field("bytes", &"[REDACTED]") |
| .finish() |
| } |
| } |
| |
| impl<const N: usize> PartialEq for SecureKey<N> { |
| fn eq(&self, other: &Self) -> bool { |
| self.bytes.ct_eq(&other.bytes).into() |
| } |
| } |
| |
| impl<const N: usize> Eq for SecureKey<N> {} |
| |
| impl<const N: usize> AsRef<[u8]> for SecureKey<N> { |
| fn as_ref(&self) -> &[u8] { |
| &self.bytes |
| } |
| } |
| |
| impl<const N: usize> KeyHandle for SecureKey<N> {} |
| |
| /// Common error kinds for MAC operations. |
| #[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] |
| #[non_exhaustive] |
| pub enum ErrorKind { |
| /// The input data length is not valid for the MAC function. |
| InvalidInputLength, |
| /// The specified MAC algorithm is not supported by the hardware or software implementation. |
| UnsupportedAlgorithm, |
| /// Failed to allocate memory for the MAC computation. |
| MemoryAllocationFailure, |
| /// Failed to initialize the MAC computation context. |
| InitializationError, |
| /// Error occurred while updating the MAC computation with new data. |
| UpdateError, |
| /// Error occurred while finalizing the MAC computation. |
| FinalizationError, |
| /// The hardware accelerator is busy and cannot process the MAC computation. |
| Busy, |
| /// General hardware failure during MAC computation. |
| HardwareFailure, |
| /// The specified output size is not valid for the MAC function. |
| InvalidOutputSize, |
| /// Insufficient permissions to access the hardware or perform the MAC computation. |
| PermissionDenied, |
| /// The MAC computation context has not been initialized. |
| NotInitialized, |
| /// MAC verification failed - computed MAC does not match expected value. |
| VerificationFailed, |
| } |
| |
| /// Trait for converting implementation-specific errors into a common error kind. |
| pub trait Error: Debug { |
| /// Returns a generic error kind corresponding to the specific error. |
| fn kind(&self) -> ErrorKind; |
| } |
| |
| impl Error for core::convert::Infallible { |
| fn kind(&self) -> ErrorKind { |
| match *self {} |
| } |
| } |
| |
| /// Trait for types that associate with a specific error type. |
| pub trait ErrorType { |
| /// The associated error type. |
| type Error: Error; |
| } |
| |
| /// Trait representing a MAC algorithm and its output characteristics. |
| pub trait MacAlgorithm: Copy + Debug { |
| /// The number of bits in the MAC output. |
| const OUTPUT_BITS: usize; |
| |
| /// The type representing the MAC output. |
| type MacOutput: IntoBytes; |
| } |
| |
| /// Trait for key handles - implementation determines security model. |
| /// |
| /// This is a marker trait with no methods, which means there's no way to extract |
| /// raw key bytes through this interface. Different implementations can provide |
| /// different security properties: |
| /// - Software keys may contain raw bytes internally |
| /// - Hardware keys may be opaque handles to secure storage |
| /// - TPM keys may be persistent handle references |
| pub trait KeyHandle { |
| // Marker trait - no methods means no way to extract raw bytes |
| // Different implementations can have different security properties |
| } |
| |
| /// Trait for initializing a MAC operation for a specific algorithm. |
| /// |
| /// This trait is generic over the key type, allowing different implementations |
| /// to accept different kinds of keys with varying security properties. |
| pub trait MacInit<A: MacAlgorithm>: ErrorType { |
| /// The key type this implementation accepts. |
| /// |
| /// Each implementation specifies what kind of keys it can work with: |
| /// - Software implementations might use `SecureKey<N>` |
| /// - Hardware implementations might use `HardwareKeySlot` |
| /// - TPM implementations might use `TpmKeyHandle` |
| type Key: KeyHandle; |
| |
| /// The type representing the operational context for the MAC. |
| type OpContext<'a>: MacOp<Output = A::MacOutput> |
| where |
| Self: 'a; |
| |
| /// Initializes the MAC operation with the specified algorithm and key. |
| /// |
| /// # Parameters |
| /// |
| /// - `algo`: A zero-sized type representing the MAC algorithm to use. |
| /// - `key`: The key handle. The type is determined by the implementation's |
| /// security model and requirements. |
| /// |
| /// # Returns |
| /// |
| /// A result containing the operational context for the MAC, or an error. |
| /// |
| /// # Security |
| /// |
| /// The key parameter's type determines the security properties: |
| /// - Software keys may expose raw bytes to the implementation |
| /// - Hardware keys remain opaque and never expose raw material |
| fn init<'a>(&'a mut self, algo: A, key: Self::Key) -> Result<Self::OpContext<'a>, Self::Error>; |
| } |
| |
| /// Optional trait for resetting a MAC context to its initial state. |
| pub trait MacCtrlReset: ErrorType { |
| /// Resets the MAC context. |
| /// |
| /// # Returns |
| /// |
| /// A result indicating success or failure. |
| fn reset(&mut self) -> Result<(), Self::Error>; |
| } |
| |
| /// Trait for performing MAC operations. |
| pub trait MacOp: ErrorType { |
| /// The type of the MAC output. |
| type Output: IntoBytes; |
| |
| /// Updates the MAC state with the provided input data. |
| /// |
| /// # Parameters |
| /// |
| /// - `input`: A byte slice containing the data to authenticate. |
| /// |
| /// # Returns |
| /// |
| /// A result indicating success or failure. |
| fn update(&mut self, input: &[u8]) -> Result<(), Self::Error>; |
| |
| /// Finalizes the MAC computation and returns the result. |
| /// |
| /// # Returns |
| /// |
| /// A result containing the MAC output, or an error. |
| fn finalize(self) -> Result<Self::Output, Self::Error>; |
| } |
| |
| /// Utility function for constant-time MAC verification. |
| /// |
| /// This function provides a secure way to verify MAC values using constant-time |
| /// comparison to prevent timing attacks. |
| /// |
| /// # Parameters |
| /// |
| /// - `computed_mac`: The computed MAC bytes |
| /// - `expected_mac`: The expected MAC bytes to verify against |
| /// |
| /// # Returns |
| /// |
| /// `true` if the MACs match, `false` otherwise |
| /// |
| /// # Security |
| /// |
| /// This function uses constant-time comparison to prevent timing attacks |
| /// that could reveal information about the expected MAC value. |
| pub fn verify_mac_constant_time(computed_mac: &[u8], expected_mac: &[u8]) -> bool { |
| if computed_mac.len() != expected_mac.len() { |
| return false; |
| } |
| computed_mac.ct_eq(expected_mac).into() |
| } |
| |
| // ============================================================================= |
| // MAC Algorithm Marker Types |
| // ============================================================================= |
| |
| /// HMAC-SHA-256 MAC algorithm marker type. |
| /// |
| /// This zero-sized type represents the HMAC-SHA-256 message authentication code |
| /// algorithm, which produces a 256-bit (32-byte) MAC output using SHA-256 as |
| /// the underlying hash function. |
| /// |
| /// HMAC-SHA-256 combines the SHA-256 hash function with a secret key to provide |
| /// both data integrity and authentication. |
| #[derive(Clone, Copy, Debug)] |
| pub struct HmacSha2_256; |
| impl MacAlgorithm for HmacSha2_256 { |
| const OUTPUT_BITS: usize = 256; |
| type MacOutput = Digest<{ Self::OUTPUT_BITS / 32 }>; |
| } |
| |
| /// HMAC-SHA-384 MAC algorithm marker type. |
| /// |
| /// This zero-sized type represents the HMAC-SHA-384 message authentication code |
| /// algorithm, which produces a 384-bit (48-byte) MAC output using SHA-384 as |
| /// the underlying hash function. |
| /// |
| /// HMAC-SHA-384 provides a larger output size than HMAC-SHA-256 for applications |
| /// requiring additional security margin. |
| #[derive(Clone, Copy, Debug)] |
| pub struct HmacSha2_384; |
| impl MacAlgorithm for HmacSha2_384 { |
| const OUTPUT_BITS: usize = 384; |
| type MacOutput = Digest<{ Self::OUTPUT_BITS / 32 }>; |
| } |
| |
| /// HMAC-SHA-512 MAC algorithm marker type. |
| /// |
| /// This zero-sized type represents the HMAC-SHA-512 message authentication code |
| /// algorithm, which produces a 512-bit (64-byte) MAC output using SHA-512 as |
| /// the underlying hash function. |
| /// |
| /// HMAC-SHA-512 provides the largest standard output size, offering maximum |
| /// collision resistance and authentication strength. |
| #[derive(Clone, Copy, Debug)] |
| pub struct HmacSha2_512; |
| impl MacAlgorithm for HmacSha2_512 { |
| const OUTPUT_BITS: usize = 512; |
| type MacOutput = Digest<{ Self::OUTPUT_BITS / 32 }>; |
| } |
| |
| /// Computes a MAC using a key retrieved from a key vault. |
| /// |
| /// This function provides integrated MAC computation with secure key storage, |
| /// ensuring that MAC keys are retrieved from a secure vault and used for |
| /// authentication operations without exposing the key material. |
| /// |
| /// # Parameters |
| /// - `mac_impl`: The MAC implementation to use |
| /// - `vault`: The key vault containing the MAC key |
| /// - `key_id`: Unique identifier for the key in the vault |
| /// - `algorithm`: The MAC algorithm to use (zero-sized type) |
| /// - `data`: The data to authenticate |
| /// |
| /// # Returns |
| /// The computed MAC output |
| /// |
| /// # Security Notes |
| /// - MAC key is retrieved securely from vault |
| /// - Key material is only exposed to the MAC implementation during initialization |
| /// - Key retrieval and MAC computation are atomic |
| /// - Supports vault access control and locking mechanisms |
| /// - Automatic key zeroization after use (vault dependent) |
| /// |
| /// # Example |
| /// ```rust,ignore |
| /// use openprot_hal_blocking::mac::{compute_mac_with_vault, HmacSha2_256, verify_mac_constant_time}; |
| /// use openprot_hal_blocking::key_vault::{KeyLifecycle}; |
| /// |
| /// let mut mac_impl = MyMacImplementation::new(); |
| /// let vault = MyKeyVault::new(); |
| /// let data = b"Hello, world!"; |
| /// |
| /// // Compute MAC with vault-stored key |
| /// let mac_output = compute_mac_with_vault( |
| /// &mut mac_impl, |
| /// &vault, |
| /// KeyId::new(42), |
| /// HmacSha2_256, |
| /// data |
| /// )?; |
| /// |
| /// ``` |
| /// Computes a MAC using a key retrieved from a key vault. |
| /// |
| /// This function provides integrated MAC computation with secure key storage, |
| /// ensuring that MAC keys are retrieved from a secure vault and used for |
| /// authentication operations. The security properties depend on the vault |
| /// and MAC implementation types. |
| /// |
| /// # Type Parameters |
| /// - `A`: The MAC algorithm type |
| /// - `M`: The MAC implementation type |
| /// - `V`: The key vault type |
| /// - `E`: The unified error type |
| /// |
| /// # Parameters |
| /// - `mac_impl`: The MAC implementation to use |
| /// - `vault`: The key vault containing the MAC key |
| /// - `key_id`: Unique identifier for the key in the vault |
| /// - `algorithm`: The MAC algorithm to use (zero-sized type) |
| /// - `data`: The data to authenticate |
| /// |
| /// # Returns |
| /// The computed MAC output |
| /// |
| /// # Security Notes |
| /// - MAC key is retrieved securely from vault |
| /// - Key exposure depends on implementation types: |
| /// - Software implementations may see raw key bytes during initialization |
| /// - Hardware implementations may keep keys opaque throughout |
| /// - Key retrieval and MAC computation are atomic |
| /// - Supports vault access control and locking mechanisms |
| /// - Automatic key zeroization after use (vault dependent) |
| /// |
| /// # Example |
| /// ```rust,ignore |
| /// use openprot_hal_blocking::mac::{compute_mac_with_vault_generic, HmacSha2_256}; |
| /// use openprot_hal_blocking::key_vault::{KeyVault}; |
| /// |
| /// let mut mac_impl = MyMacImplementation::new(); |
| /// let vault = MyKeyVault::new(); |
| /// let data = b"Hello, world!"; |
| /// |
| /// // Compute MAC with vault-stored key |
| /// let mac_output = compute_mac_with_vault_generic( |
| /// &mut mac_impl, |
| /// &vault, |
| /// KeyId::new(42), |
| /// HmacSha2_256, |
| /// data |
| /// )?; |
| /// ``` |
| pub fn compute_mac_with_vault_generic<A, M, V, E>( |
| mac_impl: &mut M, |
| vault: &V, |
| key_id: V::KeyId, |
| algorithm: A, |
| data: &[u8], |
| ) -> Result<A::MacOutput, E> |
| where |
| A: MacAlgorithm, |
| M: MacInit<A, Key = V::Key>, // MAC impl must accept vault's key type |
| V: KeyVault, |
| E: From<M::Error> + From<V::Error>, |
| for<'a> <M::OpContext<'a> as ErrorType>::Error: Into<E>, |
| { |
| // Retrieve key from vault |
| let key = vault.retrieve_key(key_id).map_err(E::from)?; |
| |
| // Initialize MAC operation with key handle |
| let mut mac_ctx = mac_impl.init(algorithm, key).map_err(E::from)?; |
| |
| // Update with data |
| mac_ctx.update(data).map_err(Into::into)?; |
| |
| // Finalize and return MAC |
| mac_ctx.finalize().map_err(Into::into) |
| } |
| |
| /// Trait for key vaults that can provide different key types. |
| /// |
| /// This trait abstracts over different key storage mechanisms, allowing |
| /// implementations to provide different security models through the type system. |
| pub trait KeyVault { |
| /// The type used to identify keys in this vault. |
| type KeyId; |
| |
| /// The type of keys this vault provides. |
| /// |
| /// This determines the security properties: |
| /// - Software vaults might provide `SecureKey<N>` |
| /// - Hardware vaults might provide opaque `HardwareKeySlot` |
| /// - TPM vaults might provide `TpmKeyHandle` |
| type Key: KeyHandle; |
| |
| /// The error type for vault operations. |
| type Error; |
| |
| /// Retrieve a key from the vault. |
| /// |
| /// # Parameters |
| /// - `key_id`: The identifier for the key to retrieve |
| /// |
| /// # Returns |
| /// The key handle, or an error if the key cannot be retrieved |
| fn retrieve_key(&self, key_id: Self::KeyId) -> Result<Self::Key, Self::Error>; |
| } |
| |
| pub mod scoped { |
| //! Scoped MAC API with borrowed contexts (legacy) |
| //! |
| //! This module provides the traditional scoped MAC API where contexts are borrowed |
| //! and have lifetime constraints. This API is suitable for simple embedded applications |
| //! and direct hardware mapping. |
| //! |
| //! This module re-exports the traditional MAC traits that use borrowed contexts |
| //! with lifetime constraints. These traits are suitable for embedded applications |
| //! where MAC contexts cannot be stored in structs due to lifetime constraints. |
| |
| pub use super::{ErrorType, MacAlgorithm, MacCtrlReset, MacInit, MacOp}; |
| } |
| |
| pub mod owned { |
| //! Owned MAC API with move-based resource management |
| //! |
| //! This module provides a move-based MAC API where contexts are owned |
| //! rather than borrowed. This enables: |
| //! - Persistent session storage |
| //! - Multiple concurrent contexts |
| //! - IPC boundary crossing |
| //! - Resource recovery patterns |
| //! - Compile-time prevention of use-after-finalize |
| //! |
| //! This API is specifically designed for server applications like Hubris |
| //! MAC servers that need to maintain long-lived sessions. |
| |
| use super::{ErrorType, IntoBytes, KeyHandle, MacAlgorithm}; |
| use core::result::Result; |
| |
| /// Trait for initializing MAC operations with owned contexts. |
| /// |
| /// This trait takes ownership of the controller and returns an owned context |
| /// that can be stored, moved, and persisted across function boundaries. |
| /// Unlike the scoped API, there are no lifetime constraints. |
| /// |
| /// Like the scoped API, this trait is generic over the key type, allowing |
| /// different implementations to accept different kinds of keys with varying |
| /// security properties. |
| /// |
| /// # Type Parameters |
| /// |
| /// * `A` - The MAC algorithm type that implements [`MacAlgorithm`] |
| /// |
| /// # Examples |
| /// |
| /// ```rust,no_run |
| /// # use openprot_hal_blocking::mac::*; |
| /// # use openprot_hal_blocking::mac::owned::{MacInit, MacOp}; |
| /// # struct MyController; |
| /// # impl ErrorType for MyController { type Error = core::convert::Infallible; } |
| /// # struct MyContext; |
| /// # impl ErrorType for MyContext { type Error = core::convert::Infallible; } |
| /// # #[derive(Debug, Clone)] |
| /// # struct MyKey([u8; 32]); |
| /// # impl KeyHandle for MyKey {} |
| /// # impl MacOp for MyContext { |
| /// # type Output = [u8; 32]; |
| /// # type Controller = MyController; |
| /// # fn update(self, _: &[u8]) -> Result<Self, Self::Error> { Ok(self) } |
| /// # fn finalize(self) -> Result<(Self::Output, Self::Controller), Self::Error> { todo!() } |
| /// # fn cancel(self) -> Self::Controller { todo!() } |
| /// # } |
| /// # impl MacInit<HmacSha2_256> for MyController { |
| /// # type Key = MyKey; |
| /// # type Context = MyContext; |
| /// # type Output = [u8; 32]; |
| /// # fn init(self, _: HmacSha2_256, _key: MyKey) -> Result<Self::Context, Self::Error> { todo!() } |
| /// # } |
| /// let controller = MyController; |
| /// let key = MyKey([0u8; 32]); // Key handle type determined by implementation |
| /// let context = controller.init(HmacSha2_256, key)?; |
| /// // Context can be stored in structs, moved across functions, etc. |
| /// # Ok::<(), core::convert::Infallible>(()) |
| /// ``` |
| pub trait MacInit<A: MacAlgorithm>: ErrorType + Sized { |
| /// The key type this implementation accepts. |
| /// |
| /// Each implementation specifies what kind of keys it can work with: |
| /// - Software implementations might use `SecureKey<N>` |
| /// - Hardware implementations might use `HardwareKeySlot` |
| /// - TPM implementations might use `TpmKeyHandle` |
| type Key: KeyHandle; |
| |
| /// The owned context type that will handle the MAC computation. |
| /// |
| /// This context has no lifetime constraints and can be stored in structs, |
| /// moved between functions, and persisted across IPC boundaries. |
| type Context: MacOp<Output = Self::Output, Controller = Self>; |
| |
| /// The output type produced by this MAC implementation. |
| /// |
| /// This type must implement [`IntoBytes`] to allow conversion to byte arrays |
| /// for interoperability with other systems and zero-copy operations. |
| type Output: IntoBytes; |
| |
| /// Initialize a new MAC computation context. |
| /// |
| /// Takes ownership of the controller and returns an owned context. |
| /// The controller will be returned when the context is finalized or cancelled. |
| /// |
| /// # Parameters |
| /// |
| /// - `algorithm`: Algorithm-specific initialization parameters |
| /// - `key`: The key handle. The type is determined by the implementation's |
| /// security model and requirements. |
| /// |
| /// # Returns |
| /// |
| /// An owned context that can be used for MAC operations. |
| /// |
| /// # Security |
| /// |
| /// The key parameter's type determines the security properties: |
| /// - Software keys may expose raw bytes to the implementation |
| /// - Hardware keys remain opaque and never expose raw material |
| fn init(self, algorithm: A, key: Self::Key) -> Result<Self::Context, Self::Error>; |
| } |
| |
| /// Trait for performing MAC operations with owned contexts. |
| /// |
| /// This trait uses move semantics where each operation consumes the |
| /// context and returns a new context (for `update`) or the final result |
| /// with a recovered controller (for `finalize`/`cancel`). |
| /// |
| /// # Move-based Safety |
| /// |
| /// The move-based pattern provides compile-time guarantees: |
| /// - Cannot use a context after finalization |
| /// - Cannot finalize the same context twice |
| /// - Controller is always recovered for reuse |
| /// |
| /// # Examples |
| /// |
| /// ```rust,no_run |
| /// # use openprot_hal_blocking::mac::*; |
| /// # use openprot_hal_blocking::mac::owned::{MacInit, MacOp}; |
| /// # struct MyContext; |
| /// # impl ErrorType for MyContext { type Error = core::convert::Infallible; } |
| /// # struct MyController; |
| /// # impl MacOp for MyContext { |
| /// # type Output = [u8; 32]; |
| /// # type Controller = MyController; |
| /// # fn update(self, _: &[u8]) -> Result<Self, Self::Error> { Ok(self) } |
| /// # fn finalize(self) -> Result<(Self::Output, Self::Controller), Self::Error> { todo!() } |
| /// # fn cancel(self) -> Self::Controller { todo!() } |
| /// # } |
| /// # fn get_context() -> MyContext { todo!() } |
| /// let context = get_context(); // MyContext |
| /// let context = context.update(b"hello")?; |
| /// let context = context.update(b" world")?; |
| /// let (mac_output, controller) = context.finalize()?; |
| /// // Controller recovered for reuse |
| /// # Ok::<(), core::convert::Infallible>(()) |
| /// ``` |
| pub trait MacOp: ErrorType + Sized { |
| /// The MAC output type. |
| /// |
| /// This type represents the final MAC value produced by [`finalize`](Self::finalize). |
| /// It must implement [`IntoBytes`] to enable zero-copy conversion to byte arrays. |
| type Output: IntoBytes; |
| |
| /// The controller type that will be recovered after finalization or cancellation. |
| /// |
| /// This enables resource recovery and reuse patterns essential for server applications. |
| type Controller; |
| |
| /// Update the MAC state with input data. |
| /// |
| /// This method consumes the current context and returns a new context with |
| /// the updated state. This prevents use-after-update bugs at compile time |
| /// through move semantics. |
| /// |
| /// # Parameters |
| /// |
| /// - `data`: Input data to be authenticated by the MAC algorithm |
| /// |
| /// # Returns |
| /// |
| /// A new context with updated state, or an error |
| fn update(self, data: &[u8]) -> Result<Self, Self::Error>; |
| |
| /// Finalize the MAC computation and recover the controller. |
| /// |
| /// This method consumes the context and returns both the final MAC output |
| /// and the original controller, enabling resource reuse. |
| /// |
| /// # Returns |
| /// |
| /// A tuple containing the MAC output and the recovered controller |
| fn finalize(self) -> Result<(Self::Output, Self::Controller), Self::Error>; |
| |
| /// Cancel the MAC computation and recover the controller. |
| /// |
| /// This method cancels the current computation and returns the controller |
| /// in a clean state, ready for reuse. Unlike `finalize`, this cannot fail. |
| /// |
| /// # Returns |
| /// |
| /// The recovered controller in a clean state |
| fn cancel(self) -> Self::Controller; |
| } |
| } |
| |
| /// Computes a MAC using an owned controller and a key retrieved from a key vault. |
| /// |
| /// This function provides integrated MAC computation with secure key storage using |
| /// the owned API pattern. It enables server applications to perform MAC operations |
| /// with vault-stored keys while maintaining resource recovery patterns. |
| /// |
| /// # Type Parameters |
| /// |
| /// * `A` - The MAC algorithm type |
| /// * `C` - The owned MAC controller type |
| /// * `V` - The key vault type |
| /// * `E` - The unified error type |
| /// |
| /// # Parameters |
| /// |
| /// * `controller` - The owned MAC controller (consumed by this function) |
| /// * `vault` - Reference to the key vault |
| /// * `key_id` - Identifier for the key to retrieve from the vault |
| /// * `algorithm` - The MAC algorithm to use |
| /// * `data` - The data to authenticate |
| /// |
| /// # Returns |
| /// |
| /// A tuple containing the MAC output and the recovered controller, or an error. |
| /// |
| /// # Examples |
| /// |
| /// ```rust,ignore |
| /// use openprot_hal_blocking::mac::{compute_mac_with_vault_owned, HmacSha2_256}; |
| /// use openprot_hal_blocking::mac::owned::{MacInit, MacOp}; |
| /// use openprot_hal_blocking::key_vault::KeyLifecycle; |
| /// |
| /// let controller = MyOwnedMacController::new(); |
| /// let vault = MyKeyVault::new(); |
| /// let data = b"Hello, world!"; |
| /// |
| /// // Compute MAC with vault-stored key using owned API |
| /// let (mac_output, recovered_controller) = compute_mac_with_vault_owned( |
| /// controller, |
| /// &vault, |
| /// KeyId::new(42), |
| /// HmacSha2_256, |
| /// data |
| /// )?; |
| /// |
| /// // Reuse the recovered controller for next operation |
| /// let (next_mac, _) = compute_mac_with_vault_owned( |
| /// recovered_controller, |
| /// &vault, |
| /// KeyId::new(43), |
| /// HmacSha2_256, |
| /// b"Next message" |
| /// )?; |
| /// ``` |
| /// Computes a MAC using an owned controller and a key retrieved from a key vault. |
| /// |
| /// This function provides integrated MAC computation with secure key storage using |
| /// the owned API pattern. It enables server applications to perform MAC operations |
| /// with vault-stored keys while maintaining resource recovery patterns. |
| /// |
| /// # Type Parameters |
| /// |
| /// * `A` - The MAC algorithm type |
| /// * `C` - The owned MAC controller type |
| /// * `V` - The key vault type |
| /// * `E` - The unified error type |
| /// |
| /// # Parameters |
| /// |
| /// * `controller` - The owned MAC controller (consumed by this function) |
| /// * `vault` - Reference to the key vault |
| /// * `key_id` - Identifier for the key to retrieve from the vault |
| /// * `algorithm` - The MAC algorithm to use |
| /// * `data` - The data to authenticate |
| /// |
| /// # Returns |
| /// |
| /// A tuple containing the MAC output and the recovered controller, or an error. |
| /// |
| /// # Examples |
| /// |
| /// ```rust,ignore |
| /// use openprot_hal_blocking::mac::{compute_mac_with_vault_owned_generic, HmacSha2_256}; |
| /// use openprot_hal_blocking::mac::owned::{MacInit, MacOp}; |
| /// use openprot_hal_blocking::mac::KeyVault; |
| /// |
| /// let controller = MyOwnedMacController::new(); |
| /// let vault = MyKeyVault::new(); |
| /// let data = b"Hello, world!"; |
| /// |
| /// // Compute MAC with vault-stored key using owned API |
| /// let (mac_output, recovered_controller) = compute_mac_with_vault_owned_generic( |
| /// controller, |
| /// &vault, |
| /// KeyId::new(42), |
| /// HmacSha2_256, |
| /// data |
| /// )?; |
| /// |
| /// // Reuse the recovered controller for next operation |
| /// let (next_mac, _) = compute_mac_with_vault_owned_generic( |
| /// recovered_controller, |
| /// &vault, |
| /// KeyId::new(43), |
| /// HmacSha2_256, |
| /// b"Next message" |
| /// )?; |
| /// ``` |
| pub fn compute_mac_with_vault_owned_generic<A, C, V, E>( |
| controller: C, |
| vault: &V, |
| key_id: V::KeyId, |
| algorithm: A, |
| data: &[u8], |
| ) -> Result<(A::MacOutput, C), E> |
| where |
| A: MacAlgorithm, |
| C: owned::MacInit<A, Key = V::Key, Output = A::MacOutput>, |
| C::Context: owned::MacOp<Output = A::MacOutput, Controller = C>, |
| V: KeyVault, |
| E: From<C::Error> + From<V::Error>, |
| E: From<<C::Context as ErrorType>::Error>, |
| { |
| use owned::MacOp; // Bring trait into scope |
| |
| // Retrieve key from vault |
| let key = vault.retrieve_key(key_id).map_err(E::from)?; |
| |
| // Initialize MAC operation with key handle |
| let context = controller.init(algorithm, key).map_err(E::from)?; |
| |
| // Update with data |
| let context = context.update(data).map_err(E::from)?; |
| |
| // Finalize and return MAC with recovered controller |
| context.finalize().map_err(E::from) |
| } |
| |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| |
| #[test] |
| fn test_mac_algorithm_traits() { |
| // Test that MAC algorithm types implement the correct traits |
| let _sha256 = HmacSha2_256; |
| let _sha384 = HmacSha2_384; |
| let _sha512 = HmacSha2_512; |
| |
| // Test output sizes (in bits) |
| assert_eq!(<HmacSha2_256 as MacAlgorithm>::OUTPUT_BITS, 256); |
| assert_eq!(<HmacSha2_384 as MacAlgorithm>::OUTPUT_BITS, 384); |
| assert_eq!(<HmacSha2_512 as MacAlgorithm>::OUTPUT_BITS, 512); |
| } |
| |
| #[test] |
| fn test_secure_key_creation() { |
| let key_bytes = [0u8; 32]; |
| let secure_key = SecureKey::new(key_bytes); |
| assert_eq!(secure_key.as_bytes().len(), 32); |
| } |
| |
| #[test] |
| fn test_secure_key_from_slice() { |
| let key_slice = &[0u8; 32][..]; |
| let result = SecureKey::<32>::from_slice(key_slice); |
| assert!(result.is_ok()); |
| |
| // Test wrong size |
| let wrong_size = &[0u8; 16][..]; |
| let result = SecureKey::<32>::from_slice(wrong_size); |
| assert!(matches!(result, Err(ErrorKind::InvalidInputLength))); |
| } |
| |
| #[test] |
| fn test_secure_key_constant_time_eq() { |
| let key1 = SecureKey::new([1u8; 32]); |
| let key2 = SecureKey::new([1u8; 32]); |
| let key3 = SecureKey::new([2u8; 32]); |
| |
| assert_eq!(key1, key2); |
| assert_ne!(key1, key3); |
| } |
| |
| #[test] |
| fn test_secure_key_verify_mac() { |
| let key = SecureKey::new([0u8; 32]); |
| let mac1 = [1, 2, 3, 4]; |
| let mac2 = [1, 2, 3, 4]; |
| let mac3 = [1, 2, 3, 5]; |
| |
| assert!(key.verify_mac(&mac1, &mac2)); |
| assert!(!key.verify_mac(&mac1, &mac3)); |
| |
| // Different lengths should return false |
| let mac_short = [1, 2, 3]; |
| assert!(!key.verify_mac(&mac1, &mac_short)); |
| } |
| |
| #[test] |
| fn test_secure_key_as_ref() { |
| let key = SecureKey::new([1, 2, 3, 4, 5, 6, 7, 8]); |
| let bytes: &[u8] = key.as_ref(); |
| assert_eq!(bytes, &[1, 2, 3, 4, 5, 6, 7, 8]); |
| } |
| |
| #[test] |
| fn test_flexible_key_inputs() { |
| // Test that different key types can be used via AsRef<[u8]> |
| |
| // Array reference |
| let array_key = [0u8; 32]; |
| let _array_ref: &[u8] = array_key.as_ref(); |
| |
| // SecureKey |
| let secure_key = SecureKey::new([0u8; 32]); |
| let _secure_ref: &[u8] = secure_key.as_ref(); |
| |
| // Slice |
| let slice_key: &[u8] = &[0u8; 32]; |
| let _slice_ref: &[u8] = slice_key; |
| |
| // All should be compatible with the new API |
| assert_eq!(_array_ref.len(), 32); |
| assert_eq!(_secure_ref.len(), 32); |
| assert_eq!(_slice_ref.len(), 32); |
| } |
| } |