blob: 13dba1d3019806b8d40e68bc29cf14e09af924e3 [file]
// Licensed under the Apache-2.0 license
//! SPDM Hash Implementation
//!
//! Provides cryptographic hashing for SPDM protocol operations by
//! delegating to the OpenPRoT crypto service via IPC.
//!
//! ## Architecture
//!
//! This crate implements the `SpdmHash` trait from spdm-lib by wrapping
//! the `CryptoClient` and calling its hash methods. It supports both
//! stateless one-shot hashing and stateful streaming operations.
//!
//! ## Supported Algorithms
//!
//! - **SHA-384** (48-byte output) — Default per SPDM spec
//! - **SHA-512** (64-byte output)
//!
//! ## Usage
//!
//! ### Stateless (One-Shot)
//!
//! ```rust,no_run
//! use openprot_spdm_hash::SpdmCryptoHash;
//! use spdm_lib::platform::hash::{SpdmHash, SpdmHashAlgoType};
//!
//! let mut hasher = SpdmCryptoHash::new(crypto_handle);
//! let mut output = [0u8; 48];
//! hasher.hash(SpdmHashAlgoType::SHA384, b"data", &mut output).unwrap();
//! ```
//!
//! ### Stateful (Streaming)
//!
//! ```rust,no_run
//! use openprot_spdm_hash::SpdmCryptoHash;
//! use spdm_lib::platform::hash::{SpdmHash, SpdmHashAlgoType};
//!
//! let mut hasher = SpdmCryptoHash::new(crypto_handle);
//!
//! // Initialize
//! hasher.init(SpdmHashAlgoType::SHA384, None).unwrap();
//!
//! // Accumulate data
//! hasher.update(b"chunk1").unwrap();
//! hasher.update(b"chunk2").unwrap();
//!
//! // Finalize
//! let mut output = [0u8; 48];
//! hasher.finalize(&mut output).unwrap();
//!
//! // Clean up
//! hasher.reset();
//! ```
#![no_std]
#![warn(missing_docs)]
use crypto_client::{CryptoClient, Sha384Session, Sha512Session};
use spdm_lib::platform::hash::{SpdmHash, SpdmHashAlgoType, SpdmHashError, SpdmHashResult};
/// SPDM hash implementation using OpenPRoT crypto service.
///
/// This struct wraps a `CryptoClient` handle and maintains internal state
/// to support both stateless one-shot hashing and stateful streaming operations.
pub struct SpdmCryptoHash {
crypto: CryptoClient,
state: HashState,
}
/// Internal state tracking for streaming hash operations.
enum HashState {
/// No active hash session
Idle,
/// Active SHA-384 streaming session
Sha384(Sha384Session),
/// Active SHA-512 streaming session
Sha512(Sha512Session),
}
impl SpdmCryptoHash {
/// Create a new SPDM hash implementation using the crypto service.
///
/// # Arguments
///
/// * `crypto_handle` — IPC channel handle for the crypto service
/// (typically from your app's generated handle module, e.g., `handle::CRYPTO`)
pub const fn new(crypto_handle: u32) -> Self {
Self {
crypto: CryptoClient::new(crypto_handle),
state: HashState::Idle,
}
}
}
impl SpdmHash for SpdmCryptoHash {
fn hash(
&mut self,
hash_algo: SpdmHashAlgoType,
data: &[u8],
hash: &mut [u8],
) -> SpdmHashResult<()> {
match hash_algo {
SpdmHashAlgoType::SHA384 => {
if hash.len() < 48 {
return Err(SpdmHashError::BufferTooSmall);
}
let result = self
.crypto
.sha384(data)
.map_err(|_| SpdmHashError::PlatformError)?;
hash[..48].copy_from_slice(&result);
Ok(())
}
SpdmHashAlgoType::SHA512 => {
if hash.len() < 64 {
return Err(SpdmHashError::BufferTooSmall);
}
let result = self
.crypto
.sha512(data)
.map_err(|_| SpdmHashError::PlatformError)?;
hash[..64].copy_from_slice(&result);
Ok(())
}
}
}
fn init(&mut self, hash_algo: SpdmHashAlgoType, data: Option<&[u8]>) -> SpdmHashResult<()> {
// Ensure we're in Idle state before starting a new session
if !matches!(self.state, HashState::Idle) {
return Err(SpdmHashError::PlatformError);
}
// Start the appropriate session
match hash_algo {
SpdmHashAlgoType::SHA384 => {
let session = self
.crypto
.sha384_begin()
.map_err(|_| SpdmHashError::PlatformError)?;
self.state = HashState::Sha384(session);
}
SpdmHashAlgoType::SHA512 => {
let session = self
.crypto
.sha512_begin()
.map_err(|_| SpdmHashError::PlatformError)?;
self.state = HashState::Sha512(session);
}
}
// If initial data was provided, update with it
if let Some(initial_data) = data {
self.update(initial_data)?;
}
Ok(())
}
fn update(&mut self, data: &[u8]) -> SpdmHashResult<()> {
match &mut self.state {
HashState::Idle => Err(SpdmHashError::PlatformError),
HashState::Sha384(session) => session
.update(data)
.map_err(|_| SpdmHashError::PlatformError),
HashState::Sha512(session) => session
.update(data)
.map_err(|_| SpdmHashError::PlatformError),
}
}
fn finalize(&mut self, hash: &mut [u8]) -> SpdmHashResult<()> {
// Take the session out of state, replacing with Idle
let state = core::mem::replace(&mut self.state, HashState::Idle);
match state {
HashState::Idle => Err(SpdmHashError::PlatformError),
HashState::Sha384(session) => {
if hash.len() < 48 {
return Err(SpdmHashError::BufferTooSmall);
}
let result = session
.finalize()
.map_err(|_| SpdmHashError::PlatformError)?;
hash[..48].copy_from_slice(&result);
Ok(())
}
HashState::Sha512(session) => {
if hash.len() < 64 {
return Err(SpdmHashError::BufferTooSmall);
}
let result = session
.finalize()
.map_err(|_| SpdmHashError::PlatformError)?;
hash[..64].copy_from_slice(&result);
Ok(())
}
}
}
fn reset(&mut self) {
// Simply drop the current session and return to Idle
self.state = HashState::Idle;
}
fn algo(&self) -> SpdmHashAlgoType {
match &self.state {
HashState::Idle => SpdmHashAlgoType::SHA384, // Default
HashState::Sha384(_) => SpdmHashAlgoType::SHA384,
HashState::Sha512(_) => SpdmHashAlgoType::SHA512,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_struct_creation() {
let _hasher = SpdmCryptoHash::new(42);
}
#[test]
fn test_default_algo() {
let hasher = SpdmCryptoHash::new(42);
assert_eq!(hasher.algo(), SpdmHashAlgoType::SHA384);
}
#[test]
fn test_reset_idempotent() {
let mut hasher = SpdmCryptoHash::new(42);
hasher.reset();
hasher.reset();
assert_eq!(hasher.algo(), SpdmHashAlgoType::SHA384);
}
}