diff --git a/Cargo.lock b/Cargo.lock index c4f3e15aeb81b584d3cda5718079ff621b768ef5..af58821b0ff9a6a179bf099eb6cab7e5f89160cb 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -413,7 +413,7 @@ dependencies = [ "clap 4.5.1", "config", "env_logger 0.10.2", - "jsonwebtoken", + "jsonwebtoken 9.2.0", "jwt-simple", "kbs-types", "lazy_static", @@ -742,6 +742,12 @@ version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + [[package]] name = "base64-url" version = "2.0.2" @@ -842,6 +848,12 @@ version = "2.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" +[[package]] +name = "bitmask" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5da9b3d9f6f585199287a473f4f8dfab6566cf827d15c00c219f53c645687ead" + [[package]] name = "block-buffer" version = "0.10.4" @@ -936,6 +948,28 @@ dependencies = [ "libc", ] +[[package]] +name = "ccatoken" +version = "0.1.0" +source = "git+https://github.com/veraison/rust-ccatoken?branch=main#cd2cac3fac8a277c4aa4e7b1abbab0d6fb447694" +dependencies = [ + "base64 0.21.7", + "bitmask", + "ciborium", + "clap 4.5.1", + "cose-rust", + "ear", + "hex", + "hex-literal", + "jsonwebtoken 9.2.0", + "multimap 0.9.1", + "openssl", + "serde", + "serde_json", + "serde_with 3.8.1", + "thiserror", +] + [[package]] name = "cexpr" version = "0.6.0" @@ -1391,8 +1425,18 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a01d95850c592940db9b8194bc39f4bc0e89dee5c4265e4b1807c34a9aba453c" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.13.4", + "darling_macro 0.13.4", +] + +[[package]] +name = "darling" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54e36fcd13ed84ffdfda6f5be89b31287cbb80c439841fe69e04841435464391" +dependencies = [ + "darling_core 0.20.8", + "darling_macro 0.20.8", ] [[package]] @@ -1409,17 +1453,42 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c2cf1c23a687a1feeb728783b993c4e1ad83d99f351801977dd809b48d0a70f" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.10.0", + "syn 2.0.51", +] + [[package]] name = "darling_macro" version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c972679f83bdf9c42bd905396b6c3588a843a17f0f16dfcfa3e2c5d57441835" dependencies = [ - "darling_core", + "darling_core 0.13.4", "quote", "syn 1.0.109", ] +[[package]] +name = "darling_macro" +version = "0.20.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" +dependencies = [ + "darling_core 0.20.8", + "quote", + "syn 2.0.51", +] + [[package]] name = "dashmap" version = "5.5.3" @@ -1482,6 +1551,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" dependencies = [ "powerfmt", + "serde", ] [[package]] @@ -1550,13 +1620,14 @@ checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" [[package]] name = "ear" version = "0.1.2" -source = "git+https://github.com/veraison/rust-ear?rev=43f7f480d09ea2ebc03137af8fbcd70fe3df3468#43f7f480d09ea2ebc03137af8fbcd70fe3df3468" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a803b607ab6a71337ed90f753f7390aa706a1e1de8c5a1406420477f2ed3578" dependencies = [ "base64 0.21.7", "ciborium", "cose-rust", "hex", - "jsonwebtoken", + "jsonwebtoken 8.3.0", "openssl", "phf", "serde", @@ -2053,6 +2124,15 @@ name = "hex" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +dependencies = [ + "serde", +] + +[[package]] +name = "hex-literal" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fe2267d4ed49bc07b63801559be28c718ea06c4738b7a03c94df7386d2cde46" [[package]] name = "hkdf" @@ -2277,6 +2357,7 @@ checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" dependencies = [ "autocfg", "hashbrown 0.12.3", + "serde", ] [[package]] @@ -2287,6 +2368,7 @@ checksum = "233cf39063f058ea2caae4091bf4a3ef70a653afbc026f5c4a4135d114e3c177" dependencies = [ "equivalent", "hashbrown 0.14.3", + "serde", ] [[package]] @@ -2398,6 +2480,20 @@ dependencies = [ "zeroize", ] +[[package]] +name = "jsonwebtoken" +version = "8.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6971da4d9c3aa03c3d8f3ff0f4155b534aad021292003895a469716b2a230378" +dependencies = [ + "base64 0.21.7", + "pem 1.1.1", + "ring 0.16.20", + "serde", + "serde_json", + "simple_asn1", +] + [[package]] name = "jsonwebtoken" version = "9.2.0" @@ -2406,7 +2502,7 @@ checksum = "5c7ea04a7c5c055c175f189b6dc6ba036fd62306b58c66c9f6389036c503a3f4" dependencies = [ "base64 0.21.7", "js-sys", - "pem", + "pem 3.0.3", "ring 0.17.8", "serde", "serde_json", @@ -2781,6 +2877,15 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "multimap" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1a5d38b9b352dbd913288736af36af41c48d61b1a8cd34bcecd727561b7d511" +dependencies = [ + "serde", +] + [[package]] name = "native-tls" version = "0.2.11" @@ -3207,6 +3312,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099" +[[package]] +name = "pem" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8835c273a76a90455d7344889b0964598e3316e2a79ede8e36f16bdcf2228b8" +dependencies = [ + "base64 0.13.1", +] + [[package]] name = "pem" version = "3.0.3" @@ -3579,7 +3693,7 @@ dependencies = [ "itertools 0.10.5", "lazy_static", "log", - "multimap", + "multimap 0.8.3", "petgraph", "prettyplease", "prost", @@ -4372,7 +4486,25 @@ dependencies = [ "base64 0.13.1", "chrono", "serde", - "serde_with_macros", + "serde_with_macros 1.5.2", +] + +[[package]] +name = "serde_with" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad483d2ab0149d5a5ebcd9972a3852711e0153d863bf5a5d0391d28883c4a20" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.2.3", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros 3.8.1", + "time", ] [[package]] @@ -4381,12 +4513,24 @@ version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e182d6ec6f05393cc0e5ed1bf81ad6db3a8feedf8ee515ecdd369809bcce8082" dependencies = [ - "darling", + "darling 0.13.4", "proc-macro2", "quote", "syn 1.0.109", ] +[[package]] +name = "serde_with_macros" +version = "3.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65569b702f41443e8bc8bbb1c5779bd0450bbe723b56198980e80ec45780bce2" +dependencies = [ + "darling 0.20.8", + "proc-macro2", + "quote", + "syn 2.0.51", +] + [[package]] name = "serial_test" version = "0.9.0" @@ -5405,14 +5549,14 @@ checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191" [[package]] name = "veraison-apiclient" version = "0.0.1" -source = "git+https://github.com/chendave/rust-apiclient?branch=token#64fb77855bf0ce14e3167deec62cb2da3e6421c0" +source = "git+https://github.com/thomas-fossati/rust-apiclient?branch=token#67b9bd75eda5ea9b283d9140adb407fd62877f73" dependencies = [ "base64 0.13.1", "chrono", "jsonwebkey", "reqwest", "serde", - "serde_with", + "serde_with 1.14.0", "thiserror", "url", ] @@ -5430,6 +5574,7 @@ dependencies = [ "base64 0.21.7", "bincode", "byteorder", + "ccatoken", "cfg-if", "codicon", "csv-rs", @@ -5437,7 +5582,7 @@ dependencies = [ "eventlog-rs", "hex", "jsonwebkey", - "jsonwebtoken", + "jsonwebtoken 9.2.0", "kbs-types", "log", "openssl", diff --git a/attestation-service/verifier/Cargo.toml b/attestation-service/verifier/Cargo.toml index ef0cd9df9c0c307364c25cbf0d7e077d4029c59f..ec3d0ddb6b42f5eb7ace47f874e48a078355176b 100644 --- a/attestation-service/verifier/Cargo.toml +++ b/attestation-service/verifier/Cargo.toml @@ -12,7 +12,7 @@ az-snp-vtpm-verifier = [ "az-snp-vtpm", "sev", "snp-verifier" ] 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" ] +cca-verifier = [ "ear", "jsonwebtoken", "veraison-apiclient", "ccatoken" ] [dependencies] anyhow.workspace = true @@ -41,8 +41,9 @@ serde_json.workspace = true sev = { version = "1.2.0", features = ["openssl", "snp"], optional = true } sgx-dcap-quoteverify-rs = { git = "https://github.com/intel/SGXDataCenterAttestationPrimitives", tag = "DCAP_1.16", optional = true } strum.workspace = true -veraison-apiclient = { git = "https://github.com/chendave/rust-apiclient", branch = "token", optional = true } -ear = { git = "https://github.com/veraison/rust-ear", rev = "43f7f480d09ea2ebc03137af8fbcd70fe3df3468", optional = true } +veraison-apiclient = { git = "https://github.com/thomas-fossati/rust-apiclient", branch = "token", optional = true } +ear = { version = "0.1.2", optional = true } +ccatoken = { git = "https://github.com/veraison/rust-ccatoken", branch = "main", optional = true } x509-parser = { version = "0.14.0", optional = true } [build-dependencies] diff --git a/attestation-service/verifier/src/cca/config.rs b/attestation-service/verifier/src/cca/config.rs new file mode 100644 index 0000000000000000000000000000000000000000..ed13341d022f1e97d63c48a5f4cd3b1193b14ed0 --- /dev/null +++ b/attestation-service/verifier/src/cca/config.rs @@ -0,0 +1,55 @@ +// Copyright (c) 2024 Linaro Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// + +use serde::{Deserialize, Serialize}; +use std::fs::File; +use std::path::Path; +use thiserror::Error; + +pub const DEFAULT_CCA_CONFIG: &str = "/etc/coco-as/cca-config.json"; + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct Config { + pub local_verifier: Option<LocalVerifier>, + pub remote_verifier: Option<RemoteVerifier>, +} +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct RemoteVerifier { + #[serde(alias = "origin")] + pub origin: String, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(rename_all = "kebab-case")] +pub struct LocalVerifier { + pub ta_store: String, + pub rv_store: String, +} + +#[derive(Error, Debug)] +pub enum ConfigError { + #[error("IO error: {0}")] + IO(#[from] std::io::Error), + #[error("failed to parse CCA config file: {0}")] + JsonFileParse(#[source] serde_json::Error), +} + +impl Default for Config { + fn default() -> Config { + Config { + remote_verifier: None, + local_verifier: None, + } + } +} + +impl TryFrom<&Path> for Config { + type Error = ConfigError; + fn try_from(config_path: &Path) -> Result<Self, ConfigError> { + let file = File::open(config_path)?; + serde_json::from_reader::<File, Config>(file).map_err(ConfigError::JsonFileParse) + } +} diff --git a/attestation-service/verifier/src/cca/local.rs b/attestation-service/verifier/src/cca/local.rs new file mode 100644 index 0000000000000000000000000000000000000000..3281fbfc2cc257e6787b4cd64ba461fc0d0b16dd --- /dev/null +++ b/attestation-service/verifier/src/cca/local.rs @@ -0,0 +1,108 @@ +// Copyright (c) 2023 Arm Ltd. +// Copyright (c) 2024 Linaro Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// + +use super::*; +use base64::engine::general_purpose; +use ccatoken::store::*; +use ccatoken::token::Evidence; +use config::Config; +use core::result::Result::Ok; +use ear::{Appraisal, Ear, RawValue, TrustTier, VerifierID}; +use log::debug; +use std::collections::BTreeMap; +use std::fs; + +pub fn verify( + config: Config, + token: &Vec<u8>, + _expected_report_data: &Vec<u8>, +) -> Result<Ear, anyhow::Error> { + debug!("using config: {:?}", config); + + let ta_store = config.local_verifier.clone().unwrap().ta_store; + let rv_store = config.local_verifier.clone().unwrap().rv_store; + + let jta = fs::read_to_string(ta_store).context("loading TA store")?; + let jrv = fs::read_to_string(rv_store).context("loading RV store")?; + + let mut tas: MemoTrustAnchorStore = Default::default(); + tas.load_json(&jta) + .context("loading trust anchors from JSON store")?; + + let mut rvs: MemoRefValueStore = Default::default(); + rvs.load_json(&jrv) + .context("loading reference values from JSON store")?; + + let mut e: Evidence = Evidence::decode(&token).context("decoding CCA evidence")?; + + e.verify(&tas).context("verifying CCA evidence")?; + e.appraise(&rvs).context("appraising CCA evidence")?; + + let (platform_tv, realm_tvec) = e.get_trust_vectors(); + + // Check that the Realm token was correctly signed using the RAK and that + // the RAK was correctly attested. + if realm_tvec.instance_identity.tier() != TrustTier::Affirming { + bail!("RAK signature or RAK attestation could not be verified"); + } + + // Synthesize TCB claims the way EAR wants to report them. + let annotated_evidence = + annotated_evidence(&e).context("syntesizing CCA Realm TCB claims-set")?; + + let mut appraisal = Appraisal::new(); + appraisal.annotated_evidence = annotated_evidence; + appraisal.trust_vector = platform_tv; + appraisal.update_status_from_trust_vector(); + + let ear = Ear { + profile: "tag:github.com,2023:veraison/ear".to_string(), + vid: VerifierID { + build: "CoCo CCA local verifier".to_string(), + developer: "https://veraison-project.org".to_string(), + }, + submods: BTreeMap::from([("CCA_SSD_PLATFORM".to_string(), appraisal)]), + iat: 0, // not relevant + nonce: None, // not relevant + raw_evidence: None, // not relevant + }; + + Ok(ear) +} + +fn annotated_evidence(e: &Evidence) -> Result<BTreeMap<String, RawValue>, anyhow::Error> { + let pv = general_purpose::STANDARD.encode(e.realm_claims.perso.clone()); + let rim = general_purpose::STANDARD.encode(e.realm_claims.rim.clone()); + let rem0 = general_purpose::STANDARD.encode(e.realm_claims.rem[0].clone()); + let rem1 = general_purpose::STANDARD.encode(e.realm_claims.rem[1].clone()); + let rem2 = general_purpose::STANDARD.encode(e.realm_claims.rem[2].clone()); + let rem3 = general_purpose::STANDARD.encode(e.realm_claims.rem[3].clone()); + let nonce = general_purpose::STANDARD.encode(e.realm_claims.challenge.clone()); + + // I am making the choice of reporting only the claims that are related to + // (currently) unvalidate pieces of the TCB. The assumption is that CCA + // platform has been already fully validated (including RAK attestation), + // and that the only piece of TCB that remains to be validated is the Realm. + let j = format!( + r#"{{ + "platform": {{}}, + "realm": {{ + "cca-realm-challenge": "{nonce}", + "cca-realm-extensible-measurements": [ "{rem0}", "{rem1}", "{rem2}", "{rem3}" ], + "cca-realm-hash-algo-id": "", + "cca-realm-initial-measurement": "{rim}", + "cca-realm-personalization-value": "{pv}", + "cca-realm-public-key": "", + "cca-realm-public-key-hash-algo-id": "" + }} + }} + "# + ); + + let tcb = serde_json::from_str(&j)?; + + Ok(tcb) +} diff --git a/attestation-service/verifier/src/cca/mod.rs b/attestation-service/verifier/src/cca/mod.rs index 78fc0ce362dbe2506702e80838ed6f104f3ee607..5c279e421b27cb93a957d6c0f309c80413455602 100644 --- a/attestation-service/verifier/src/cca/mod.rs +++ b/attestation-service/verifier/src/cca/mod.rs @@ -1,4 +1,5 @@ // Copyright (c) 2023 Arm Ltd. +// Copyright (c) 2024 Linaro Ltd. // // SPDX-License-Identifier: Apache-2.0 // @@ -9,15 +10,18 @@ use async_trait::async_trait; use base64::Engine; use core::result::Result::Ok; use ear::{Ear, RawValue}; -use jsonwebtoken::{self as jwt}; -use log::{debug, error, info}; +use log::{debug, info}; use serde::{Deserialize, Serialize}; +use std::path::Path; use std::{collections::BTreeMap, str}; use veraison_apiclient::*; -const VERAISON_ADDR: &str = "VERAISON_ADDR"; -const DEFAULT_VERAISON_ADDR: &str = "localhost:8080"; -const MEDIA_TYPE: &str = "application/eat-collection; profile=http://arm.com/CCA-SSD/1.0.0"; +mod config; +use config::{Config, DEFAULT_CCA_CONFIG}; +mod local; +mod remote; + +const CCA_CONFIG_FILE: &str = "CCA_CONFIG_FILE"; #[derive(Debug, Default)] pub struct CCA {} @@ -35,8 +39,8 @@ pub struct SwComponent { #[derive(Debug, Default, Serialize, Deserialize)] #[serde(rename_all = "kebab-case")] pub struct CcaPlatformClaims { - pub cca_platform_challenge: String, - pub cca_platform_sw_components: Vec<SwComponent>, + pub cca_platform_challenge: Option<String>, + pub cca_platform_sw_components: Option<Vec<SwComponent>>, } #[derive(Debug, Default, Serialize, Deserialize)] @@ -50,7 +54,7 @@ pub struct RealmClaims { } #[derive(Debug, Default, Serialize, Deserialize)] -struct Evidence { +struct EvidenceClaimsSet { realm: RealmClaims, platform: CcaPlatformClaims, } @@ -61,17 +65,6 @@ struct CcaEvidence { token: Vec<u8>, } -fn my_evidence_builder( - nonce: &[u8], - accept: &[String], - token: Vec<u8>, -) -> Result<(Vec<u8>, String), veraison_apiclient::Error> { - info!("server challenge: {:?}", nonce); - info!("acceptable media types: {:#?}", accept); - // TODO: Get the CCA media type from the slice of `accept`. - Ok((token, MEDIA_TYPE.to_string())) -} - #[async_trait] impl Verifier for CCA { async fn evaluate( @@ -80,6 +73,14 @@ impl Verifier for CCA { expected_report_data: &ReportData, expected_init_data_hash: &InitDataHash, ) -> Result<TeeEvidenceParsedClaim> { + let config_file = + std::env::var(CCA_CONFIG_FILE).unwrap_or_else(|_| DEFAULT_CCA_CONFIG.to_string()); + + let config = Config::try_from(Path::new(&config_file)) + .map_err(|e| anyhow!("parsing {config_file}: {e}"))?; + + debug!("remote verifier: {:?}", config.remote_verifier); + let ReportData::Value(expected_report_data) = expected_report_data else { bail!("CCA verifier must provide report data field!"); }; @@ -89,54 +90,21 @@ impl Verifier for CCA { let evidence = serde_json::from_slice::<CcaEvidence>(evidence) .context("Deserialize CCA Evidence failed.")?; - let host_url = - std::env::var(VERAISON_ADDR).unwrap_or_else(|_| DEFAULT_VERAISON_ADDR.to_string()); - - let discovery = Discovery::from_base_url(format!("http://{:}", host_url))?; - - let verification_api = discovery.get_verification_api().await?; + let ear: Ear; - let relative_endpoint = verification_api - .get_api_endpoint("newChallengeResponseSession") - .context("Failed to discover the verification endpoint details.")?; - - let api_endpoint = format!("http://{:}{}", host_url, relative_endpoint); - - // create a ChallengeResponse object - let cr = ChallengeResponseBuilder::new() - .with_new_session_url(api_endpoint) - .build()?; - - let token = evidence.token; - let n = Nonce::Value(expected_report_data.clone()); - let result = match cr.run(n, my_evidence_builder, token.clone()).await { - Err(e) => { - error!("Error: {}", e); - bail!("CCA Attestation failed with error: {:?}", e); - } - Ok(attestation_result) => attestation_result, + if config.remote_verifier.is_some() { + ear = remote::verify(config, &evidence.token, &expected_report_data).await?; + } else { + ear = local::verify(config, &evidence.token, &expected_report_data)?; }; - // Get back the pub key to decrypt the ear which holds raw evidence and the session nonce - let public_key_pem = verification_api.ear_verification_key_as_pem()?; - let dk = jwt::DecodingKey::from_ec_pem(public_key_pem.as_bytes()) - .context("get the decoding key from the pem public key")?; - let plain_ear = Ear::from_jwt(result.as_str(), jwt::Algorithm::ES256, &dk) - .context("decrypt the ear with the decoding key")?; + info!("EAR: {:?}", ear); - let ear_nonce = plain_ear.nonce.context("get nonce from ear")?; - let nonce_byte = base64::engine::general_purpose::URL_SAFE - .decode(ear_nonce.to_string()) - .context("decode nonce byte from ear")?; - - if expected_report_data != nonce_byte { - bail!("report data is different from that in ear's session nonce"); - } - - let cca_mod = match plain_ear.submods.get("CCA_SSD_PLATFORM") { + let cca_mod = match ear.submods.get("CCA_SSD_PLATFORM") { Some(value) => value, None => bail!("no entry found for CCA_SSD_PLATFORM"), }; + let evidence = &cca_mod.annotated_evidence; // NOTE: CCA validation by the Verasion has some overlapping with the RVPS, the similar validation has been done by the Verasion already. @@ -144,7 +112,7 @@ impl Verifier for CCA { let tcb = parse_cca_evidence(evidence)?; if let InitDataHash::Value(expected_init_data_hash) = expected_init_data_hash { - debug!("Check the binding of init data."); + debug!("Check the binding of init data"); if *expected_init_data_hash != base64::engine::general_purpose::STANDARD .decode(&tcb.realm.cca_realm_personalization_value) @@ -228,11 +196,11 @@ impl Verifier for CCA { /// } /// } /// NOTE: each of the value are base64 encoded hex value. -fn parse_cca_evidence(evidence_map: &BTreeMap<String, RawValue>) -> Result<Evidence> { - let mut evidence = Evidence::default(); +fn parse_cca_evidence(evidence_map: &BTreeMap<String, RawValue>) -> Result<EvidenceClaimsSet> { + let mut evidence = EvidenceClaimsSet::default(); let platfrom = evidence_map .get("platform") - .context("get platform evidence from the cca evidence map")?; + .context("get platform evidence from the CCA evidence map")?; let output = serde_json::to_string(platfrom)?; let p: CcaPlatformClaims = serde_json::from_str(output.as_str())?; evidence.platform = p; @@ -247,7 +215,7 @@ fn parse_cca_evidence(evidence_map: &BTreeMap<String, RawValue>) -> Result<Evide Ok(evidence) } -fn cca_generate_parsed_claim(tcb: Evidence) -> Result<TeeEvidenceParsedClaim> { +fn cca_generate_parsed_claim(tcb: EvidenceClaimsSet) -> Result<TeeEvidenceParsedClaim> { let v = serde_json::to_value(tcb).context("build json value from the cca evidence")?; Ok(v as TeeEvidenceParsedClaim) } @@ -261,7 +229,7 @@ mod tests { fn test_cca_generate_parsed_claim() { let s = fs::read("./test_data/cca-claims.json").unwrap(); let evidence = String::from_utf8_lossy(&s); - let tcb = serde_json::from_str::<Evidence>(&evidence).unwrap(); + let tcb = serde_json::from_str::<EvidenceClaimsSet>(&evidence).unwrap(); let parsed_claim = cca_generate_parsed_claim(tcb); assert!(parsed_claim.is_ok()); let _ = fs::write( diff --git a/attestation-service/verifier/src/cca/remote.rs b/attestation-service/verifier/src/cca/remote.rs new file mode 100644 index 0000000000000000000000000000000000000000..5732ee8e93a30e3709e035873e56a33d105b11d4 --- /dev/null +++ b/attestation-service/verifier/src/cca/remote.rs @@ -0,0 +1,85 @@ +// Copyright (c) 2023 Arm Ltd. +// Copyright (c) 2024 Linaro Ltd. +// +// SPDX-License-Identifier: Apache-2.0 +// + +use super::*; +use base64::Engine; +use config::Config; +use core::result::Result::Ok; +use ear::Ear; +use log::{debug, error}; +use std::str; + +const MEDIA_TYPE: &str = "application/eat-collection; profile=http://arm.com/CCA-SSD/1.0.0"; + +fn evidence_builder( + nonce: &[u8], + accept: &[String], + token: Vec<u8>, +) -> Result<(Vec<u8>, String), veraison_apiclient::Error> { + debug!("server challenge: {:?}", nonce); + debug!("acceptable media types: {:#?}", accept); + // TODO: Get the CCA media type from the slice of `accept`. + Ok((token, MEDIA_TYPE.to_string())) +} + +pub async fn verify( + config: Config, + token: &Vec<u8>, + expected_report_data: &Vec<u8>, +) -> Result<Ear, anyhow::Error> { + let host_url = config.remote_verifier.unwrap().origin; + + let discovery = Discovery::from_base_url(host_url.clone())?; + + let verification_api = discovery.get_verification_api().await?; + + let relative_endpoint = verification_api + .get_api_endpoint("newChallengeResponseSession") + .context("discovering verification endpoint details")?; + + let api_endpoint = format!("{:}{}", host_url, relative_endpoint); + + let cr = ChallengeResponseBuilder::new() + .with_new_session_url(api_endpoint) + .build()?; + + let n = Nonce::Value(expected_report_data.clone()); + + let result = match cr.run(n, evidence_builder, token.clone()).await { + Err(e) => { + error!("Error: {}", e); + bail!("remote verification failed with error: {:?}", e); + } + Ok(attestation_result) => attestation_result, + }; + + let verifier_pkey = verification_api.ear_verification_key_as_string(); + + let plain_ear = Ear::from_jwt_jwk( + result.as_str(), + ear::Algorithm::ES256, + verifier_pkey.as_bytes(), + ) + .context("decrypting EAR with the decoding key")?; + + if let Some(ref ear_nonce) = plain_ear.nonce { + let nonce_byte = base64::engine::general_purpose::URL_SAFE + .decode(ear_nonce.to_string()) + .context("base64-decoding nonce from EAR")?; + + if *expected_report_data != nonce_byte { + bail!( + "nonce verification failed: want {:02x?}, got {:02x?}", + *expected_report_data, + nonce_byte + ); + } + } else { + bail!("no nonce found in EAR") + } + + Ok(plain_ear) +} diff --git a/cca/NOTES.md b/cca/NOTES.md index eee5b6ba57aee58de13b23d2ff3cce79f06b93c3..bf1ddc8f36434847af8105f2b80fff92cc58fd39 100644 --- a/cca/NOTES.md +++ b/cca/NOTES.md @@ -103,43 +103,32 @@ RUST_LOG=debug ${TRUSTEE_SRC}/target/release/rvps -c ${TRUSTEE_SRC}/kbs/config/r ## AS -```sh -env VERAISON_ADDR=veraison.example:8080 RUST_LOG=debug ${TRUSTEE_SRC}/target/debug/grpc-as -c ${TRUSTEE_SRC}/kbs/config/as-config.json -s 127.0.0.1:50004 -``` - -## Test using `grpcurl(1)` - -Install `grpcurl`: -```sh -go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest -``` +local verifier: ```sh -${TRUSTEE_SRC}/cca/scripts/request.sh +CCA_CONFIG_FILE=${TRUSTEE_SRC}/cca/misc/cca-config-local.json \ +VERAISON_ADDR=213.146.141.101:8080 \ +RUST_LOG=debug \ + ${TRUSTEE_SRC}/target/debug/grpc-as \ + -c ${TRUSTEE_SRC}/cca/misc/as-config.json \ + -s 127.0.0.1:50004 ``` -# Shell Utils - -## Base64Url functions - -`base64(1)` does not handle the URL-safe alphabet. - -`base64url.sh` provides two functions (`base64url::encode` and `base64url::decode`) that can be used instead: +remote verifier: ```sh -source ${TRUSTEE_SRC}/cca/scripts/base64url.sh` +CCA_CONFIG_FILE=${TRUSTEE_SRC}/cca/misc/cca-config-remote.json \ +VERAISON_ADDR=213.146.141.101:8080 \ +RUST_LOG=debug \ + ${TRUSTEE_SRC}/target/debug/grpc-as \ + -c ${TRUSTEE_SRC}/cca/misc/as-config.json \ + -s 127.0.0.1:50004 ``` -## Tokenise Script - -The `${TRUSTEE_SRC}/cca/scripts/tokenise.sh` script takes a raw CCA attestation token in CBOR format and wraps it the (pretty peculiar) way the gRPC interface expects it to be consumed. - -The CBOR file is passed as argument, e.g.: +## KBC simulator ```sh -${TRUSTEE_SRC}cca/scripts/tokenise.sh \ - ${TRUSTEE_SRC}/cca/misc/cca-example-token.cbor \ - > cca-example-token-grpc-request.json +${TRUSTEE_SRC}/cca/scripts/kbs-client.sh ``` # Veraison Services diff --git a/cca/misc/as-config.json b/cca/misc/as-config.json new file mode 100644 index 0000000000000000000000000000000000000000..8e42f2c8d913230ab92a2b13511b2a15e6dfcd2c --- /dev/null +++ b/cca/misc/as-config.json @@ -0,0 +1,11 @@ +{ + "work_dir": "/opt/confidential-containers/attestation-service", + "policy_engine": "opa", + "rvps_config": { + "remote_addr":"http://localhost:50003" + }, + "attestation_token_broker": "Simple", + "attestation_token_config": { + "duration_min": 5 + } +} diff --git a/cca/misc/cca-config-local.json b/cca/misc/cca-config-local.json new file mode 100644 index 0000000000000000000000000000000000000000..221cbaae805ada92111e4b72844897c568204592 --- /dev/null +++ b/cca/misc/cca-config-local.json @@ -0,0 +1,6 @@ +{ + "local-verifier": { + "ta-store": "/Users/tho/Code/git.codelinaro.org/linaro/dcap/cca-demos/coco-trustee/cca/misc/tastore.json", + "rv-store": "/Users/tho/Code/git.codelinaro.org/linaro/dcap/cca-demos/coco-trustee/cca/misc/rvstore.json" + } +} diff --git a/cca/misc/cca-config-remote.json b/cca/misc/cca-config-remote.json new file mode 100644 index 0000000000000000000000000000000000000000..da4c01df934d7c91b5d1ca329947583228146eaf --- /dev/null +++ b/cca/misc/cca-config-remote.json @@ -0,0 +1,5 @@ +{ + "remote-verifier": { + "origin": "http://213.146.141.101:8080" + } +} diff --git a/cca/misc/cca-config.json b/cca/misc/cca-config.json new file mode 100644 index 0000000000000000000000000000000000000000..da4c01df934d7c91b5d1ca329947583228146eaf --- /dev/null +++ b/cca/misc/cca-config.json @@ -0,0 +1,5 @@ +{ + "remote-verifier": { + "origin": "http://213.146.141.101:8080" + } +} diff --git a/cca/misc/rvstore.json b/cca/misc/rvstore.json new file mode 100644 index 0000000000000000000000000000000000000000..a2168486377ed31ccaf79345c4c61b62d7d675a7 --- /dev/null +++ b/cca/misc/rvstore.json @@ -0,0 +1,101 @@ +{ + "platform": [ + { + "implementation-id": "7f454c4602010100000000000000000003003e00010000005058000000000000", + "sw-components": [ + { + "measurement-value": "9a271f2a916b0b6ee6cecb2426f0b3206ef074578be55d9bc94f6f3fe3ab86aa", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "RSE_BL1_2" + }, + { + "measurement-value": "53c234e5e8472b6ac51c1ae1cab3fe06fad053beb8ebfd8977b010655bfdd3c3", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "RSE_BL2" + }, + { + "measurement-value": "1121cfccd5913f0a63fec40a6ffd44ea64f9dc135c66634ba001d10bcf4302a2", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "RSE_S" + }, + { + "measurement-value": "1571b5ec78bd68512bf7830bb6a2a44b2047c7df57bce79eb8a1c0e5bea0a501", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "AP_BL1" + }, + { + "measurement-value": "10159baf262b43a92d95db59dae1f72c645127301661e0a3ce4e38b295a97c58", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "AP_BL2" + }, + { + "measurement-value": "10122e856b3fcd49f063636317476149cb730a1aa1cfaad818552b72f56d6f68", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "SCP_BL1" + }, + { + "measurement-value": "aa67a169b0bba217aa0aa88a65346920c84c42447c36ba5f7ea65f422c1fe5d8", + "signer-id": "f14b4987904bcb5814e4459a057ed4d20f58a633152288a761214dcd28780b56", + "version": null, + "component-type": "SCP_BL2" + }, + { + "measurement-value": "2e6d31a5983a91251bfae5aefa1c0a19d8ba3cf601d0e8a706b4cfa9661a6b8a", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "AP_BL31" + }, + { + "measurement-value": "a1fb50e6c86fae1679ef3351296fd6713411a08cf8dd1790a4fd05fae8688164", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "RMM" + }, + { + "measurement-value": "1a252402972f6057fa53cc172b52b9ffca698e18311facd0f3b06ecaaef79e17", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "HW_CONFIG" + }, + { + "measurement-value": "9a92adbc0cee38ef658c71ce1b1bf8c65668f166bfb213644c895ccb1ad07a25", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "FW_CONFIG" + }, + { + "measurement-value": "238903180cc104ec2c5d8b3f20c5bc61b389ec0a967df8cc208cdc7cd454174f", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "TB_FW_CONFIG" + }, + { + "measurement-value": "e6c21e8d260fe71882debdb339d2402a2ca7648529bc2303f48649bce0380017", + "signer-id": "5378796307535df3ec8d8b15a2e2dc5641419c3d3060cfe32238c0fa973f7aa3", + "version": null, + "component-type": "SOC_FW_CONFIG" + } + ], + "platform-configuration": "cfcfcfcf" + } + ], + "realm": [ + { + "initial-measurement": "ae87fbf7f105e71a7827cb6f0896889c73d2beaf4830d4f9cb05e3e96c3fc7b4faa04caf36a26bd094cf9c7b738617e4c189aaee184dedda5d7a03c39dbdac7c", + "rak-hash-algorithm": "sha-256", + "extensible-measurements": [ + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000", + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + ], + "personalization-value": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + } + ] +} \ No newline at end of file diff --git a/cca/misc/tastore.json b/cca/misc/tastore.json new file mode 100644 index 0000000000000000000000000000000000000000..e35b3a618225733bd290334ac1ca78c2d24fb989 --- /dev/null +++ b/cca/misc/tastore.json @@ -0,0 +1,12 @@ +[ + { + "pkey": { + "crv": "P-384", + "kty": "EC", + "x": "IShnxS4rlQiwpCCpBWDzlNLfqiG911FP8akBr-fh94uxHU5m-Kijivp2r2oxxN6M", + "y": "hM4tr8mWQli1P61xh3T0ViDREbF26DGOEYfbAjWjGNN7pZf-6A4OTHYqEryz6m7U" +}, + "implementation-id": "7f454c4602010100000000000000000003003e00010000005058000000000000", + "instance-id": "0107060504030201000f0e0d0c0b0a090817161514131211101f1e1d1c1b1a1918" + } +] \ No newline at end of file diff --git a/cca/scripts/base64url.sh b/cca/scripts/base64url.sh deleted file mode 100644 index 395745fb3661a6c29b30d76e104e1348eb018e43..0000000000000000000000000000000000000000 --- a/cca/scripts/base64url.sh +++ /dev/null @@ -1,11 +0,0 @@ -base64url::encode() { - base64 -w0 \ - | tr '+/' '-_' \ - | tr -d '='; -} - -base64url::decode() { - awk '{ if (length($0) % 4 == 3) print $0"="; else if (length($0) % 4 == 2) print $0"=="; else print $0; }' \ - | tr -- '-_' '+/' \ - | base64 -d; -} diff --git a/cca/scripts/endorse-rim.sh b/cca/scripts/endorse-rim.sh new file mode 100755 index 0000000000000000000000000000000000000000..d4e01effa4e035027c4f8b0f61b84e28162c9e6a --- /dev/null +++ b/cca/scripts/endorse-rim.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +set -eu +set -o pipefail + +RIM=${RIM:?must be set to the expected RIM value (b64-encoded)} + +_d="$(dirname $(readlink -f $0))" + +source "${_d}/func.sh" + +tmp_dir="$(mktemp -d)" +trap 'rm -rf -- "$tmp_dir"' EXIT + +rv="${tmp_dir}/rim-rv.json" + +cat << EOF > ${rv} +{ + "cca.realm.cca-realm-initial-measurement": [ + "${RIM}" + ] +} +EOF + +blob="${tmp_dir}/blob.json" + +payload=$(cat ${rv} | ${_base64}) +cat << EOF > ${blob} +{ + "version" : "0.1.0", + "type": "sample", + "payload": "$payload" +} +EOF + +echo ">>> submitting ${blob} to RVPS" + +${_d}/../../target/release/rvps-tool register -p ${blob} + +echo ">>> done!" diff --git a/cca/scripts/func.sh b/cca/scripts/func.sh new file mode 100644 index 0000000000000000000000000000000000000000..22504081b5fbe0b3999e2683a252e82aaee9c34d --- /dev/null +++ b/cca/scripts/func.sh @@ -0,0 +1,44 @@ +if [ $(uname) == "Darwin" ] +then + _base64="base64" + _sed="gsed" + _tokenise="mac_tokenise" +else + _base64="base64 -w0" + _sed="sed" + _tokenise="linux_tokenise" +fi + +function mac_tokenise() { + local token="$1" + + od -v -t u1 "${token}" \ + | cut -c11- \ + | tr -s '[:blank:]' '\n' \ + | ${_sed} -e 's/$/,/' \ + | ${_sed} -e '$s/,//' \ + | tr -d '\n' +} + +function linux_tokenise() { + local token="$1" + + od -v -w1 -t u1 ${token} \ + | cut -c9- \ + | ${_sed} -e 's/$/,/' \ + | ${_sed} '$d' \ + | ${_sed} -e '$s/,//' \ + | tr -d '\n' +} + +base64url_encode() { + ${_base64} \ + | tr '+/' '-_' \ + | tr -d '='; +} + +base64url_decode() { + awk '{ if (length($0) % 4 == 3) print $0"="; else if (length($0) % 4 == 2) print $0"=="; else print $0; }' \ + | tr -- '-_' '+/' \ + | base64 -d; +} diff --git a/cca/scripts/request.sh b/cca/scripts/grpc-request.sh similarity index 100% rename from cca/scripts/request.sh rename to cca/scripts/grpc-request.sh diff --git a/cca/scripts/kbs-client.sh b/cca/scripts/kbs-client.sh index 443e2be570534ddf7037b215d6197345d1d0811c..18831fa5367da4d4662d561864c0d500ca1d74dc 100755 --- a/cca/scripts/kbs-client.sh +++ b/cca/scripts/kbs-client.sh @@ -1,16 +1,19 @@ #!/bin/bash # -# TODO(tho) -# * make per-run temp directory to store all the transient files -# * make kbs-request on the fly (remove file in ../misc) +# TODO # * check that evcli is in PATH as a precondition set -euo pipefail _d="$(dirname $(readlink -f $0))" +source "${_d}/func.sh" + +tmp_dir="$(mktemp -d)" +trap 'rm -rf -- "$tmp_dir"' EXIT + kbs="kbs:8080" -cookie_jar=cookie.txt +cookie_jar="${tmp_dir}/cookie.txt" cp /dev/null ${cookie_jar} @@ -21,7 +24,7 @@ nonce=$(curl -X POST http://${kbs}/kbs/v0/auth \ -sS \ -c ${cookie_jar} \ -H 'Content-Type: application/json' \ - -d @${_d}/../misc/kbs-request.json | jq .nonce | sed -e 's/"//g') + -d @${_d}/../misc/kbs-request.json | jq .nonce | ${_sed} -e 's/"//g') echo ">>> got nonce: $nonce" echo ">>> cookies:" @@ -33,7 +36,7 @@ p_cpak=${_d}/../misc/cpak-pub.json s_cpak=${_d}/../misc/cpak-priv.json s_rak=${_d}/../misc/rak-priv.json golden=${_d}/../misc/cca-example-token.cbor -token=/tmp/t.cbor +token="${tmp_dir}/t.cbor" nonce_hex=$(cat << EOF | tr -d '\n' | tr -d '[:space:] ' | shasum -a 384 | cut -d' ' -f1 { @@ -48,25 +51,17 @@ nonce_hex=$(cat << EOF | tr -d '\n' | tr -d '[:space:] ' | shasum -a 384 | cut - EOF )"00000000000000000000000000000000" -nonce_b64=$(echo $nonce_hex | xxd -p -r | base64 -w0) +nonce_b64=$(echo $nonce_hex | xxd -p -r | ${_base64}) echo ">>> computed nonce: $nonce_b64" evcli cca create -r ${s_rak} -p ${s_cpak} -t ${token} -c <(evcli cca check -k ${p_cpak} -t ${golden} \ - | tail -1 \ - | jq --arg n "$nonce_b64" '."cca-realm-delegated-token"."cca-realm-challenge" = $n') - -vectoken=$(cat << EOF -$(od -v -w1 -t u1 ${token} \ - | cut -c9- \ - | sed -e 's/$/,/' \ - | sed '$d' \ - | sed -e '$s/,//' \ - | tr -d '\n' ) -EOF -) + | tail -1 \ + | jq --arg n "$nonce_b64" '."cca-realm-delegated-token"."cca-realm-challenge" = $n') + +vectoken="$(${_tokenise} ${token})" -attreq=a.json +attreq="${tmp_dir}/a.json" cat << EOF > $attreq { diff --git a/cca/scripts/make-token.sh b/cca/scripts/make-token.sh deleted file mode 100755 index 8a04763b11bf646ad2a7e1a8376318e5fdeabf04..0000000000000000000000000000000000000000 --- a/cca/scripts/make-token.sh +++ /dev/null @@ -1,24 +0,0 @@ -#!/bin/bash - -set -exu -set -o pipefail - -_P="$(dirname $(readlink -f $0))" - -CPAK_PUB=${_P}/../misc/cpak-pub.json -CPAK_PRIV=${_P}/../misc/cpak-priv.json -RAK_PRIV=${_P}/../misc/rak-priv.json -GOLDEN=${_P}/../misc/cca-example-token.cbor -TOKEN=t.cbor - -NONCE=$1 - -PAD="0000000000000000000000000000000000000000000000000000000000000000" - -NONCE_HEX="$(echo -n $NONCE | base64 -d | xxd -p -c 256)$PAD" - -NONCE=$(echo $NONCE_HEX | xxd -p -r | base64 -w0) - -evcli cca create -r ${RAK_PRIV} -p ${CPAK_PRIV} -t ${TOKEN} -c <(evcli cca check -k ${CPAK_PUB} -t ${GOLDEN} \ - | tail -1 \ - | jq --arg n "$NONCE" '."cca-realm-delegated-token"."cca-realm-challenge" = $n') diff --git a/cca/scripts/tokenise.sh b/cca/scripts/tokenise.sh deleted file mode 100755 index f600b837a20b1c30d1d9b6ce0c5810c3e66d0922..0000000000000000000000000000000000000000 --- a/cca/scripts/tokenise.sh +++ /dev/null @@ -1,32 +0,0 @@ -#!/bin/bash - -set -Eeux -set -o pipefail - -TRUSTEE_SRC="$(dirname $0)/../.." -. ${TRUSTEE_SRC}/cca/scripts/base64url.sh - -CBOR=$1 - -[ -f "${CBOR}" ] || exit 1 - -b64=$(cat << EOF -{ "token": [ -$(od -v -w1 -t u1 ${CBOR} \ - | cut -c9- \ - | sed -e 's/$/,/' \ - | sed '$d' \ - | sed -e '$s/,//') -] } -EOF -) - -cat << EOF -{ - "tee": "7", - "evidence": "$(echo $b64 | base64url::encode)", - "tee-evidence": "$b64", - "raw_runtime_data": "bobW2XzHE7xt1D285JGmtAMRwCeov4WjnaY-nORMEyqKEZ0pb65qaZnpvz5EcbDOASRdiJQkwx6JeTs7HWsVBA", - "policy_ids": [] -} -EOF