diff --git a/Cargo.toml b/Cargo.toml index dc0f63ee3d..a5bc7fa91f 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -34,6 +34,7 @@ kbs-types = "0.6.0" jsonwebtoken = { version = "9", default-features = false } log = "0.4.17" prost = "0.11.0" +pv = { version = "0.10.0", package = "s390_pv" } regorus = { version = "0.1.2", default-features = false, features = ["regex", "base64", "time"] } rstest = "0.18.1" serde = { version = "1.0", features = ["derive"] } @@ -47,3 +48,7 @@ tokio = { version = "1.23.0", features = ["full"] } tempfile = "3.4.0" tonic = "0.8.1" tonic-build = "0.8.0" + +[patch.crates-io] +s390_pv = { path = "/root/src/tmp_pv_crate/pv" } +s390_pv_core = { path = "/root/src/tmp_pv_crate/pv_core" } diff --git a/attestation-service/verifier/Cargo.toml b/attestation-service/verifier/Cargo.toml index 7deaa5e866..b3c95dea6d 100644 --- a/attestation-service/verifier/Cargo.toml +++ b/attestation-service/verifier/Cargo.toml @@ -13,7 +13,7 @@ az-tdx-vtpm-verifier = [ "az-tdx-vtpm", "openssl", "tdx-verifier" ] snp-verifier = [ "asn1-rs", "openssl", "sev", "x509-parser" ] csv-verifier = [ "openssl", "csv-rs", "codicon" ] cca-verifier = [ "ear", "jsonwebtoken", "veraison-apiclient" ] -se-verifier = [ "openssl" ] +se-verifier = [ "pv" ] [dependencies] anyhow.workspace = true diff --git a/attestation-service/verifier/src/se/ibmse.rs b/attestation-service/verifier/src/se/ibmse.rs index 3a7f354a90..b07d886066 100644 --- a/attestation-service/verifier/src/se/ibmse.rs +++ b/attestation-service/verifier/src/se/ibmse.rs @@ -3,48 +3,165 @@ // SPDX-License-Identifier: Apache-2.0 // -use anyhow::*; -use async_trait; - -#[derive(Default)] -pub struct FakeSeAttest {} - -#[async_trait::async_trait] -pub trait SeFakeVerifier { - async fn create<'a, 'b, 'c>( - &self, - _hkd_files: Vec, - _cert_file: &'a str, - _signing_file: &'b str, - _arpk_file: &'c str, - ) -> Result>; - - async fn verify<'a, 'b>( - &self, - _evidence: &[u8], - _arpk_file: &'a str, - _hdr_file: &'b str, - ) -> Result>; +use anyhow::{anyhow, bail, Context, Result}; +use log::{debug, warn}; +use pv::{ + attest::{ + AdditionalData, AttestationFlags, AttestationItems, AttestationMeasAlg, + AttestationMeasurement, AttestationRequest, AttestationVersion, ExchangeFormatCtx, + ExchangeFormatVersion, + }, + misc::{CertificateOptions, create_file, open_file, read_exact_file, write_file, HexSlice}, + request::{openssl::pkey::PKey, BootHdrTags, Confidential, ReqEncrCtx, Request, SymKey, SymKeyType}, +}; +use serde::Serialize; +use std::{fmt::Display, process::ExitCode}; + +use crate::{ + cli::{VerifyOpt, VerifyOutputType}, + EXIT_CODE_ATTESTATION_FAIL, +}; + +#[derive(Serialize)] +struct VerifyOutput<'a> { + cuid: HexSlice<'a>, + #[serde(skip_serializing_if = "Option::is_none")] + additional_data: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + additional_data_fields: Option>>, + #[serde(skip_serializing_if = "Option::is_none")] + user_data: Option>, +} + +impl<'a> VerifyOutput<'a> { + fn from_exchange(ctx: &'a ExchangeFormatCtx, flags: &AttestationFlags) -> Result { + let additional_data_fields = ctx + .additional() + .map(|a| AdditionalData::from_slice(a, flags)) + .transpose()?; + let user_data = ctx.user().map(|u| u.into()); + + Ok(Self { + cuid: ctx.config_uid()?.into(), + additional_data: ctx.additional().map(|a| a.into()), + additional_data_fields, + user_data, + }) + } } -#[async_trait::async_trait] -impl SeFakeVerifier for FakeSeAttest { - async fn create<'a, 'b, 'c>( - &self, - _hkd_files: Vec, - _cert_file: &'a str, - _signing_file: &'b str, - _arpk_file: &'c str, - ) -> Result> { - Result::Ok("test".as_bytes().to_vec()) +impl<'a> Display for VerifyOutput<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + writeln!(f, "Config UID:")?; + writeln!(f, "{:#}", self.cuid)?; + if let Some(data) = &self.additional_data { + writeln!(f, "Additional-data:")?; + writeln!(f, "{:#}", data)?; + } + if let Some(data) = &self.additional_data_fields { + writeln!(f, "Additional-data content:")?; + writeln!(f, "{:#}", data)?; + } + if let Some(data) = &self.user_data { + writeln!(f, "user-data:")?; + writeln!(f, "{:#}", data)?; + } + Ok(()) + } +} + +pub fn verify(input: mut &[u8], hdr_file: String, arpk_file: String, output: Vec, user_data: Vec) -> Result { + let mut img = open_file(&hdr_file)?; + let output = output_file.map(create_file).transpose()?; + let arpk = SymKey::Aes256( + read_exact_file(&arpk_file, "Attestation request protection key").map(Confidential::new)?, + ); + let hdr = BootHdrTags::from_se_image(&mut img)?; + let ctx = ExchangeFormatCtx::read(&mut input, true)?; + + let (auth, conf) = AttestationRequest::decrypt_bin(ctx.arcb(), &arpk)?; + let meas_key = PKey::hmac(conf.measurement_key())?; + let items = AttestationItems::from_exchange(&ctx, &hdr, conf.nonce())?; + + let measurement = + AttestationMeasurement::calculate(items, AttestationMeasAlg::HmacSha512, &meas_key)?; + + let uv_meas = ctx.measurement().ok_or(anyhow!( + "The input is missing the measurement. It is probably no attestation response" + ))?; + if !measurement.eq_secure(uv_meas) { + debug!("Measurement values:"); + debug!("Recieved: {}", HexSlice::from(uv_meas)); + debug!("Calculated: {}", HexSlice::from(measurement.as_ref())); + warn!("Attestation measurement verification failed. Calculated and received attestation measurement are not equal."); + return Ok(ExitCode::from(EXIT_CODE_ATTESTATION_FAIL)); } + warn!("Attestation measurement verified"); + // Error impossible CUID is present Attestation verified + let pr_data = VerifyOutput::from_exchange(&ctx, auth.flags())?; - async fn verify<'a, 'b>( - &self, - _evidence: &[u8], - _arpk_file: &'a str, - _hkd_files: &'b str, - ) -> Result> { - Result::Ok("test".as_bytes().to_vec()) + warn!("{pr_data}"); + serde_yaml::to_writer(&mut output, &pr_data)?, + + if let Some(user_data) = &user_data { + match ctx.user() { + Some(data) => write_file(user_data, data, "user-data")?, + None => { + warn!("Location for `user-data` specified, but respose does not contain any user-data") + } + } + }; + + Ok(ExitCode::SUCCESS) +} + +pub fn create(host_key_documents: Vec, certs: Vec, crls: Vec, arpk_file: String) -> Result> { + let att_version = AttestationVersion::One; + let meas_alg = AttestationMeasAlg::HmacSha512; + + let mut att_flags = AttestationFlags::default(); + att_flags.set_image_phkh(); + + let mut arcb = AttestationRequest::new(att_version, meas_alg, att_flags)?; + debug!("Generated Attestation request"); + + let certificate_args: CertificateOptions { + host_key_documents, + no_verify: true, + certs, + crls, + offline: true, + // root_ca:, } -} \ No newline at end of file + // Add host-key documents + certificate_args + .get_verified_hkds("attestation request")? + .into_iter() + .for_each(|k| arcb.add_hostkey(k)); + debug!("Added all host-keys"); + + let encr_ctx = + ReqEncrCtx::random(SymKeyType::Aes256).context("Failed to generate random input")?; + let ser_arcb = arcb.encrypt(&encr_ctx)?; + warn!("Successfully generated the request"); + + let output: Vec + let exch_ctx = ExchangeFormatCtx::new_request( + ser_arcb, + meas_alg.exp_size(), + arcb.flags().expected_additional_size().into(), + )?; + exch_ctx.write(&mut output, ExchangeFormatVersion::One)?; + + let arpk = match encr_ctx.prot_key() { + SymKey::Aes256(k) => k, + _ => bail!("Unexpected key type"), + }; + write_file( + &arpk_file, + arpk.value(), + "Attestation request Protection Key", + )?; + + Result::Ok(output) +} diff --git a/attestation-service/verifier/src/se/mod.rs b/attestation-service/verifier/src/se/mod.rs index 56aa4ff373..6b8e17ca3a 100644 --- a/attestation-service/verifier/src/se/mod.rs +++ b/attestation-service/verifier/src/se/mod.rs @@ -7,9 +7,10 @@ use super::*; use async_trait::async_trait; use base64::prelude::*; use serde_json::json; +use pv::misc::CertificateOptions; +use pv::cli::{AttAddFlags, CreateAttOpt}; use crate::{InitDataHash, ReportData}; -use crate::se::ibmse::FakeSeAttest; -use crate::se::ibmse::SeFakeVerifier; +use crate::se::ibmse; pub mod ibmse; @@ -31,17 +32,13 @@ impl Verifier for SeVerifier { &self, _tee_parameters: String, ) -> Result { - - // TODO replace FakeSeAttest with real IBM SE crate - let attester = FakeSeAttest::default(); - // TODO replace the placeholder let hkds: Vec = vec![String::new(); 2]; - let certk = "cert_file_path"; - let signk = "sign_file_path"; + let certs: Vec = vec![String::new(); 2]; + let crls: None; let arpk = "arpk_file_path"; - let challenge = attester.create(hkds, certk, signk, arpk) + let challenge = ibmse.create(hkds, certs, crls, arpk) .await .context("Create SE attestation request failed: {:?}")?; @@ -54,21 +51,18 @@ async fn verify_evidence( _expected_report_data: &ReportData<'_>, _expected_init_data_hash: &InitDataHash<'_>, ) -> Result { - // TODO replace FakeSeAttest with real IBM SE crate - let attester = FakeSeAttest::default(); - // TODO replace the placeholder let arpk = "arpk_file_path"; let hdr = "hdr_file_path"; - let se = attester.verify(evidence, arpk, hdr) - .await - .context("Verify SE attestation evidence failed: {:?}")?; + let output: Vec; + let userdata: Vec; + let res = ibmse.verify(evidence, hdr, arpk, output, userdata); let claims_map = json!({ "serial_number": format!("{}", "SE-ID"), - "measurement": format!("{}", BASE64_STANDARD.encode(se.clone())), - "report_data": format!("{}", BASE64_STANDARD.encode(se.clone())), + "measurement": format!("{}", BASE64_STANDARD.encode(output.clone())), + "report_data": format!("{}", BASE64_STANDARD.encode(user_data.clone())), }); Ok(claims_map as TeeEvidenceParsedClaim)