【Confidential Computing - Big Manufacturers Have Something to Say】AMD

Proof of SEV-SNP platform based on VirTEE/SEV

Publication number 58217, version v1.2, released on 2023.7

1 Introduction

        The VirTEE/sev toolbox provides a simple and easy-to-use API based on the rust language to access the security processor in the AMD EPYC processor. This library has already supported the traditional SEV firmware, and recently added a new one in the VirTEE community. The SEV-SNP firmware supports mainly 3rd generation and newly released AMD EPYC processors. Some potential solutions for platform attestation of Trusted Execution Environment TEE using AMD SEV-SNP technology are included in the user manual, mainly for the following users:

  • Platform owner: Management is used to deploy virtual machines and container system software, such as hosts or cloud service providers. These types of software mainly refer to the virtual machine monitor hypervisor that runs confidential virtual machines and machine containers.
  • Guest Owner: Refers to who deploys the workload

        This user guide explores some of the new capabilities that the SEV-SNP technology update to the VirTEE/sev toolbox brings to platform owners and guest owners, provides some use cases, and explains some configuration options of concern to users.

2. Platform owner

        Platform owners are system software, including hypervisors, used to deploy confidential virtual machines or containers.

2.1 System Requirements

  • Hardware: The system must be a 3rd generation or newer AMD EPYC processor
  • Firmware: The BIOS version that supports the latest SEV firmware must be used to ensure that it has corresponding security protection functions. VirTEE/sev SEV-SNP feature compatible in version 1.54.01
  • Kernel: SEV-SNP is under development, ADM recommends using the latest kernel patch upstream
  • Software: Generally speaking, AMD recommends using the upstream OVMF/EDK II and QEMU 7.1, but for platform owners or guest owners who have the following requirements, it is recommended to use the latest OVMF/EDK II and the latest QEMU

2.2 API capabilities

        Platform owners should:

  • Request AMD Secure Processor Status
  • Load new extension configuration
  • Request an existing extension configuration

2.2.1 Request AMD Secure Processor Status

        Before configuring the host platform to cache certificates, AMD recommends checking the status of the AMD Secure Processor, including the following steps:

1. Include VirTEE/sev in user rust project

// Import library
use sev::firmware::host::*;

2. Connect to the firmware and request the status of the AMD security processing snp

// Open a connection to the firmware.
let mut firmware: Firmware = Firmware::open().unwrap();
// Request the current snp status of the AMD Secure Processor.
let snp_status: SnpPlatformStatus = firmware.snp_platform_status().unwrap();

2.2.2 Load new extension configuration

        The storage policy and certificate chain in the hypervisor memory enable the platform owner to improve the user experience of the guest owner, avoiding the trouble of requesting a certificate from the AMD key distribution server KDS, and also reducing the CSP's dependence on AMD KDS, and more importantly , which avoids the hassle of manually requesting a certificate chain when deploying VMs/containers at scale.

 1. Include VirTEE/sev into the Rust project

// Import library
use sev::firmware::host::*;

2. Read the certificate

// Read certificate bytes.
pub const ARK: &[u8] = include_bytes!("ark.pem");
pub const ASK: &[u8] = include_bytes!("ask.pem");
pub const VCEK: &[u8] = include_bytes!("vcek.pem");

3. Create a configuration to extend the report

  • Certificates only: Allows the hypervisor to store certificate chains without modifying the reported TCB
// Generate a vector of certificates to store in hypervisor memory.
let certificates: Vec<CertTableEntry> = vec![
 CertTableEntry::new(CertType::ARK, ARK.to_vec()),
 CertTableEntry::new(CertType::ASK, ASK.to_vec()),
 CertTableEntry::new(CertType::VCEK, VCEK.to_vec()),
];
// Call the `new_certs_only` constructor to generate the extended configuration.
let ext_config: ExtConfig = ExtConfig::new_certs_only(
 certificates
);
  • Configuration only: configure and update the reported TCB content without storing the certificate chain
// Specify the desired configuration
let configuration: Config = Config::new(
 TcbVersion::new(3, 0, 10, 169),
 0,
);
// Call the `new_config_only` constructor to generate the extended configuration.
let ext_config: ExtConfig = ExtConfig::new_config_only(
 configuration
);
  • Configuration and certificates: change TCB report and store certificate chain
