Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 23 additions & 0 deletions services/attest/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

package(default_visibility = ["//visibility:public"])

# All production crates (embedded-safe, no test stubs).
filegroup(
name = "attest_embedded_all",
srcs = [
"//services/attest/api:attest_api",
"//services/attest/producer:attest_producer",
],
)

# Host-side tests; run without embedded target config.
test_suite(
name = "attest_host_tests",
tests = [
"//services/attest/api:attest_api_test",
"//services/attest/producer:attest_producer_integration_test",
"//services/attest/producer:attest_producer_unit_test",
],
)
68 changes: 68 additions & 0 deletions services/attest/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<!-- SPDX-License-Identifier: Apache-2.0 -->

# services/attest

Attestation producer service for the OpenPRoT Platform Root of Trust.

This directory contains two Cargo crates and the Bazel build targets that
deliver OCP-EAT attestation token generation to the rest of the OpenPRoT
firmware stack.

## Crates

| Crate | Path | Role |
|---|---|---|
| `openprot-attest-api` | `api/` | Platform-independent traits and types. Callers depend on this crate only. |
| `openprot-attest-producer` | `producer/` | Concrete token producer backed by Caliptra hardware or a software stub. |

Neither crate depends on the verifier module or `spdm-lib`. Evidence from the
verifier is accepted as a raw CBOR byte slice (`&[u8]`), keeping the producer
decoupled from verifier internals.

## Bazel targets

```
//services/attest:attest_embedded_all # production filegroup (api + producer)
//services/attest:attest_host_tests # test_suite for all host-side tests
```

## Cargo build

```bash
# From the workspace root (~/openprot_attestation/)

# API crate only
cargo build -p openprot-attest-api

# Producer crate (pulls in API automatically)
cargo build -p openprot-attest-producer

# Producer with software stub enabled (no Caliptra hardware required)
cargo build -p openprot-attest-producer --features test-support

# Entire workspace
cargo build
```

## Testing

```bash
cargo test -p openprot-attest-producer --features test-support
cargo test --features test-support
```

## Relationship to the verifier

The attester and verifier are deliberately decoupled. The verifier
(`attestation/src/verifier/`) produces a CBOR-serialized `AttestEvidence`
value that is passed to `AttestProducer::generate_token` as the `evidence`
byte slice. The producer embeds it verbatim as the `concise-evidence` claim
(key `-70001`) in the outgoing OCP-EAT token. The outer `COSE_Sign1`
signature covers the embedded evidence.

## Standards

- OCP Entity Attestation Token — <https://opencomputeproject.github.io/Security/ietf-eat-profile/HEAD/>
- IETF EAT (RFC 9711) — <https://www.rfc-editor.org/rfc/rfc9711>
- CBOR Web Token (RFC 8392) — <https://www.rfc-editor.org/rfc/rfc8392>
- COSE (RFC 9052) — <https://www.rfc-editor.org/rfc/rfc9052>
20 changes: 20 additions & 0 deletions services/attest/api/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

load("@rules_rust//rust:defs.bzl", "rust_library", "rust_test")

rust_library(
name = "attest_api",
srcs = glob(["src/**/*.rs"]),
crate_name = "openprot_attest_api",
edition = "2021",
visibility = ["//visibility:public"],
deps = [
"@rust_crates//:thiserror",
],
)

rust_test(
name = "attest_api_test",
crate = ":attest_api",
)
12 changes: 12 additions & 0 deletions services/attest/api/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Licensed under the Apache-2.0 license
# SPDX-License-Identifier: Apache-2.0

[package]
name = "openprot-attest-api"
version = "0.1.0"
edition = "2021"
license = "Apache-2.0"
description = "Platform-independent API for the OpenPRoT attestation producer service"

[dependencies]
thiserror = "1"
92 changes: 92 additions & 0 deletions services/attest/api/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
<!-- SPDX-License-Identifier: Apache-2.0 -->

# openprot-attest-api

Platform-independent trait and type definitions for the OpenPRoT attestation
producer service.

Callers and other OpenPRoT services depend **only** on this crate. It has no
dependency on the producer implementation, the verifier module, or `spdm-lib`.

