Skip to content

Commit

Permalink
Verifier: IBM SE implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Qi Feng Huo <[email protected]>
  • Loading branch information
Qi Feng Huo committed May 7, 2024
1 parent 708167d commit c9f18e6
Show file tree
Hide file tree
Showing 4 changed files with 174 additions and 58 deletions.
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand All @@ -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" }
2 changes: 1 addition & 1 deletion attestation-service/verifier/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
197 changes: 157 additions & 40 deletions attestation-service/verifier/src/se/ibmse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>,
_cert_file: &'a str,
_signing_file: &'b str,
_arpk_file: &'c str,
) -> Result<Vec<u8>>;

async fn verify<'a, 'b>(
&self,
_evidence: &[u8],
_arpk_file: &'a str,
_hdr_file: &'b str,
) -> Result<Vec<u8>>;
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<HexSlice<'a>>,
#[serde(skip_serializing_if = "Option::is_none")]
additional_data_fields: Option<AdditionalData<HexSlice<'a>>>,
#[serde(skip_serializing_if = "Option::is_none")]
user_data: Option<HexSlice<'a>>,
}

impl<'a> VerifyOutput<'a> {
fn from_exchange(ctx: &'a ExchangeFormatCtx, flags: &AttestationFlags) -> Result<Self> {
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<String>,
_cert_file: &'a str,
_signing_file: &'b str,
_arpk_file: &'c str,
) -> Result<Vec<u8>> {
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<u8>, user_data: Vec<u8>) -> Result<ExitCode> {
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<Vec<u8>> {
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<String>, certs: Vec<String>, crls: Vec<String>, arpk_file: String) -> Result<Vec<u8>> {
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:,
}
}
// 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<u8>
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)
}
28 changes: 11 additions & 17 deletions attestation-service/verifier/src/se/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -31,17 +32,13 @@ impl Verifier for SeVerifier {
&self,
_tee_parameters: String,
) -> Result<String> {

// TODO replace FakeSeAttest with real IBM SE crate
let attester = FakeSeAttest::default();

// TODO replace the placeholder
let hkds: Vec<String> = vec![String::new(); 2];
let certk = "cert_file_path";
let signk = "sign_file_path";
let certs: Vec<String> = 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: {:?}")?;

Expand All @@ -54,21 +51,18 @@ async fn verify_evidence(
_expected_report_data: &ReportData<'_>,
_expected_init_data_hash: &InitDataHash<'_>,
) -> Result<TeeEvidenceParsedClaim> {
// 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<u8>;
let userdata: Vec<u8>;
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)
Expand Down

0 comments on commit c9f18e6

Please sign in to comment.