// Specify the desired configuration
let configuration: Config = Config::new(
 TcbVersion::new(3, 0, 10, 169),
 0,
);
// Generate a vector of certificates to store in hypervisor memory.
let certificates: Vec<CertTableEntry> = vec![
 CertTableEntry::new(CertType::ARK, ARK.to_vec()),
 CertTableEntry::new(CertType::ASK, ASK.to_vec()),
 CertTableEntry::new(CertType::VCEK, VCEK.to_vec()),
];
// Call the `new` constructor to generate the extended configuration.
let ext_config: ExtConfig = ExtConfig::new(
 configuration,
 certificates
);

4. Connect the firmware and forward the extension request to the AMD Secure Processor

// Open a connection to the firmware.
let mut fw: Firmware = Firmware::open().unwrap();
// Forward the certificates to the AMD Secure Processor to be loaded.
if let Err(error) = fw.snp_reset_config(&ext_config) {
 // Handle an error if one is encountered.
 ...
}

2.2.3 Requesting an Existing Extended Configuration

Requesting an existing extension configuration involves the following steps:

  1. Include VirTEE/sev into the Rust project

// Import library
use sev::firmware::host::*;

2. Connect to the firmware and request the current configuration

// Open a connection to the firmware.
let mut fw: Firmware = Firmware::open().unwrap();
// Request the current configuration.
let current_configuration: ExtConfig = fw.snp_get_ext_config().unwrap();

3. Guest Owner

        The guest owner is a tenant of the virtualization provider, which may own one or more confidential virtual machines or containers in environments that do not belong to the platform owner.

3.1 System requirements

Guest level support has been fully implemented, use Linux Kernel 5.19 or newer to access the guest driver.

3.2 API capabilities

Guest owners can:

  • Request a Standard Attestation Report
  • Request an Extended Attestation Report
  • Request a unique cryptographic key derived from a hardware key

3.2.1 Standard Proof Warranty Request and Attestation

1. Import the required code modules from the toolbox

// Import the modules
use sev::firmware::guest::* ;

2. Create 64 bytes of unique data for attestation reports

// This could be a unique message, a public key, etc.
let unique_data: [u8; 64] = [
 65, 77, 68, 32, 105, 115, 32, 101, 120, 116, 114, 101, 109, 101, 108, 121, 32, 97, 
119,
 101, 115, 111, 109, 101, 33, 32, 87, 101, 32, 109, 97, 107, 101, 32, 116, 104, 101, 
32,
 98, 101, 115, 116, 32, 67, 80, 85, 115, 33, 32, 65, 77, 68, 32, 82, 111, 99, 107, 
115,
 33, 33, 33, 33, 33, 33,
];

3. Connect to the firmware and request the SEV-SNP attestation report

// Open a connection to the firmware.
let mut fw: Firmware = Firmware::open()?;
// Request a standard attestation report.
let attestation_report: AttestationReport = fw.get_report(None, Some(unique_data), 
None);

4. Verify the root of trust. Verifying the root of trust is the most important step in the entire certification process. The ope https://crates.io/crates/openssl nssl toolbox provides all the tools used to verify the signature of the certificate chain. The VirTEE/sev library also contains some simplified certificates. Processing structures and functions. The current AMD roots of trust are:

  • AMD root key ARK is self-signed
  • ARK signs the ADM signing key ASK
  • ASK signs the chip version endorsement key VCEK

for example:

  • Import the necessary certificate handling logic
use sev::{
 certs::snp::{ca, Certificate, Chain},
 firmware::host::CertType,
};
  • Import related openssl library
 ecdsa::EcdsaSig,
 pkey::{PKey, Public},
 sha::Sha384,
 x509::X509,
  • Pull the certificate chain from the AMD key distribution system AKS, refer to the VCEK specification . All fields are at least 2 bytes long and are zero-padded. Prove that the hwid and chip_id in the report match
