-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #38 from mkulke/mkulke/az-tdx-vtpm
Add experimental TDX attester/verifier crate (#35)
- Loading branch information
Showing
35 changed files
with
481 additions
and
25 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,7 @@ | ||
name: e2e | ||
|
||
on: | ||
pull_request: | ||
push: | ||
branches: [ "main" ] | ||
|
||
jobs: | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
target | ||
arm/*.json | ||
Cargo.lock | ||
*.swp |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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). |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
2 changes: 1 addition & 1 deletion
2
az-snp-vtpm/example/Cargo.toml → az-cvm-vtpm/az-snp-vtpm/example/Cargo.toml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" | ||
|
||
|
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,169 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// Licensed under the MIT License. | ||
|
||
use crate::tdx::TdxVmReport; | ||
use jsonwebkey::JsonWebKey; | ||
use memoffset::offset_of; | ||
use serde::{Deserialize, Serialize}; | ||
use serde_big_array::BigArray; | ||
use sev::firmware::guest::AttestationReport as SnpVmReport; | ||
use sha2::{Digest, Sha256}; | ||
use std::mem; | ||
use thiserror::Error; | ||
|
||
const HCL_AKPUB_KEY_ID: &str = "HCLAkPub"; | ||
const MAX_REPORT_SIZE: usize = mem::size_of::<SnpVmReport>(); | ||
const MIN_REPORT_SIZE: usize = mem::size_of::<TdxVmReport>(); | ||
const SNP_REPORT_TYPE: u32 = 2; | ||
const TDX_REPORT_TYPE: u32 = 4; | ||
|
||
#[derive(Error, Debug)] | ||
pub enum HclError { | ||
#[error("invalid report type")] | ||
InvalidReportType, | ||
#[error("AkPub not found")] | ||
AkPubNotFound, | ||
#[error("binary parse error")] | ||
BinaryParseError(#[from] bincode::Error), | ||
#[error("JSON parse error")] | ||
JsonParseError(#[from] serde_json::Error), | ||
} | ||
|
||
#[derive(Deserialize, Debug)] | ||
struct VarDataKeys { | ||
keys: Vec<JsonWebKey>, | ||
} | ||
|
||
#[repr(u32)] | ||
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq)] | ||
enum IgvmHashType { | ||
Invalid = 0, | ||
Sha256, | ||
Sha384, | ||
Sha512, | ||
} | ||
|
||
#[repr(C)] | ||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] | ||
struct IgvmRequestData { | ||
data_size: u32, | ||
version: u32, | ||
report_type: u32, | ||
report_data_hash_type: IgvmHashType, | ||
variable_data_size: u32, | ||
variable_data: [u8; 0], | ||
} | ||
|
||
#[repr(C)] | ||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] | ||
struct AttestationHeader { | ||
signature: u32, | ||
version: u32, | ||
report_size: u32, | ||
request_type: u32, | ||
status: u32, | ||
reserved: [u32; 3], | ||
} | ||
|
||
#[repr(C)] | ||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] | ||
struct HwReport { | ||
tdx_vm_report: TdxVmReport, | ||
#[serde(with = "BigArray")] | ||
_padding: [u8; MAX_REPORT_SIZE - MIN_REPORT_SIZE], | ||
} | ||
|
||
#[repr(C)] | ||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] | ||
struct AttestationReport { | ||
header: AttestationHeader, | ||
hw_report: HwReport, | ||
hcl_data: IgvmRequestData, | ||
} | ||
|
||
pub struct HclReport { | ||
bytes: Vec<u8>, | ||
attestation_report: AttestationReport, | ||
report_type: ReportType, | ||
} | ||
|
||
#[derive(Copy, Clone, Debug, PartialEq)] | ||
pub enum ReportType { | ||
Tdx, | ||
Snp, | ||
} | ||
|
||
impl HclReport { | ||
pub fn new(bytes: Vec<u8>) -> Result<Self, HclError> { | ||
let attestation_report: AttestationReport = bincode::deserialize(&bytes)?; | ||
let report_type = match attestation_report.hcl_data.report_type { | ||
TDX_REPORT_TYPE => ReportType::Tdx, | ||
SNP_REPORT_TYPE => ReportType::Snp, | ||
_ => return Err(HclError::InvalidReportType), | ||
}; | ||
|
||
let report = Self { | ||
bytes, | ||
attestation_report, | ||
report_type, | ||
}; | ||
Ok(report) | ||
} | ||
|
||
pub fn report_type(&self) -> ReportType { | ||
self.report_type | ||
} | ||
|
||
pub fn tdx_report_slice(&self) -> &[u8] { | ||
let tdx_report_offset = offset_of!(AttestationReport, hw_report); | ||
let tdx_report_end = tdx_report_offset + mem::size_of::<TdxVmReport>(); | ||
&self.bytes[tdx_report_offset..tdx_report_end] | ||
} | ||
|
||
pub fn var_data_sha256(&self) -> [u8; 32] { | ||
if self.attestation_report.hcl_data.report_data_hash_type != IgvmHashType::Sha256 { | ||
unimplemented!( | ||
"Only SHA256 is supported, got {:?}", | ||
self.attestation_report.hcl_data.report_data_hash_type | ||
); | ||
} | ||
let mut hasher = Sha256::new(); | ||
hasher.update(self.var_data_slice()); | ||
let hash = hasher.finalize(); | ||
hash.into() | ||
} | ||
|
||
fn var_data_slice(&self) -> &[u8] { | ||
let var_data_offset = | ||
offset_of!(AttestationReport, hcl_data) + offset_of!(IgvmRequestData, variable_data); | ||
let hcl_data = &self.attestation_report.hcl_data; | ||
let var_data_end = var_data_offset + hcl_data.variable_data_size as usize; | ||
&self.bytes[var_data_offset..var_data_end] | ||
} | ||
|
||
pub fn ak_pub(&self) -> Result<JsonWebKey, HclError> { | ||
let VarDataKeys { keys } = serde_json::from_slice(self.var_data_slice())?; | ||
let ak_pub = keys | ||
.into_iter() | ||
.find(|key| { | ||
let Some(ref key_id) = key.key_id else { | ||
return false; | ||
}; | ||
key_id == HCL_AKPUB_KEY_ID | ||
}) | ||
.ok_or(HclError::AkPubNotFound)?; | ||
Ok(ak_pub) | ||
} | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use super::*; | ||
|
||
#[test] | ||
fn parse_hcl_report() { | ||
let bytes: &[u8] = include_bytes!("../test/hcl_report.bin"); | ||
let hcl_report = HclReport::new(bytes.to_vec()).unwrap(); | ||
let _ = hcl_report.ak_pub().unwrap(); | ||
} | ||
} |
Oops, something went wrong.