AMD SEV-SNP rust utils and primitives.
Instead of cargo test
, run:
shell
make test
| Variable | Default | Description | |----------------------------|--------------------|-----------------------------------------------| | SEVSNPCACHEPATH | /var/cache/sev-snp | Path to store downloaded certs. | | SEVSNPCACHEENTRIES_VCEK | 100 | Max cache entries for VCEK certs (in-memory). |
To request a report from a SEV-SNP capable CPU (the same functionality as sev-guest-get-report
):
```rust use sevsnputils::{AttestationReport, Requester};
fn main() { let report = AttestationReport::request() .expect("failed to request guest report");
println!("version: {:?}", report.version);
// Or raw bytes
let report_bytes = AttestationReport::request_raw()
.expect("failed to request guest report");
println!("bytes len: {:?}", report_bytes.len());
} ```
Parse a guest_report.bin file from sev-guest-get-report
(or one saved from AttestationReport::request_raw()
):
```rust use sevsnputils::AttestationReport;
fn main() { let report = AttestationReport::fromfile("./guestreport.bin") .expect("failed to parse guest report");
println!("version: {:?}", report.version);
println!("guest_svn: {:?}", report.guest_svn);
println!("policy: {:?}", report.policy);
println!("platform_version: {:?}", report.platform_version.raw_decimal());
println!("measurement: {}", report.measurement_hex());
println!("report data: {}", report.report_data_hex());
println!("id key digest: {}", report.id_key_digest_hex());
println!("author key digest: {}", report.author_key_digest_hex());
println!("chip id: {}", report.chip_id_hex());
println!("hash: {}", report.sha384_hex());
println!("signature:");
println!(" r: {}", report.signature.r_hex());
println!(" s: {}", report.signature.s_hex());
} ```
The verification process:
Verify a guest_report.bin file:
```rust use sevsnputils::{AttestationReport, Verification, Policy};
async fn verifyguest() { let report = AttestationReport::fromfile("./guest_report.bin") .expect("failed to parse guest report");
let res = report.verify(Some(Policy::permissive())).await
.expect("failed to call verify");
if !res {
panic!("verification failed");
}
} ```
You may also use Policy::strict()
or make your own policy:
rust
let policy = Policy::new(
true, // require_no_debug
true, // require_no_ma
true, // require_no_smt
true, // require_id_key
true // require_author_key
);
You may also obtain the certificates to work with them directly:
```rust use sevsnputils::{ AttestationReport, KdsCertificates, CertFormat, getkdsarkaskcertsbytes, getkdsarkaskcerts, getkdsarkaskcertsandvalidate, validatearkaskvcekcerts, PRODUCTNAME_MILAN };
async fn getcerts() { let report = AttestationReport::fromfile("./guest_report.bin") .expect("failed to parse guest report");
// VCEK
// Raw bytes as PEM or DER (cached only on disk)
let pem_bytes = report.get_kds_vcek_cert_bytes(CertFormat::PEM).await
.expect("failed to get VCEK PEM");
let der_bytes = report.get_kds_vcek_cert_bytes(CertFormat::DER).await
.expect("failed to get VCEK DER");
// X509 (cached in-memory, prefer this method)
let cert = report.get_kds_vcek_cert().await
.expect("failed to get VCEK cert");
// ARK & ASK
// Raw bytes as PEM or DER (cached only on disk)
let (ark_pem, ask_pem) = get_kds_ark_ask_certs_bytes(PRODUCT_NAME_MILAN, CertFormat::PEM).await
.expect("failed to get ARK/ASK PEMs");
// X509 (cached in-memory, prefer this method)
let (ark_cert, ask_cert) = get_kds_ark_ask_certs(PRODUCT_NAME_MILAN).await
.expect("failed to get ARK/ASK certs");
// X509 validated (cached in-memory, prefer this method)
let (ark_cert, ask_cert) = get_kds_ark_ask_certs_and_validate(PRODUCT_NAME_MILAN).await
.expect("failed to get ARK/ASK certs");
// Validate
validate_ark_ask_vcek_certs(&ark_cert, &ask_cert, Some(&cert))
.expect("failed to validate certs");
} ```
```rust use std::fs; use std::path::PathBuf; use sevsnputils::{ calclaunchdigest, SevMode, CpuType };
fn main() { let ovmfpath = PathBuf::from("./OVMFCODE.fd"); let kernelpath = PathBuf::from("./vmlinuz"); let appendpath = PathBuf::from("./vmlinuz.cmdline"); let initrd_path = PathBuf::from("./initrd.img");
let append = fs::read_to_string(&append_path)
.expect(format!("failed to read '{:?}'", &append_path).as_str());
let digest = calc_launch_digest(SevMode::SevSnp, 64, ovmf_path.as_path(),
Some(kernel_path.as_path()), Some(initrd_path.as_path()),
Some(append.as_str()))
.expect("failed to calculate launch digest");
} ```
Before you can generate an IdBlock
and IdAuthInfo
you'll first need to create some ECDSA keys (pem files).
```shell openssl genpkey -algorithm ec -pkeyopt ecparamgencurve:"P-384" -out id-key.pem
openssl genpkey -algorithm ec -pkeyopt ecparamgencurve:"P-384" -out author-key.pem ```
```rust use std::path::PathBuf; use sevsnputils::{ createidentityblock, LaunchDigest, FamilyId, ImageId, ToBase64 };
fn main() { let idkeypem = PathBuf::from("./id-key.pem"); let authorkeypem = PathBuf::from("./author-key.pem");
let measurement = LaunchDigest::from_str("ffb0cb7f01a5d5b122430d66f211326ab5cf11a9a5d3189ec53adf9a60730bc63d9856fe9fe602abd662861d0ee36007");
let family_id = FamilyId::zeroes();
let image_id = ImageId::from_str("ffb0cb7f01a5d5b122430d66f211326a");
let guest_svn = 0;
let policy = 0x30000;
let (id_block, id_auth_info) = create_identity_block(measurement, family_id, image_id,
guest_svn, policy, id_key_pem.as_path(),
Some(author_key_pem.as_path()))
.expect("failed to create identity block");
println!("id_block: {}", id_block.to_base64().unwrap()); // Or call save_base64().
println!("id_auth_info: {}", id_auth_info.to_base64().unwrap());
} ```
```rust use std::path::PathBuf; use sevsnputils::{ IdBlock, LaunchDigest, FamilyId, ImageId, BlockSigner, ToBase64 };
fn main() { let idkeypem = PathBuf::from("./id-key.pem"); let authorkeypem = PathBuf::from("./author-key.pem");
let id_block = IdBlock::default()
.with_ld(LaunchDigest::from_str("ffb0cb7f01a5d5b122430d66f211326ab5cf11a9a5d3189ec53adf9a60730bc63d9856fe9fe602abd662861d0ee36007"))
.with_family_id(FamilyId::zeroes())
.with_image_id(ImageId::from_str("ffb0cb7f01a5d5b122430d66f211326a"))
.with_guest_svn(0)
.with_policy(0x30000);
let id_auth_info = id_block.sign(id_key_pem.as_path(), Some(author_key_pem.as_path()))
.expect("failed to sign id block");
println!("id_block: {}", id_block.to_base64().unwrap()); // Or call save_base64().
println!("id_auth_info: {}", id_auth_info.to_base64().unwrap());
} ```
```rust use std::path::PathBuf; use sevsnputils::{ fingerprintidkeyashex };
fn main() { let idkeypem = PathBuf::from("./id-key.pem"); let authorkeypem = PathBuf::from("./author-key.pem");
let id_fingerprint = fingerprint_id_key_as_hex(id_key_pem.as_path()) // or fingerprint_id_key()
.expect("failed to fingerprint");
let author_fingerprint = fingerprint_id_key_as_hex(author_key_pem.as_path())
.expect("failed to fingerprint");
println!("id_fingerprint: {}", id_fingerprint);
println!("author_fingerprint: {}", author_fingerprint);
} ```
The guest can ask the firmware to provide a key derived from a root key contained within the AMD SEV-SNP PSP. This key may be used by the guest for any purpose it chooses, such as sealing keys (i.e. for disk encryption) or communicating with external entities. Usually the intention will be that this can be used to create a key that's only known to the guest.
Prepare the request using DerivedKeyRequestedBuilder
like so:
rust
let options = DerivedKeyRequestBuilder::new()
.with_tcb_version()
.with_image_id()
.build();
The example above mixes the TCB version provided by the guest and the image ID provided at launch into the derived key.
Here is the complete list of builder methods you can use to mix different data into the derived key:
with_tcb_version
: mixes in the TCB version provided by the guest.with_svn
: mixes in the SVN of the guest.with_launch_measurement
: mixes in the measurement of the guest at launch.with_family_id
: mixes in the family ID at launch.with_image_id
: mixes in the image ID at launch.with_policy
: mixes in the guest policy at launch.Pass in the DerivedKeyRequestOptions
struct to the DerivedKey::request
method like so:
rust
let derived_key = DerivedKey::request(options).unwrap();
Here is a MCVE of how to request a derived key from the firmware:
```rust use sevsnputils::guest::derivedkey::getderivedkey::{DerivedKeyRequester, DerivedKeyRequestBuilder}; use sevsnputils::guest::derivedkey::derived_key::DerivedKey;
fn main() { let options = DerivedKeyRequestBuilder::new() .withtcbversion() .withimageid() .build(); println!("Options: {:?}", options);
let derived_key = DerivedKey::request(options).unwrap();
println!("Derived Key: {:?}", derived_key);
} ```