const KDS_CERT_SITE: &str = "https://kdsintf.amd.com";
const KDS_VCEK: &str = "/vcek/v1";
const KDS_CERT_CHAIN: &str = "cert_chain";
/// Requests the certificate-chain (AMD ASK + AMD ARK)
/// These may be used to verify the downloaded VCEK is authentic.
pub fn request_cert_chain (sev_prod_name: &str) -> (ask, ark) {
 // Should make -> https://kdsintf.amd.com/vcek/v1/{SEV_PROD_NAME}/cert_chain
 let url: String = format!("{KDS_CERT_SITE}{KDS_VCEK}/{sev_prod_name}/
{KDS_CERT_CHAIN}");
 println!("Requesting AMD certificate-chain from: {url}");
 let rsp: Response = get(&url).unwrap();
let body: Vec<u8> = rsp.bytes().unwrap().to_vec(); 
let chain: Vec<x509> = X509::stack_from_pem(&body).unwrap();
// Create a ca chain with ark and ask
let ca_chain: ca::Chain = ca::Chain::from_pem(&chain[1].to_pem, &chain[0].to_pem);
ca_chain
};
/// Requests the VCEK for the specified chip and TCP
pub fn request_vcek(chip_id: [u8; 64], reported_tcb: TcbVersion) -> X509 {
 let hw_id: String = hexify(&chip_id);
 let url: String = format!(
 "{KDS_CERT_SITE}{KDS_VCEK}/{SEV_PROD_NAME}/\
 {hw_id}?blSPL={:02}&teeSPL={:02}&snpSPL={:02}&ucodeSPL={:02}",
 reported_tcb.boot_loader,
 reported_tcb.microcode
 ); 
 println!("Requesting VCEK from: {url}\n");
 let rsp_bytes = get(&url).unwrap().bytes().unwrap().to_vec();
 Certificate::from_der(&rsp_bytes)
};
  • Verify root of trust
let ca_chain: ca::Chain = request_cert_chain("milan");
// chip_id and reported_tcb should be pulled from the host machine,
// or an attestation report. let vcek: Certificate = request_vcek(
chip_id,
reported_tcb
);
// Create a full-chain with the certificates:
Let cert_chain = Chain{ca: ca_chain, vcek: vcek};
//Now you can simply verify the whole chain in one command.
cert_chain.verify().unwrap();
//Or you can verify each certificate individually
let ark = cert_chain.ca.ark;
let ask = cert_chain.ca.ask;
if (&ark,&ark).verify().unwrap() {
 println!("The AMD ARK was self-signed...");
 if (&ark,&ask).verify().unwrap() {
 iprintln!("The AMD ASK was signed by the AMD ARK...");
 f (&ask,&vcek).verify().unwrap() {
 println!("The VCEK was signed by the AMD ASK...");
} else {
 eprintln!("The VCEK was not signed by the AMD ASK!");
 }
} else {
 eprintln!("The AMD ASK was not signed by the AMD ARK!");
 }
} else {
 eprintln!("The AMD ARK is not self-signed!");
}

5. Verify the Guest Trusted Computer TCB by verifying the following domains and VCEK in the Attestation Report

  • Bootloader
  • TEE
  • SNP
  • Microcode
  • Chip ID

Neither openssl nor the VirTEE/sev toolkit support x509v3 extended authentication, a trusted solution is to use asn1 rs of the x509 parser toolkit  . The following example is built from these definitions:

/
******************************************************************************************
 * RELEVANT X509v3 EXTENSION OIDS
******************************************************************************************
/
use asn1_rs::{oid, Oid};
use x509_parser::{
 self,
 certificate::X509Certificate,
 pem::{parse_x509_pem, Pem},
 prelude::X509Extension,
};
enum SnpOid {
 BootLoader,
 Tee,
 Snp,
 Ucode,
 HwId,
}
impl SnpOid {
 fn oid(&self) -> Oid {
 match self {
 SnpOid::BootLoader => oid!(1.3.6.1.4.1.3704.1.3.1),
 SnpOid::Tee => oid!(1.3.6.1.4.1.3704.1.3.2),
 SnpOid::Snp => oid!(1.3.6.1.4.1.3704.1.3.3),
 SnpOid::Ucode => oid!(1.3.6.1.4.1.3704.1.3.8),
 SnpOid::HwId => oid!(1.3.6.1.4.1.3704.1.4),
 }
 }
}
impl std::fmt::Display for SnpOid {
 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
 write!(f, "{}", self.oid().to_id_string())
 }
}

/
******************************************************************************************
 * HELPER FUNCTIONS
******************************************************************************************
/
fn check_cert_ext_byte(ext: &X509Extension, val: u8) -> bool {
 if ext.value[0] != 0x2 {
 panic!("Invalid type encountered!");
 }
 
 if ext.value[1] != 0x1 && ext.value[1] != 0x2 {
 panic!("Invalid octet length encountered");
 }
 
 if let Some(byte_value) = ext.value.last() {
 *byte_value == val
 } else {
 false
 }
}
fn check_cert_ext_bytes(ext: &X509Extension, val: &[u8]) -> bool {
 ext.value == val
}
/
******************************************************************************************
 * EXAMPLE ATTESTATION FUNCTION:
******************************************************************************************
/
fn validate_cert_metadata(
 cert: &X509Certificate,
 report: &AttestationReport,
) -> bool {
 let extensions: HashMap<Oid, &X509Extension> = cert.extensions_map().unwrap();
 
 if let Some(cert_bl) = extensions.get(&SnpOid::BootLoader.oid()) {
 if !check_cert_ext_byte(cert_bl, report.reported_tcb.boot_loader) {
 eprintln!("Report TCB Boot Loader and Certificate Boot Loader mismatch 
encountered.");
 return false;
 }
 println!("Reported TCB Boot Loader from certificate matches the attestation 
report.");
 }
 
 if let Some(cert_tee) = extensions.get(&SnpOid::Tee.oid()) {
 if !check_cert_ext_byte(cert_tee, report.reported_tcb.tee) {
 eprintln!("Report TCB TEE and Certificate TEE mismatch encountered.");
 return false;
 }
 println!("Reported TCB TEE from certificate matches the attestation report.");
 }
 
 if let Some(cert_snp) = extensions.get(&SnpOid::Snp.oid()) {
 if !check_cert_ext_byte(cert_snp, report.reported_tcb.snp) {
 eprintln!("Report TCB SNP and Certificate SNP mismatch encountered.");
 return false;
 }
 println!("Reported TCB SNP from certificate matches the attestation report.");
 }
 
 if let Some(cert_ucode) = extensions.get(&SnpOid::Ucode.oid()) {
 if !check_cert_ext_byte(cert_ucode, report.reported_tcb.microcode) {
 eprintln!("Report TCB Microcode and Certificate Microcode mismatch 
encountered.");
 return false;
 }
 println!("Reported TCB Microcode from certificate matches the attestation 
report.");
 }
 
 if let Some(cert_hwid) = extensions.get(&SnpOid::HwId.oid()) {
 if !check_cert_ext_bytes(cert_hwid, &report.chip_id) {
 eprintln!("Report TCB Microcode and Certificate Microcode mismatch 
encountered.");
 return false;
 }
 println!("Chip ID from certificate matches the attestation report.");
 }
 
 true
}

6. Verify that the signature in the report is indeed issued by VCEK

let ar_signature: EcdsaSig = EcdsaSig::try_from(&report.signature).unwrap();
let signed_bytes: &[u8] = &bincode::serialize(&report).unwrap()[0x0..0x2A0];
let amd_vcek_pubkey: EcKey<Public> = vcek.public_key().unwrap().ec_key().unwrap();
let mut hasher: Sha384 = Sha384::new()
hasher.update(signed_bytes);
let base_message_digest: [u8; 48] = hasher.finish();
if ar_signature.verify(base_message_digest.as_ref(), vcek_pubkey.as_ref()).unwrap() {
 println!("VCEK signed the Attestation Report!");
} else {
 eprintln!("VCEK did NOT sign the Attestation Report!");
}
// Or you can use a complete certificate chain to verify the attestation report
(&report, &certificate_chain).verify().unwrap()

3.2.2 Request for Extended Attestation Report and Attestation

1. Create 64 bytes of unique data to prove the report

// This could be a unique message, a public key, etc.
let unique_data: [u8; 64] = [
 65, 77, 68, 32, 105, 115, 32, 101, 120, 116, 114, 101, 109, 101, 108, 121, 32, 97, 
119,
 101, 115, 111, 109, 101, 33, 32, 87, 101, 32, 109, 97, 107, 101, 32, 116, 104, 101, 
32,
 98, 101, 115, 116, 32, 67, 80, 85, 115, 33, 32, 65, 77, 68, 32, 82, 111, 99, 107, 
115,
 33, 33, 33, 33, 33, 33,
];

2. Connect to firmware and request extended report

let mut fw: Firmware = Firmware::open().unwrap();
let (extended_report, certificates): (AttestationReport, Vec<CertTableEntry>) = 
fw.get_ext_report(None, Some(unique_data), 0)

3. Use VirTEE/SEV library to parse ARK, ASK, VCEK from AMD security processor

// Assumes all certificates are in PEM format (for simplicity).
let certs: Chain = Chain::from_cert_table_pem(certificates).unwrap();

4. Authenticate using the standard attestation report root of trust, skipping connecting to the AMD key distribution server.

3.2.3 Request a derived key

        There are many use cases for guest owners to derive unique encryption keys based on a hardware root of trust. This derived key may depend on multiple TCB component parameters, and only the same parameters can be used to derive the same key.

1. Take away a DerivedKey according to the specification

let request: DerivedKey = DerivedKey::new(false, GuestFieldSelect(1), 0, 0, 0);

2. Connect to the firmware and request a derived key

let mut fw: Firmware = Firmware::open().unwrap();
let derived_key: [u8; 32]= fw.get_derived_key(None, request).unwrap();

Guess you like

Origin blog.csdn.net/BillyThe/article/details/132146924