## Purpose

This crate defines the stable interface boundary for attestation token
generation. By depending on `openprot-attest-api` rather than
`openprot-attest-producer`, services can be tested with any `AttestProducer`
implementation — including the `SoftwareAttestProducer` stub — without pulling
in hardware dependencies.

## Source files

| File | Contents |
|---|---|
| `src/lib.rs` | Public re-exports. `#![forbid(unsafe_code)]`. |
| `src/traits.rs` | `AttestProducer` trait. |
| `src/types.rs` | `Measurement`, `DigestAlgorithm`, `MeasurementAuthority`, `CertChain`, `AttestConfig`, `OemId`, `CaliptraSigner` trait, `MeasurementProvider` trait. |
| `src/error.rs` | `AttestError` — shared error type for both service crates. |

## Key traits

### `AttestProducer`

The primary interface implemented by `HwAttestProducer` (and the
`SoftwareAttestProducer` stub in the producer crate).

```rust
pub trait AttestProducer: Send + Sync {
fn generate_token(&self, nonce: &[u8], evidence: &[u8]) -> Result<Vec<u8>, AttestError>;
fn cert_chain(&self) -> Result<CertChain, AttestError>;
}
```

`evidence` is the raw CBOR output of the verifier service. Pass an empty slice
when no peer attestation has been performed. The bytes are embedded verbatim as
the `concise-evidence` claim (key `-70001`) in the OCP-EAT token.

### `CaliptraSigner`

Abstracts signing operations that must execute inside the Caliptra hardware
boundary.

```rust
pub trait CaliptraSigner: Send + Sync {
fn sign_es384(&self, payload: &[u8]) -> Result<[u8; 96], AttestError>;
fn alias_cert_der(&self) -> Result<Vec<u8>, AttestError>;
fn cert_chain_der(&self) -> Result<Vec<Vec<u8>>, AttestError>;
}
```

The private Alias Key never leaves Caliptra. Production code implements this
trait via the Caliptra mailbox driver (`caliptra-sw`).

### `MeasurementProvider`

Plug in platform-specific firmware measurement sources (UEFI, BMC, etc.)
beyond the Caliptra-internal measurements.

```rust
pub trait MeasurementProvider: Send + Sync {
fn component_name(&self) -> &str;
fn measurements(&self) -> Result<Vec<Measurement>, AttestError>;
}
```

## Key types

| Type | Description |
|---|---|
| `Measurement` | Single firmware measurement: component name, version, digest algorithm, digest bytes, measurement authority. |
| `DigestAlgorithm` | `Sha384` or `Sha512`. |
| `MeasurementAuthority` | `Caliptra` (hardware-measured) or `Platform` (software-registered). |
| `CertChain` | DER-encoded certificate chain ordered leaf → root. |
| `AttestConfig` | Producer configuration: `oemid`, `hw_model`, `hw_version`, `cert_cache_ttl`. |

## Cargo

```toml
[dependencies]
openprot-attest-api = { path = "services/attest/api" }
```

No additional features are required. The crate has a single dependency:
`thiserror` for `AttestError` derivation.
14 changes: 14 additions & 0 deletions services/attest/api/src/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

#[derive(Debug, thiserror::Error)]
pub enum AttestError {
#[error("Caliptra mailbox error: {0}")]
Caliptra(String),
#[error("CBOR encoding error: {0}")]
Cbor(String),
#[error("COSE signing error: {0}")]
Cose(String),
#[error("Measurement provider error: {0}")]
Provider(String),
}
37 changes: 37 additions & 0 deletions services/attest/api/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

//! Platform-independent API for the OpenPRoT attestation producer service.
//!
//! # Usage
//!
//! Applications depend only on this crate. Platform code provides a concrete
//! [`AttestProducer`] implementation (hardware-backed via Caliptra, or the
//! `test-support`-gated software stub in the `openprot-attest-producer` crate).
//!
//! ```text
//! ┌───────────────────────────────────────────────┐
//! │ application / verifier service │
//! │ depends on: openprot-attest-api │
//! │ calls: AttestProducer::generate_token│
//! └──────────────────┬────────────────────────────┘
//! │ trait object / generic bound
//! ┌──────────────────▼────────────────────────────┐
//! │ openprot-attest-producer │
//! │ HwAttestProducer (production) │
//! │ SoftwareAttestProducer (test-support) │
//! └───────────────────────────────────────────────┘
//! ```

