Import of SPDM Requester and Responder services

This imports the requester, responder, and support trait implementations
from the OCP demo.
diff --git a/services/spdm/hash/BUILD.bazel b/services/spdm/hash/BUILD.bazel
new file mode 100644
index 0000000..ce65e90
--- /dev/null
+++ b/services/spdm/hash/BUILD.bazel
@@ -0,0 +1,20 @@
+# Licensed under the Apache-2.0 license
+
+load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+
+rust_library(
+    name = "spdm_hash_lib",
+    srcs = glob(["src/**/*.rs"]),
+    crate_name = "openprot_spdm_hash",
+    edition = "2024",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//services/crypto/client:crypto_client",
+        "@oot_crates_no_std//:spdm-lib",
+    ],
+)
+
+rust_test(
+    name = "spdm_hash_test",
+    crate = ":spdm_hash_lib",
+)
diff --git a/services/spdm/hash/Cargo.toml b/services/spdm/hash/Cargo.toml
new file mode 100644
index 0000000..80b2e8c
--- /dev/null
+++ b/services/spdm/hash/Cargo.toml
@@ -0,0 +1,12 @@
+# Licensed under the Apache-2.0 license
+
+[package]
+name = "openprot-spdm-hash"
+version = "0.1.0"
+edition = "2021"
+description = "SPDM hash implementation using OpenPRoT crypto service"
+license = "Apache-2.0"
+
+[dependencies]
+spdm-lib = { git = "https://github.com/9elements/spdm-lib.git", branch = "buildup" }
+crypto-client = { path = "../../crypto/client" }
diff --git a/services/spdm/hash/README.md b/services/spdm/hash/README.md
new file mode 100644
index 0000000..17119c4
--- /dev/null
+++ b/services/spdm/hash/README.md
@@ -0,0 +1,124 @@
+# SPDM Hash
+
+Hash functions for SPDM protocol operations, implemented via OpenPRoT crypto service.
+
+## Overview
+
+Implements the `SpdmHash` trait from spdm-lib supporting SHA-384 and SHA-512 algorithms. All cryptographic operations are delegated to the centralized crypto service via IPC.
+
+## Architecture
+
+```
+SpdmCryptoHash → CryptoClient → IPC → CryptoServer → RustCryptoBackend → SHA2
+```
+
+## Supported Algorithms
+
+- **SHA-384** (48 bytes) — Default per SPDM spec
+- **SHA-512** (64 bytes)
+
+## Usage Patterns
+
+### Stateless (One-Shot)
+
+For small messages that fit in a single IPC call:
+
+```rust
+use openprot_spdm_hash::SpdmCryptoHash;
+use spdm_lib::platform::hash::{SpdmHash, SpdmHashAlgoType};
+
+let mut hasher = SpdmCryptoHash::new(handle::CRYPTO);
+let mut output = [0u8; 48];
+hasher.hash(SpdmHashAlgoType::SHA384, b"data to hash", &mut output)?;
+```
+
+### Stateful (Streaming)
+
+For large messages or data that arrives in chunks:
+
+```rust
+let mut hasher = SpdmCryptoHash::new(handle::CRYPTO);
+
+// Initialize
+hasher.init(SpdmHashAlgoType::SHA384, None)?;
+
+// Accumulate data
+hasher.update(chunk1)?;
+hasher.update(chunk2)?;
+hasher.update(chunk3)?;
+
+// Finalize
+let mut output = [0u8; 48];
+hasher.finalize(&mut output)?;
+
+// Clean up for next use
+hasher.reset();
+```
+
+### With Initial Data
+
+The `init()` method supports providing initial data:
+
+```rust
+// Initialize with VCA (Version/Capabilities/Algorithms) data
+hasher.init(SpdmHashAlgoType::SHA384, Some(vca_buffer))?;
+
+// Then add additional messages
+hasher.update(request_data)?;
+hasher.update(response_data)?;
+
+// Finalize
+hasher.finalize(&mut output)?;
+```
+
+## SPDM Use Cases
+
+This implementation is used by spdm-lib for:
+
+- **Transcript Hashing**: M1 and L1 transcript hashes for CHALLENGE and MEASUREMENTS
+- **Signature Context**: Hash of signing context for CHALLENGE responses
+- **Measurement Summaries**: Hashing measurement blocks for attestation
+- **Certificate Verification**: Hashing certificate chains
+
+## Dependencies
+
+- `spdm-lib` — SPDM protocol library (https://github.com/9elements/spdm-lib.git, branch: buildup)
+- `crypto-client` — OpenPRoT crypto service client
+
+## State Management
+
+The implementation maintains internal state to support streaming operations:
+
+- **Idle**: No active session
+- **SHA-384 Session**: Active SHA-384 streaming hash
+- **SHA-512 Session**: Active SHA-512 streaming hash
+
+State transitions:
+- `init()` → Creates session (Idle → Sha384/Sha512)
+- `update()` → Feeds data (stays in current session)
+- `finalize()` → Completes hash (Sha384/Sha512 → Idle)
+- `reset()` → Aborts session (Any → Idle)
+- `hash()` → Operates independently of state
+
+## Performance
+
+- **One-shot operations**: Single IPC round-trip (~10-50μs)
+- **Streaming operations**: One IPC call per begin/update/finish
+- **Maximum one-shot size**: ~900 bytes (IPC buffer limit)
+- **Streaming advantage**: Can handle arbitrarily large data
+
+## Security
+
+- **Algorithms**: FIPS 180-4 compliant SHA-384/512 via RustCrypto
+- **Constant-time**: RustCrypto implementations aim for constant-time where feasible
+- **Trust boundary**: Assumes crypto service is trusted
+- **IPC protection**: Relies on kernel IPC channel security
+
+## Future Enhancements
+
+- Hardware acceleration via AST1060 HACE (transparent when backend is upgraded)
+- Additional hash algorithms if required by future SPDM versions
+
+## License
+
+Apache-2.0
diff --git a/services/spdm/hash/src/lib.rs b/services/spdm/hash/src/lib.rs
new file mode 100644
index 0000000..13dba1d
--- /dev/null
+++ b/services/spdm/hash/src/lib.rs
@@ -0,0 +1,237 @@
+// 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);
+    }
+}
diff --git a/services/spdm/requester/BUILD.bazel b/services/spdm/requester/BUILD.bazel
new file mode 100644
index 0000000..0362cd5
--- /dev/null
+++ b/services/spdm/requester/BUILD.bazel
@@ -0,0 +1,21 @@
+# Licensed under the Apache-2.0 license
+
+load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+
+rust_library(
+    name = "spdm_requester_lib",
+    srcs = glob(["src/**/*.rs"]),
+    crate_name = "openprot_spdm_requester",
+    edition = "2024",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//services/spdm/transport-mctp:spdm_transport_mctp",
+        "@rust_crates//:heapless",
+        "@rust_crates//:spdm-lib",
+    ],
+)
+
+rust_test(
+    name = "spdm_requester_test",
+    crate = ":spdm_requester_lib",
+)
diff --git a/services/spdm/requester/Cargo.toml b/services/spdm/requester/Cargo.toml
new file mode 100644
index 0000000..e87282e
--- /dev/null
+++ b/services/spdm/requester/Cargo.toml
@@ -0,0 +1,13 @@
+# Licensed under the Apache-2.0 license
+
+[package]
+name = "openprot-spdm-requester"
+version = "0.1.0"
+edition = "2021"
+description = "SPDM requester implementation for OpenPRoT"
+license = "Apache-2.0"
+
+[dependencies]
+openprot-spdm-transport-mctp = { path = "../transport-mctp" }
+spdm-lib = { git = "https://github.com/9elements/spdm-lib.git", branch = "buildup" }
+heapless = { workspace = true }
diff --git a/services/spdm/requester/README.md b/services/spdm/requester/README.md
new file mode 100644
index 0000000..0cca5d2
--- /dev/null
+++ b/services/spdm/requester/README.md
@@ -0,0 +1,29 @@
+# openprot-spdm-requester
+
+SPDM requester (client) implementation for OpenPRoT.
+
+## Overview
+
+This crate provides the SPDM requester role, which initiates attestation operations with SPDM responders. The requester sends requests to retrieve:
+- Version information
+- Device capabilities
+- Cryptographic algorithms
+- Certificates and measurements
+- Challenge-response attestation
+
+## Status
+
+This crate is in early development. Basic infrastructure is in place, but SPDM protocol operations are not yet implemented.
+
+## Dependencies
+
+- `spdm-lib` — SPDM protocol library from 9elements
+- `heapless` — `no_std` collections
+
+## Future Work
+
+- Implement SPDM 1.2+ request builders
+- Add GET_VERSION, GET_CAPABILITIES support
+- Implement CHALLENGE and GET_MEASUREMENTS
+- Add certificate chain validation
+- Integrate with MCTP transport layer
diff --git a/services/spdm/requester/src/lib.rs b/services/spdm/requester/src/lib.rs
new file mode 100644
index 0000000..7b6bf73
--- /dev/null
+++ b/services/spdm/requester/src/lib.rs
@@ -0,0 +1,67 @@
+// Licensed under the Apache-2.0 license
+
+//! SPDM Requester Service
+//!
+//! This service implements the SPDM requester (client) role, which initiates
+//! attestation and measurement operations with SPDM responders.
+//!
+//! ## Overview
+//!
+//! The SPDM requester sends requests to responders to:
+//! - Get version information
+//! - Negotiate capabilities and algorithms
+//! - Challenge the responder for attestation
+//! - Retrieve measurements and certificates
+//!
+//! ## Architecture
+//!
+//! ```text
+//! ┌─────────────────────────┐
+//! │  Application            │
+//! │  (attestation manager)  │
+//! └───────────┬─────────────┘
+//!             │
+//!             ▼
+//! ┌─────────────────────────┐
+//! │  SPDM Requester         │◄── This crate
+//! │  (request builder)      │
+//! └───────────┬─────────────┘
+//!             │ SPDM messages
+//!             ▼
+//! ┌─────────────────────────┐
+//! │  MCTP Transport         │
+//! └─────────────────────────┘
+//! ```
+
+#![no_std]
+#![warn(missing_docs)]
+
+/// SPDM requester state and configuration.
+#[derive(Debug)]
+pub struct SpdmRequester {
+    /// Remote endpoint ID for MCTP transport.
+    pub remote_eid: u8,
+}
+
+impl SpdmRequester {
+    /// Create a new SPDM requester targeting the given endpoint.
+    pub fn new(remote_eid: u8) -> Self {
+        Self { remote_eid }
+    }
+
+    /// Get the remote endpoint ID.
+    pub fn remote_eid(&self) -> u8 {
+        self.remote_eid
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    #[test]
+    fn test_requester_creation() {
+        let requester = SpdmRequester::new(42);
+        assert_eq!(requester.remote_eid(), 42);
+    }
+}
diff --git a/services/spdm/responder/BUILD.bazel b/services/spdm/responder/BUILD.bazel
new file mode 100644
index 0000000..35e35f8
--- /dev/null
+++ b/services/spdm/responder/BUILD.bazel
@@ -0,0 +1,14 @@
+# Licensed under the Apache-2.0 license
+
+load("@rules_rust//rust:defs.bzl", "rust_library")
+
+rust_library(
+    name = "spdm_responder_lib",
+    srcs = glob(["src/**/*.rs"]),
+    crate_name = "spdm_responder",
+    edition = "2024",
+    visibility = ["//visibility:public"],
+    deps = [
+        "@oot_crates_no_std//:spdm-lib",
+    ],
+)
diff --git a/services/spdm/responder/Cargo.toml b/services/spdm/responder/Cargo.toml
new file mode 100644
index 0000000..8080708
--- /dev/null
+++ b/services/spdm/responder/Cargo.toml
@@ -0,0 +1,11 @@
+# Licensed under the Apache-2.0 license
+
+[package]
+name = "spdm-responder"
+version = "0.1.0"
+edition = "2021"
+description = "SPDM responder service for OpenPRoT"
+license = "Apache-2.0"
+
+[dependencies]
+spdm-lib = { git = "https://github.com/9elements/spdm-lib.git", branch = "buildup" }
diff --git a/services/spdm/responder/README.md b/services/spdm/responder/README.md
new file mode 100644
index 0000000..f21e695
--- /dev/null
+++ b/services/spdm/responder/README.md
@@ -0,0 +1,5 @@
+# SPDM Responder Service
+
+SPDM responder service for OpenPRoT - wraps spdm-lib SpdmContext for simplified message processing.
+
+See source code documentation for detailed usage.
diff --git a/services/spdm/responder/src/lib.rs b/services/spdm/responder/src/lib.rs
new file mode 100644
index 0000000..09f3806
--- /dev/null
+++ b/services/spdm/responder/src/lib.rs
@@ -0,0 +1,315 @@
+// Licensed under the Apache-2.0 license
+
+//! SPDM Responder Service
+//!
+//! This service implements the SPDM responder (server) role, which responds
+//! to attestation and measurement requests from SPDM requesters.
+//!
+//! ## Overview
+//!
+//! The SPDM responder receives requests and provides:
+//! - Version and capability information
+//! - Certificate chains for authentication
+//! - Device measurements
+//! - Challenge-response attestation
+//!
+//! ## Architecture
+//!
+//! ```text
+//! ┌─────────────────────────┐
+//! │  MCTP Transport         │
+//! │  (incoming messages)    │
+//! └───────────┬─────────────┘
+//!             │ SPDM requests
+//!             ▼
+//! ┌─────────────────────────┐
+//! │  SPDM Responder         │◄── This crate
+//! │  (SpdmContext wrapper)  │
+//! └───────────┬─────────────┘
+//!             │
+//!             ▼
+//! ┌─────────────────────────────────────┐
+//! │  Platform Implementations           │
+//! │  - CertStore (certificates)         │
+//! │  - Hash (SHA-384)                   │
+//! │  - RNG (random numbers)             │
+//! │  - Evidence (measurements)          │
+//! │  - Transport (MCTP)                 │
+//! └─────────────────────────────────────┘
+//! ```
+//!
+//! ## Usage
+//!
+//! ```rust,no_run
+//! use spdm_responder::SpdmResponder;
+//!
+//! // Create platform implementations
+//! let cert_store = Ast1060CertStore::new(crypto_handle);
+//! let hash = SpdmCryptoHash::new(crypto_handle);
+//! let rng = SpdmCryptoRng::new(crypto_handle);
+//! let evidence = Ast1060Evidence::new();
+//! let transport = MctpSpdmTransport::new(mctp_client);
+//!
+//! // Create responder
+//! let mut responder = SpdmResponder::new(
+//!     transport,
+//!     cert_store,
+//!     hash,
+//!     rng,
+//!     evidence,
+//! )?;
+//!
+//! // Process messages in loop
+//! loop {
+//!     responder.process_message()?;
+//! }
+//! ```
+
+#![no_std]
+
+use spdm_lib::cert_store::SpdmCertStore;
+use spdm_lib::codec::MessageBuf;
+use spdm_lib::context::SpdmContext;
+use spdm_lib::error::SpdmError;
+use spdm_lib::platform::evidence::SpdmEvidence;
+use spdm_lib::platform::hash::SpdmHash;
+use spdm_lib::platform::rng::SpdmRng;
+use spdm_lib::platform::transport::SpdmTransport;
+use spdm_lib::protocol::algorithms::{
+    AeadCipherSuite, AlgorithmPriorityTable, BaseAsymAlgo, BaseHashAlgo, DeviceAlgorithms,
+    DheNamedGroup, KeySchedule, LocalDeviceAlgorithms, MeasurementHashAlgo,
+    MeasurementSpecification, MelSpecification, OtherParamSupport, ReqBaseAsymAlg,
+};
+use spdm_lib::protocol::version::SpdmVersion;
+
+/// Supported SPDM versions (static to avoid lifetime issues)
+static SUPPORTED_VERSIONS: [SpdmVersion; 2] = [SpdmVersion::V12, SpdmVersion::V11];
+use spdm_lib::protocol::{CapabilityFlags, DeviceCapabilities};
+
+/// Maximum SPDM message size
+const MAX_SPDM_MSG_SIZE: usize = 4096;
+
+/// SPDM responder result type
+pub type ResponderResult<T> = Result<T, ResponderError>;
+
+/// SPDM responder errors
+#[derive(Debug)]
+pub enum ResponderError {
+    /// SPDM protocol error
+    SpdmError(SpdmError),
+    /// Message buffer error
+    BufferError,
+}
+
+impl From<SpdmError> for ResponderError {
+    fn from(e: SpdmError) -> Self {
+        ResponderError::SpdmError(e)
+    }
+}
+
+/// SPDM responder configuration
+#[derive(Debug, Clone, Copy)]
+pub struct ResponderConfig {
+    /// CT exponent for timing
+    pub ct_exponent: u8,
+    /// Data transfer size
+    pub data_transfer_size: u32,
+    /// Maximum SPDM message size
+    pub max_spdm_msg_size: u32,
+}
+
+impl Default for ResponderConfig {
+    fn default() -> Self {
+        Self {
+            ct_exponent: 0,
+            data_transfer_size: 1024,
+            max_spdm_msg_size: MAX_SPDM_MSG_SIZE as u32,
+        }
+    }
+}
+
+/// SPDM responder state and configuration.
+///
+/// This wraps the spdm-lib `SpdmContext` and provides a simplified interface
+/// for processing SPDM messages.
+pub struct SpdmResponder<'a> {
+    context: SpdmContext<'a>,
+}
+
+impl<'a> SpdmResponder<'a> {
+    /// Create a new SPDM responder with platform implementations.
+    ///
+    /// # Arguments
+    ///
+    /// * `transport` - Transport layer implementation (e.g., MCTP)
+    /// * `cert_store` - Certificate store with device certificates
+    /// * `hash` - Hash implementation for protocol operations
+    /// * `m1_hash` - Hash implementation for M1 transcript
+    /// * `l1_hash` - Hash implementation for L1 transcript
+    /// * `rng` - Random number generator
+    /// * `evidence` - Evidence provider for measurements
+    /// * `config` - Optional configuration (uses defaults if None)
+    ///
+    /// # Returns
+    ///
+    /// A new `SpdmResponder` instance ready to process messages.
+    pub fn new(
+        transport: &'a mut dyn SpdmTransport,
+        cert_store: &'a mut dyn SpdmCertStore,
+        hash: &'a mut dyn SpdmHash,
+        m1_hash: &'a mut dyn SpdmHash,
+        l1_hash: &'a mut dyn SpdmHash,
+        rng: &'a mut dyn SpdmRng,
+        evidence: &'a dyn SpdmEvidence,
+        config: Option<ResponderConfig>,
+    ) -> ResponderResult<Self> {
+        let config = config.unwrap_or_default();
+
+        // Create device capabilities
+        let capabilities = create_device_capabilities(config);
+
+        // Create local algorithms
+        let algorithms = create_local_algorithms();
+
+        // Create SPDM context
+        let context = SpdmContext::new(
+            &SUPPORTED_VERSIONS,
+            transport,
+            capabilities,
+            algorithms,
+            cert_store,
+            None, // No peer cert store needed for responder
+            hash,
+            m1_hash,
+            l1_hash,
+            rng,
+            evidence,
+        )?;
+
+        Ok(Self { context })
+    }
+
+    /// Process a single SPDM message.
+    ///
+    /// This method:
+    /// 1. Receives a request via the transport layer
+    /// 2. Processes it through the SPDM context
+    /// 3. Sends the response back via the transport layer
+    ///
+    /// # Arguments
+    ///
+    /// * `buffer` - Message buffer (must be at least MAX_SPDM_MSG_SIZE bytes)
+    ///
+    /// # Returns
+    ///
+    /// - `Ok(())` if message processed successfully
+    /// - `Err(ResponderError)` on error
+    ///
+    /// # Note
+    ///
+    /// This should be called in a loop to continuously process messages.
+    /// Transport errors indicate connection closed.
+    pub fn process_message(&mut self, buffer: &'a mut [u8]) -> ResponderResult<()> {
+        let mut message_buf = MessageBuf::new(buffer);
+        self.context.responder_process_message(&mut message_buf)?;
+        Ok(())
+    }
+
+    /// Get reference to the underlying SPDM context.
+    ///
+    /// This allows direct access to context state if needed.
+    pub fn context(&self) -> &SpdmContext<'a> {
+        &self.context
+    }
+
+    /// Get mutable reference to the underlying SPDM context.
+    ///
+    /// This allows direct manipulation of context state if needed.
+    pub fn context_mut(&mut self) -> &mut SpdmContext<'a> {
+        &mut self.context
+    }
+}
+
+/// Create SPDM device capabilities based on configuration.
+fn create_device_capabilities(config: ResponderConfig) -> DeviceCapabilities {
+    let mut flags_value = 0u32;
+
+    // Certificate capability
+    flags_value |= 1 << 1; // CERT_CAP
+
+    // Challenge capability
+    flags_value |= 1 << 2; // CHAL_CAP
+
+    // Measurements capability (with signature)
+    flags_value |= 2 << 3; // MEAS_CAP (0b10 = measurements with signature)
+
+    // Measurements freshness capability
+    flags_value |= 1 << 5; // MEAS_FRESH_CAP
+
+    // Chunk capability
+    flags_value |= 1 << 17; // CHUNK_CAP
+
+    let flags = CapabilityFlags::new(flags_value);
+
+    DeviceCapabilities {
+        ct_exponent: config.ct_exponent,
+        flags,
+        data_transfer_size: config.data_transfer_size,
+        max_spdm_msg_size: config.max_spdm_msg_size,
+        include_supported_algorithms: true,
+    }
+}
+
+/// Create local device algorithms configuration.
+///
+/// Configures supported cryptographic algorithms:
+/// - Measurement: DMTF specification with SHA-384
+/// - Asymmetric: ECDSA with NIST P-384
+/// - Hash: SHA-384
+fn create_local_algorithms<'a>() -> LocalDeviceAlgorithms<'a> {
+    // Measurement specification (DMTF)
+    let mut measurement_spec = MeasurementSpecification::default();
+    measurement_spec.set_dmtf_measurement_spec(1);
+
+    // Measurement hash algorithm (SHA-384)
+    let mut measurement_hash_algo = MeasurementHashAlgo::default();
+    measurement_hash_algo.set_tpm_alg_sha_384(1);
+
+    // Base asymmetric algorithm (ECDSA P-384)
+    let mut base_asym_algo = BaseAsymAlgo::default();
+    base_asym_algo.set_tpm_alg_ecdsa_ecc_nist_p384(1);
+
+    // Base hash algorithm (SHA-384)
+    let mut base_hash_algo = BaseHashAlgo::default();
+    base_hash_algo.set_tpm_alg_sha_384(1);
+
+    let device_algorithms = DeviceAlgorithms {
+        measurement_spec,
+        other_param_support: OtherParamSupport::default(),
+        measurement_hash_algo,
+        base_asym_algo,
+        base_hash_algo,
+        mel_specification: MelSpecification::default(),
+        dhe_group: DheNamedGroup::default(),
+        aead_cipher_suite: AeadCipherSuite::default(),
+        req_base_asym_algo: ReqBaseAsymAlg::default(),
+        key_schedule: KeySchedule::default(),
+    };
+
+    let algorithm_priority_table = AlgorithmPriorityTable {
+        measurement_specification: None,
+        opaque_data_format: None,
+        base_asym_algo: None,
+        base_hash_algo: None,
+        mel_specification: None,
+        dhe_group: None,
+        aead_cipher_suite: None,
+        req_base_asym_algo: None,
+        key_schedule: None,
+    };
+
+    LocalDeviceAlgorithms {
+        device_algorithms,
+        algorithm_priority_table,
+    }
+}
diff --git a/services/spdm/rng/BUILD.bazel b/services/spdm/rng/BUILD.bazel
new file mode 100644
index 0000000..2f93ea1
--- /dev/null
+++ b/services/spdm/rng/BUILD.bazel
@@ -0,0 +1,20 @@
+# Licensed under the Apache-2.0 license
+
+load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")
+
+rust_library(
+    name = "spdm_rng_lib",
+    srcs = glob(["src/**/*.rs"]),
+    crate_name = "openprot_spdm_rng",
+    edition = "2024",
+    visibility = ["//visibility:public"],
+    deps = [
+        "//services/crypto/client:crypto_client",
+        "@oot_crates_no_std//:spdm-lib",
+    ],
+)
+
+rust_test(
+    name = "spdm_rng_test",
+    crate = ":spdm_rng_lib",
+)
diff --git a/services/spdm/rng/Cargo.toml b/services/spdm/rng/Cargo.toml
new file mode 100644
index 0000000..8731a23
--- /dev/null
+++ b/services/spdm/rng/Cargo.toml
@@ -0,0 +1,12 @@
+# Licensed under the Apache-2.0 license
+
+[package]
+name = "openprot-spdm-rng"
+version = "0.1.0"
+edition = "2021"
+description = "SPDM RNG implementation using OpenPRoT crypto service"
+license = "Apache-2.0"
+
+[dependencies]
+spdm-lib = { git = "https://github.com/9elements/spdm-lib.git", branch = "buildup" }
+crypto-client = { path = "../../crypto/client" }
diff --git a/services/spdm/rng/README.md b/services/spdm/rng/README.md
new file mode 100644
index 0000000..73b4a42
--- /dev/null
+++ b/services/spdm/rng/README.md
@@ -0,0 +1,47 @@
+# SPDM RNG
+
+Random number generator for SPDM protocol operations, implemented via OpenPRoT crypto service.
+
+## Overview
+
+This crate implements the `SpdmRng` trait from spdm-lib by delegating to the
+centralized crypto service. All randomness is generated using ChaCha20 CSPRNG
+seeded from system entropy.
+
+## Architecture
+
+```
+SpdmCryptoRng → CryptoClient → IPC → CryptoServer → RustCryptoBackend → ChaCha20Rng
+```
+
+## Security Model
+
+- **Entropy Source:** getrandom crate (platform-dependent: hardware RNG, /dev/urandom, etc.)
+- **PRNG:** ChaCha20 stream cipher (NIST approved, used in TLS 1.3)
+- **Seeding:** Fresh seed from system entropy on each crypto service operation
+
+## Usage
+
+```rust
+use openprot_spdm_rng::SpdmCryptoRng;
+use spdm_lib::platform::rng::SpdmRng;
+
+let mut rng = SpdmCryptoRng::new(handle::CRYPTO);
+let mut challenge = [0u8; 32];
+rng.get_random_bytes(&mut challenge)?;
+```
+
+## Future Enhancements
+
+When AST1060 hardware RNG driver is available, only the crypto backend needs updating:
+- Modify `RustCryptoBackend::OneShot<GetRandomBytes>` to use hardware RNG
+- No changes needed to this crate or any SPDM code
+
+## Dependencies
+
+- `spdm-lib` — SPDM protocol library
+- `crypto-client` — OpenPRoT crypto service client
+
+## License
+
+Apache-2.0
diff --git a/services/spdm/rng/src/lib.rs b/services/spdm/rng/src/lib.rs
new file mode 100644
index 0000000..6134f69
--- /dev/null
+++ b/services/spdm/rng/src/lib.rs
@@ -0,0 +1,79 @@
+// Licensed under the Apache-2.0 license
+
+//! SPDM RNG Implementation
+//!
+//! Provides random number generation for SPDM protocol operations by
+//! delegating to the OpenPRoT crypto service via IPC.
+//!
+//! ## Architecture
+//!
+//! This crate implements the `SpdmRng` trait from spdm-lib by wrapping
+//! the `CryptoClient` and calling its `get_random_bytes()` method, which
+//! uses ChaCha20 CSPRNG seeded from system entropy.
+//!
+//! ## Usage
+//!
+//! ```rust,no_run
+//! use openprot_spdm_rng::SpdmCryptoRng;
+//! use spdm_lib::platform::rng::SpdmRng;
+//!
+//! let mut rng = SpdmCryptoRng::new(crypto_handle);
+//! let mut buffer = [0u8; 32];
+//! rng.get_random_bytes(&mut buffer).unwrap();
+//! ```
+
+#![no_std]
+#![warn(missing_docs)]
+
+use crypto_client::CryptoClient;
+use spdm_lib::platform::rng::{SpdmRng, SpdmRngResult};
+
+/// SPDM RNG implementation using OpenPRoT crypto service.
+///
+/// This struct wraps a `CryptoClient` handle and delegates all RNG
+/// operations to the centralized crypto service via IPC.
+pub struct SpdmCryptoRng {
+    crypto: CryptoClient,
+}
+
+impl SpdmCryptoRng {
+    /// Create a new SPDM RNG 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),
+        }
+    }
+}
+
+impl SpdmRng for SpdmCryptoRng {
+    fn get_random_bytes(&mut self, buf: &mut [u8]) -> SpdmRngResult<()> {
+        self.crypto
+            .get_random_bytes(buf)
+            .map_err(|_| spdm_lib::platform::rng::SpdmRngError::InvalidSize)
+    }
+
+    fn generate_random_number(&mut self, random_number: &mut [u8]) -> SpdmRngResult<()> {
+        // Both methods are identical in spdm-lib: fill a buffer with random bytes
+        self.crypto
+            .get_random_bytes(random_number)
+            .map_err(|_| spdm_lib::platform::rng::SpdmRngError::InvalidSize)
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    // Note: These tests require a running crypto service, so they're
+    // integration tests that would run in a QEMU environment
+
+    #[test]
+    fn test_struct_creation() {
+        let _rng = SpdmCryptoRng::new(42);
+    }
+}