blob: d22bf9b7ab79a7bbac0d9dae9117477eb55f15eb [file] [log] [blame]
// 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);
}
}