Skip to content

Commit

Permalink
Add experimental TDX attester/verifier crate (#35)
Browse files Browse the repository at this point in the history
The az-snp-vtpm crate has been generalized into an az-cvm-vtpm
workspace and extended to support attestation workflows on Azure TDX
CVMs, which are in limited preview at the moment. The attestation
workflow on TDX CVMs has a lot in common with the equivalent process
on SEV-SNP CVMs, so in the future we'll probably want to consolidate
both into a common az-cvm-vtpm crate.

Until TDX CVMs on are generally available on Azure, we'll want to
keep both crates seperate, as the offering is still subject to change.

Signed-off-by: Magnus Kulke <[email protected]>
  • Loading branch information
mkulke authored Oct 19, 2023
1 parent 20fd006 commit 5a114c2
Show file tree
Hide file tree
Showing 34 changed files with 312 additions and 25 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: e2e

on:
pull_request:
push:
branches: [ "main" ]

jobs:
Expand Down
14 changes: 5 additions & 9 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ env:

jobs:
build:

runs-on: ubuntu-latest
defaults:
run:
working-directory: az-cvm-vtpm

steps:
- uses: actions/checkout@v3

- name: Install deps
run: sudo apt-get update && sudo apt-get install -y libtss2-dev

- uses: actions/checkout@v3

- uses: actions-rs/toolchain@v1
with:
profile: minimal
Expand All @@ -34,24 +36,18 @@ jobs:
- name: Build
run: cargo build --verbose --all
working-directory: az-snp-vtpm

- name: Check verifier-only
run: cargo check --verbose --no-default-features --features=verifier
working-directory: az-snp-vtpm

- name: Check attester-only
run: cargo check --verbose --no-default-features --features=attester
working-directory: az-snp-vtpm

- name: Run tests
run: cargo test --verbose --all
working-directory: az-snp-vtpm

- name: Format
run: cargo fmt --all -- --check
working-directory: az-snp-vtpm

- name: Lint
run: cargo clippy --all-targets --all-features --all -- -D warnings
working-directory: az-snp-vtpm
4 changes: 2 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

Assorted tools and libraries to use with [Azure CVMs](https://azure.microsoft.com/en-us/solutions/confidential-compute/).

# vTPM-SNP
## az-cvm-vtpm

Library and CLI to integrate with vTPM on SEV-SNP enabled machines.
Attestation for Azure Confidential Virtual Machines
1 change: 1 addition & 0 deletions az-snp-vtpm/.gitignore → az-cvm-vtpm/.gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
target
arm/*.json
Cargo.lock
*.swp
7 changes: 7 additions & 0 deletions az-cvm-vtpm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[workspace]
members = [
"az-snp-vtpm",
"az-tdx-vtpm",
"az-snp-vtpm/example",
]
resolver = "2"
11 changes: 11 additions & 0 deletions az-cvm-vtpm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# az-cvm-vtpm

Attestation for Azure Confidential Virtual Machines

## az-snp-vtpm

Attestation Library for Azure AMD SEV-SNP Confidential Virtual Machines.

## az-tdx-vtpm

Attestation Library for Azure Intel TDX Confidential Virtual Machines (Limited Preview).
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,10 @@ sev = "1.2.0"
sha2 = "0.10.6"
static_assertions = "^1.1.0"
thiserror = "1.0.38"
tss-esapi = "7.2"
tss-esapi = "7.4"
ureq = { version = "2.6.2", default-features = false, features = ["json"] }

[features]
default = ["attester", "verifier"]
attester = []
verifier = ["openssl", "sev/openssl", "ureq/tls"]

[workspace]
members = ["example"]
File renamed without changes.
14 changes: 6 additions & 8 deletions az-snp-vtpm/README.md → az-cvm-vtpm/az-snp-vtpm/README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
# az-snp-vtpm

[![Rust](https://github.com/kinvolk/azure-cvm-tooling/actions/workflows/rust.yml/badge.svg)](https://github.com/kinvolk/azure-cvm-tooling/actions/workflows/rust.yml)
[![Crate](https://img.shields.io/crates/v/az-snp-vtpm.svg)](https://crates.io/crates/az-snp-vtpm)
[![Docs](https://docs.rs/rand/badge.svg)](https://docs.rs/az-snp-vtpm)

**vTPM based SEV-SNP attestation for Azure Confidential VMs**
# az-snp-vtpm

This library enables guest attestation flows for [SEV-SNP CVMs on Azure](https://learn.microsoft.com/en-us/azure/confidential-computing/confidential-vm-overview). Please refer to the documentation in [this repository](https://github.com/Azure/confidential-computing-cvm-guest-attestation) for details on the attestation procedure.

Expand All @@ -20,8 +18,8 @@ make deploy
## Build & Install

```bash
cargo b --release
scp target/release/snp-vtpm azureuser@$CONFIDENTIAL_VM:
cargo b --release -p az-snp-vtpm
scp ../target/release/snp-vtpm azureuser@$CONFIDENTIAL_VM:
```

## Run Binary
Expand All @@ -37,7 +35,7 @@ sudo ./snp-vtpm -p
There is a project in the `./example` folder depicting how the crate can be leveraged in a Remote Attestation flow. **Note:** the code is merely illustrative and doesn't feature exhaustive validation, which would be required in a production scenario.

```bash
cargo b -p example
cargo b -p snp-example
```

## SEV-SNP Report & vTPM
Expand All @@ -56,12 +54,12 @@ The vTPM is linked to the SEV-SNP report via the vTPM Attestation Key (AK). The
│ └──────────────────────┘ │ │ └────────────────────┘ │ │ │
│ ┌──────────────┐ │ └──────────────────────┬─┘ ─┘ │
│ │ vTPM Quote │ │ ┌────────────────────┐ │ │
│ │ │ │ │ HCL Report │ │ │
│ │ │ │ │ HCL Report │ │ │
signs ┌─ ┌─┴────────────┐ │ │ │ │ │ sha256
│ │ │ Message │ │ │ │ ┌────────────────┐ │ │ │
│ │ │ │ │ │ │ │ SEV-SNP Report │ │ │ │
│ │ │ ┌──────────┐ │ │ │ │ │ │ │ │ │
│ │ │ │ PCR0 │ │ │ │ │ │ ┌──────────────┴─┴─┴─┐ │
│ │ │ │ PCR0 │ │ │ │ │ │ ┌──────────────┴─┴─┴─┐ │
│ │ │ └──────────┘ │ │ │ │ │ │ Report Data │ ◄───┘
│ │ │ ... │ │ │ │ │ └──────────────┬─┬─┬─┘
│ │ │ ┌──────────┐ │ │ │ │ └────────────────┘ │ │
Expand Down
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[package]
name = "example"
name = "snp-example"
version = "0.1.0"
edition = "2021"

Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
35 changes: 35 additions & 0 deletions az-cvm-vtpm/az-tdx-vtpm/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
[package]
name = "az-tdx-vtpm"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[lib]
path = "src/lib.rs"

[[bin]]
name = "tdx-vtpm"
path = "src/main.rs"

[dependencies]
anyhow = "1.0.75"
base64-url = "2.0.0"
bincode = "1.3.3"
jsonwebkey = { version = "0.3.5", features = ["pkcs-convert"] }
memoffset = "0.9.0"
serde = { version = "1.0.189", features = ["derive"] }
serde-big-array = "0.5.1"
serde_json = "1.0.107"
sha2 = "0.10.8"
tss-esapi = "7.4"
ureq = { version = "2.6.2", default-features = false, features = ["json"] }
az-snp-vtpm = { path = "../az-snp-vtpm" }
sev = "1.2.0"
thiserror = "1.0.49"
openssl = { version = "0.10", optional = true }

[features]
default = ["attester", "verifier"]
attester = []
verifier = ["az-snp-vtpm/verifier", "openssl"]
21 changes: 21 additions & 0 deletions az-cvm-vtpm/az-tdx-vtpm/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# az-tdx-vtpm

[![Rust](https://github.com/kinvolk/azure-cvm-tooling/actions/workflows/rust.yml/badge.svg)](https://github.com/kinvolk/azure-cvm-tooling/actions/workflows/rust.yml)

> [!WARNING]
> This library enables guest attestation and verification for [TDX CVMs on Azure](https://learn.microsoft.com/en-us/azure/confidential-computing/tdx-confidential-vm-overview). TDX CVMs are currently in limited preview and hence the library is considered experimental and subject to change.
## Build & Install

```bash
cargo b --release -p az-tdx-vtpm
scp ../target/release/tdx-vtpm azureuser@$CONFIDENTIAL_VM:
```

## Run Binary

On the TDX CVM, retrieve a TD Quote and write it to disk:

```bash
sudo ./tdx-vtpm
```
44 changes: 44 additions & 0 deletions az-cvm-vtpm/az-tdx-vtpm/src/imds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use serde::Deserialize;
use thiserror::Error;

const IMDS_QUOTE_URL: &str = "http://169.254.169.254/acc/tdquote";

#[derive(Error, Debug)]
pub enum ImdsError {
#[error("http error")]
HttpError(#[from] Box<ureq::Error>),
#[error("base64 error")]
Base64Error(#[from] base64_url::base64::DecodeError),
#[error("io error")]
IoError(#[from] std::io::Error),
}

pub struct ReportBody {
report: String,
}

impl ReportBody {
pub fn new(report_bytes: &[u8]) -> Self {
let report = base64_url::encode(report_bytes);
Self { report }
}
}

#[derive(Clone, Debug, Deserialize)]
struct QuoteResponse {
quote: String,
}

pub fn get_td_quote(report_body: ReportBody) -> Result<Vec<u8>, ImdsError> {
let response: QuoteResponse = ureq::post(IMDS_QUOTE_URL)
.send_json(ureq::json!({
"report": report_body.report,
}))
.map_err(Box::new)?
.into_json()?;
let quote = base64_url::decode(&response.quote)?;
Ok(quote)
}
38 changes: 38 additions & 0 deletions az-cvm-vtpm/az-tdx-vtpm/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

//! This library enables guest attestation flows for [TDX CVMs on Azure](https://learn.microsoft.com/en-us/azure/confidential-computing/tdx-confidential-vm-overview). TDX CVMs are currently in limited preview and hence the library is considered experimental and subject to change.
//! #
//! ```no_run
//! use az_tdx_vtpm::{imds, hcl, vtpm};
//! use std::error::Error;
//!
//! fn main() -> Result<(), Box<dyn Error>> {
//! let bytes = vtpm::get_report()?;
//! let hcl_report = hcl::HclReport::new(bytes)?;
//! let tdx_report_slice = hcl_report.tdx_report_slice();
//! let report_body = imds::ReportBody::new(tdx_report_slice);
//! let td_quote_bytes = imds::get_td_quote(report_body)?;
//! let hash = hcl_report.var_data_sha256();
//! println!("var_data hash: {:x?}", hash);
//! std::fs::write("td_quote.bin", td_quote_bytes)?;
//! Ok(())
//! }
//! ```

pub use az_snp_vtpm::vtpm;

pub mod hcl;
pub mod imds;
pub mod tdx;
#[cfg(feature = "verifier")]
pub mod verify;

pub fn is_tdx_cvm() -> Result<bool, tss_esapi::Error> {
let bytes = vtpm::get_report()?;
let Ok(hcl_report) = hcl::HclReport::new(bytes) else {
return Ok(false);
};
let is_tdx = hcl_report.report_type() == hcl::ReportType::Tdx;
Ok(is_tdx)
}
20 changes: 20 additions & 0 deletions az-cvm-vtpm/az-tdx-vtpm/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use anyhow::Result;
use az_tdx_vtpm::{hcl, imds, vtpm};

fn main() -> Result<()> {
let bytes = vtpm::get_report()?;
let hcl_report = hcl::HclReport::new(bytes)?;

let hash = hcl_report.var_data_sha256();
println!("var_data hash: {:x?}", hash);

let tdx_report_slice = hcl_report.tdx_report_slice();
let report_body = imds::ReportBody::new(tdx_report_slice);
let td_quote_bytes = imds::get_td_quote(report_body)?;
std::fs::write("td_quote.bin", td_quote_bytes)?;

Ok(())
}
77 changes: 77 additions & 0 deletions az-cvm-vtpm/az-tdx-vtpm/src/tdx.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.

use serde::{Deserialize, Serialize};
use serde_big_array::BigArray;

const TDX_REPORT_DATA_LENGTH: usize = 64;

#[repr(C)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TdxReportMac {
pub report_type: u8,
pub report_sub_type: u8,
pub report_version: u8,
pub reserved_type_mbz: u8,
pub reserved_mbz1: [u8; 12],
pub cpu_svn: [u8; 16],
#[serde(with = "BigArray")]
pub tee_tcb_info_hash: [u8; 48],
#[serde(with = "BigArray")]
pub tee_info_hash: [u8; 48],
#[serde(with = "BigArray")]
pub report_data: [u8; TDX_REPORT_DATA_LENGTH],
pub reserved_mbz2: [u8; 32],
pub mac: [u8; 32],
}

#[repr(C)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TdxTeeTcbInfo {
pub tee_valid: [u8; 8],
pub tee_tcb_svn: [u8; 16],
#[serde(with = "BigArray")]
pub tee_mr_seam: [u8; 48],
#[serde(with = "BigArray")]
pub tee_mr_seam_signer: [u8; 48],
pub tee_attributes: [u8; 8],
pub tee_tcb_svn2: [u8; 16],
#[serde(with = "BigArray")]
pub tee_reserved: [u8; 95],
}

#[repr(C)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TdxRtmr {
#[serde(with = "BigArray")]
pub register_data: [u8; 48],
}

#[repr(C)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TdxTdInfo {
pub attributes: [u8; 8],
pub xfam: [u8; 8],
#[serde(with = "BigArray")]
pub mrtd: [u8; 48],
#[serde(with = "BigArray")]
pub mr_config_id: [u8; 48],
#[serde(with = "BigArray")]
pub mr_owner: [u8; 48],
#[serde(with = "BigArray")]
pub mr_owner_config: [u8; 48],
pub rtrm: [TdxRtmr; 4],
#[serde(with = "BigArray")]
pub serv_td: [u8; 48],
#[serde(with = "BigArray")]
pub reserved_mbz: [u8; 64],
}

#[repr(C)]
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)]
pub struct TdxVmReport {
pub tdx_report_mac: TdxReportMac,
pub tdx_tee_tcb_info: TdxTeeTcbInfo,
pub tdx_reserved: [u8; 17],
pub tdx_td_info: TdxTdInfo,
}
Loading

0 comments on commit 5a114c2

Please sign in to comment.