blob: 4b1806f7a8ddb5209cc593366510033054c9dc1d [file]
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0
//! Hubris Platform Integration Traits for OpenPRoT
//!
//! This crate provides Hubris-specific integration traits that bridge the gap
//! between OpenPRoT's generic HAL traits and Hubris IDL code generation requirements.
//!
//! The traits use concrete types instead of generic associated types to ensure
//! compatibility with Hubris IDL code generation systems.
#![no_std]
#![forbid(unsafe_code)]
#![deny(missing_docs)]
use core::convert::TryFrom;
use core::result::Result;
use openprot_hal_blocking::{
digest::{owned::DigestOp, Digest},
mac::owned::MacOp,
};
/// Hubris-specific cryptographic error types
///
/// These errors are designed to be IDL-compatible and map to standard
/// Hubris IPC error codes for inter-task communication.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum HubrisCryptoError {
/// Invalid key length for the requested operation
InvalidKeyLength,
/// Hardware crypto accelerator failure
HardwareFailure,
/// Invalid algorithm parameters
InvalidParameters,
/// Resource temporarily unavailable (e.g., crypto unit busy)
ResourceBusy,
/// Operation not supported by hardware
NotSupported,
/// Session state error (context or device missing)
SessionStateError,
}
/// RAII-style crypto session management
///
/// This type provides RAII-style resource management for crypto devices used in
/// session-based operations. It ensures that crypto hardware devices are properly
/// recovered and can be reused after operations complete.
///
/// The CryptoSession handles the fundamental constraint that hardware controllers
/// cannot be cloned, providing a mechanism for device recovery that works with
/// both software implementations (which can create new instances) and hardware
/// controllers (which require different recovery strategies).
///
/// ## Design Rationale
///
/// Session-based crypto operations consume the device when creating contexts,
/// but we need to recover the device for reuse. This type solves that by:
/// 1. Storing both the crypto context and a recovery device
/// 2. Providing update/finalize methods that manage the context lifecycle
/// 3. Returning the recovered device when the session completes
///
/// ## Usage Pattern
///
/// ```rust,ignore
/// let session = device.init_digest_session_sha256()?;
/// let session = session.update(b"data")?;
/// let (result, recovered_device) = session.finalize()?;
/// // recovered_device can now be used for new operations
/// ```
pub struct CryptoSession<Context, Device> {
context: Option<Context>,
device: Option<Device>,
}
impl<Context, Device> CryptoSession<Context, Device> {
/// Create a new crypto session with the given context and recovery device
pub fn new(context: Context, device: Device) -> Self {
Self {
context: Some(context),
device: Some(device),
}
}
/// Take the context out of the session (for move operations)
fn take_context(&mut self) -> Result<Context, HubrisCryptoError> {
self.context
.take()
.ok_or(HubrisCryptoError::SessionStateError)
}
/// Put a context back into the session (after move operations)
fn put_context(&mut self, context: Context) {
self.context = Some(context);
}
/// Update the session with new data
///
/// This method uses move semantics to update the context while maintaining
/// the device for recovery.
pub fn update(mut self, data: &[u8]) -> Result<Self, HubrisCryptoError>
where
Context: DigestOp<Controller = Device>,
{
let context = self.take_context()?;
let updated_context = context
.update(data)
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
self.put_context(updated_context);
Ok(self)
}
/// Finalize the digest session and recover the device
///
/// Returns the computed result and the device for reuse.
pub fn finalize(mut self) -> Result<(Context::Output, Device), HubrisCryptoError>
where
Context: DigestOp<Controller = Device>,
{
let context = self.take_context()?;
let (output, _controller) = context
.finalize()
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
// Recover our device from the session
let device = self
.device
.take()
.ok_or(HubrisCryptoError::SessionStateError)?;
Ok((output, device))
}
/// Update the MAC session with new data
pub fn update_mac(mut self, data: &[u8]) -> Result<Self, HubrisCryptoError>
where
Context: MacOp<Controller = Device>,
{
let context = self.take_context()?;
let updated_context = context
.update(data)
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
self.put_context(updated_context);
Ok(self)
}
/// Finalize the MAC session and recover the device
pub fn finalize_mac(mut self) -> Result<(Context::Output, Device), HubrisCryptoError>
where
Context: MacOp<Controller = Device>,
{
let context = self.take_context()?;
let (output, _controller) = context
.finalize()
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
// Recover our device from the session
let device = self
.device
.take()
.ok_or(HubrisCryptoError::SessionStateError)?;
Ok((output, device))
}
}
/// Hubrus-specific digest device trait
///
/// This trait is designed specifically for Hubris microkernel integration:
/// - All types are concrete for IDL compatibility
/// - Methods support task isolation semantics
/// - Error types map to Hubris IPC error codes
/// - Resource management aligns with Hubris task model
pub trait HubrisDigestDevice {
/// Digest context for SHA-256 operations
type DigestContext256: DigestOp<Controller = Self, Output = Digest<8>>;
/// Digest context for SHA-384 operations
type DigestContext384: DigestOp<Controller = Self, Output = Digest<12>>;
/// Digest context for SHA-512 operations
type DigestContext512: DigestOp<Controller = Self, Output = Digest<16>>;
/// HMAC key type that can be created from byte slices
/// Must be compatible with Hubris task memory constraints
type HmacKey: for<'a> TryFrom<&'a [u8]>;
/// HMAC context for SHA-256 operations
type HmacContext256: MacOp<Controller = Self, Output = [u8; 32]>;
/// HMAC context for SHA-384 operations
type HmacContext384: MacOp<Controller = Self, Output = [u8; 48]>;
/// HMAC context for SHA-512 operations
type HmacContext512: MacOp<Controller = Self, Output = [u8; 64]>;
/// Maximum supported key size in bytes
/// This aligns with Hubris task memory constraints
const MAX_KEY_SIZE: usize = 128;
/// Initialize a SHA-256 digest operation
///
/// # Hubris Semantics
/// - Consumes the device (move semantics for resource management)
/// - Returns concrete error types compatible with IDL
fn init_digest_sha256(self) -> Result<Self::DigestContext256, HubrisCryptoError>;
/// Initialize a SHA-384 digest operation
fn init_digest_sha384(self) -> Result<Self::DigestContext384, HubrisCryptoError>;
/// Initialize a SHA-512 digest operation
fn init_digest_sha512(self) -> Result<Self::DigestContext512, HubrisCryptoError>;
/// Initialize an HMAC-SHA256 operation with the given key
///
/// # Security Note
/// Key handling follows Hubris security model with task isolation
fn init_hmac_sha256(
self,
key: Self::HmacKey,
) -> Result<Self::HmacContext256, HubrisCryptoError>;
/// Initialize an HMAC-SHA384 operation with the given key
fn init_hmac_sha384(
self,
key: Self::HmacKey,
) -> Result<Self::HmacContext384, HubrisCryptoError>;
/// Initialize an HMAC-SHA512 operation with the given key
fn init_hmac_sha512(
self,
key: Self::HmacKey,
) -> Result<Self::HmacContext512, HubrisCryptoError>;
/// Create an HMAC key from a byte slice
///
/// # Hubris Integration
/// - Validates key size against device limits
/// - Ensures compatibility with task memory constraints
fn create_hmac_key(data: &[u8]) -> Result<Self::HmacKey, HubrisCryptoError> {
Self::HmacKey::try_from(data).map_err(|_| HubrisCryptoError::InvalidKeyLength)
}
// Session methods for digest operations
/// Initialize SHA-256 digest session with device recovery
fn init_digest_session_sha256(
self,
) -> Result<CryptoSession<Self::DigestContext256, Self>, HubrisCryptoError>
where
Self: Sized;
/// Initialize SHA-384 digest session with device recovery
fn init_digest_session_sha384(
self,
) -> Result<CryptoSession<Self::DigestContext384, Self>, HubrisCryptoError>
where
Self: Sized;
/// Initialize SHA-512 digest session with device recovery
fn init_digest_session_sha512(
self,
) -> Result<CryptoSession<Self::DigestContext512, Self>, HubrisCryptoError>
where
Self: Sized;
// Session methods for HMAC operations
/// Initialize HMAC-SHA256 session with device recovery
fn init_hmac_session_sha256(
self,
key: Self::HmacKey,
) -> Result<CryptoSession<Self::HmacContext256, Self>, HubrisCryptoError>
where
Self: Sized;
/// Initialize HMAC-SHA384 session with device recovery
fn init_hmac_session_sha384(
self,
key: Self::HmacKey,
) -> Result<CryptoSession<Self::HmacContext384, Self>, HubrisCryptoError>
where
Self: Sized;
/// Initialize HMAC-SHA512 session with device recovery
fn init_hmac_session_sha512(
self,
key: Self::HmacKey,
) -> Result<CryptoSession<Self::HmacContext512, Self>, HubrisCryptoError>
where
Self: Sized;
}
/// Extension trait for one-shot operations in Hubris
///
/// Provides efficient one-shot digest and MAC operations that are common
/// in embedded scenarios and align with Hubris task execution patterns.
pub trait HubrisDigestOneShot: HubrisDigestDevice + Sized {
/// Compute SHA-256 digest in one operation
///
/// Optimized for Hubris task scheduling - completes atomically
fn digest_sha256_oneshot(self, data: &[u8]) -> Result<Digest<8>, HubrisCryptoError> {
let ctx = self.init_digest_sha256()?;
let ctx = ctx
.update(data)
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
let (result, _controller) = ctx
.finalize()
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
Ok(result)
}
/// Compute SHA-384 digest in one operation
fn digest_sha384_oneshot(self, data: &[u8]) -> Result<Digest<12>, HubrisCryptoError> {
let ctx = self.init_digest_sha384()?;
let ctx = ctx
.update(data)
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
let (result, _controller) = ctx
.finalize()
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
Ok(result)
}
/// Compute SHA-512 digest in one operation
fn digest_sha512_oneshot(self, data: &[u8]) -> Result<Digest<16>, HubrisCryptoError> {
let ctx = self.init_digest_sha512()?;
let ctx = ctx
.update(data)
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
let (result, _controller) = ctx
.finalize()
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
Ok(result)
}
/// Compute HMAC-SHA256 in one operation
///
/// # Security
/// Key is zeroized after use following Hubris security practices
fn hmac_sha256_oneshot(self, key: &[u8], data: &[u8]) -> Result<[u8; 32], HubrisCryptoError> {
let key_handle = Self::create_hmac_key(key)?;
let ctx = self.init_hmac_sha256(key_handle)?;
let ctx = ctx
.update(data)
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
let (result, _controller) = ctx
.finalize()
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
Ok(result)
}
/// Compute HMAC-SHA384 in one operation
fn hmac_sha384_oneshot(self, key: &[u8], data: &[u8]) -> Result<[u8; 48], HubrisCryptoError> {
let key_handle = Self::create_hmac_key(key)?;
let ctx = self.init_hmac_sha384(key_handle)?;
let ctx = ctx
.update(data)
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
let (result, _controller) = ctx
.finalize()
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
Ok(result)
}
/// Compute HMAC-SHA512 in one operation
fn hmac_sha512_oneshot(self, key: &[u8], data: &[u8]) -> Result<[u8; 64], HubrisCryptoError> {
let key_handle = Self::create_hmac_key(key)?;
let ctx = self.init_hmac_sha512(key_handle)?;
let ctx = ctx
.update(data)
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
let (result, _controller) = ctx
.finalize()
.map_err(|_| HubrisCryptoError::HardwareFailure)?;
Ok(result)
}
}
// Blanket implementation for one-shot operations
impl<T: HubrisDigestDevice> HubrisDigestOneShot for T {}