#![forbid(unsafe_code)]

mod error;
mod traits;
mod types;

pub use error::AttestError;
pub use traits::AttestProducer;
pub use types::{
AttestConfig, CaliptraSigner, CertChain, DigestAlgorithm, Measurement,
MeasurementAuthority, MeasurementProvider, OemId,
};
31 changes: 31 additions & 0 deletions services/attest/api/src/traits.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

use crate::{AttestError, CertChain};

/// Platform-independent attestation producer interface.
///
/// Implementors assemble and sign an OCP-EAT COSE_Sign1 token containing
/// platform measurements, DICE identity claims, and optionally pre-serialized
/// SPDM evidence from the verifier service.
///
/// The `evidence` parameter accepted by `generate_token` is a raw CBOR byte
/// slice produced by the verifier service. Passing it as bytes rather than a
/// typed struct keeps this crate free of any verifier or spdm-lib dependency.
pub trait AttestProducer: Send + Sync {
/// Generate a signed OCP-EAT COSE_Sign1 token bound to `nonce`.
///
/// `evidence` is a CBOR-encoded blob from the verifier service, embedded
/// verbatim as claim -70001. Pass an empty slice when no peer evidence is
/// available.
///
/// Returns the complete COSE_Sign1 structure as a byte vector.
fn generate_token(
&self,
nonce: &[u8],
evidence: &[u8],
) -> Result<Vec<u8>, AttestError>;

/// Return the current DICE certificate chain, ordered leaf → root.
fn cert_chain(&self) -> Result<CertChain, AttestError>;
}
66 changes: 66 additions & 0 deletions services/attest/api/src/types.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed under the Apache-2.0 license
// SPDX-License-Identifier: Apache-2.0

use std::time::Duration;

use crate::error::AttestError;

/// OEM identifier (IANA Private Enterprise Number or UUID form).
#[derive(Clone, Debug)]
pub struct OemId(pub Vec<u8>);

#[derive(Clone, Copy, Debug)]
pub enum DigestAlgorithm {
Sha384,
Sha512,
}

#[derive(Clone, Copy, Debug)]
pub enum MeasurementAuthority {
Caliptra,
Platform,
}

/// A single firmware measurement record to include in the EAT token.
#[derive(Clone, Debug)]
pub struct Measurement {
pub component: String,
pub version: String,
pub digest_alg: DigestAlgorithm,
pub digest: Vec<u8>,
pub authority: MeasurementAuthority,
}

/// DER-encoded certificate chain ordered leaf → root.
pub struct CertChain(pub Vec<Vec<u8>>);

/// Producer configuration, set once at platform initialisation.
pub struct AttestConfig {
pub oemid: OemId,
pub hw_model: String,
pub hw_version: String,
pub cert_cache_ttl: Duration,
}

/// Hardware-backed signing operations.
///
/// Production: implemented by a Caliptra mailbox driver.
/// Testing: implement with a software key (`SoftwareAttestProducer` in the
/// producer crate behind `test-support`).
pub trait CaliptraSigner: Send + Sync {
/// Sign `payload` with the Alias Key (ES384). Returns raw (r‖s) bytes.
fn sign_es384(&self, payload: &[u8]) -> Result<[u8; 96], AttestError>;
/// Return the DER-encoded Alias (leaf) certificate.
fn alias_cert_der(&self) -> Result<Vec<u8>, AttestError>;
/// Return the full DER-encoded certificate chain, leaf → root.
fn cert_chain_der(&self) -> Result<Vec<Vec<u8>>, AttestError>;
}

/// Platform-specific measurement source.
///
/// Implement for each firmware component (UEFI, BMC, etc.) the platform
/// wants to measure beyond Caliptra-internal measurements.
pub trait MeasurementProvider: Send + Sync {
fn component_name(&self) -> &str;
fn measurements(&self) -> Result<Vec<Measurement>, AttestError>;
}
Loading
Loading