From bec41a73f21c53d10d29b7ad204bee150ae68ccf Mon Sep 17 00:00:00 2001 From: Arnaud de Grandmaison Date: Mon, 16 Sep 2024 17:52:15 +0200 Subject: [PATCH 1/6] docs(keybroker-server): add documentation string for the verifier command line argument Signed-off-by: Arnaud de Grandmaison --- rust-keybroker/keybroker-server/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/rust-keybroker/keybroker-server/src/main.rs b/rust-keybroker/keybroker-server/src/main.rs index e24aba1..b8331a1 100644 --- a/rust-keybroker/keybroker-server/src/main.rs +++ b/rust-keybroker/keybroker-server/src/main.rs @@ -146,6 +146,7 @@ struct Args { #[arg(short, long, default_value = "http://127.0.0.1")] baseurl: String, + /// The URL where the verifier can be reached #[arg(short, long, default_value = "http://veraison.test.linaro.org:8080")] verifier: String, } From 3a4ba60e181f5e15afcace94425f97f338d48cc4 Mon Sep 17 00:00:00 2001 From: Arnaud de Grandmaison Date: Mon, 16 Sep 2024 17:55:49 +0200 Subject: [PATCH 2/6] feat(keybroker-server): differentiate the NIC the server will listen to from its FQDN. Signed-off-by: Arnaud de Grandmaison --- rust-keybroker/keybroker-server/src/main.rs | 25 ++++++++++++++++----- 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/rust-keybroker/keybroker-server/src/main.rs b/rust-keybroker/keybroker-server/src/main.rs index b8331a1..461c3ef 100644 --- a/rust-keybroker/keybroker-server/src/main.rs +++ b/rust-keybroker/keybroker-server/src/main.rs @@ -36,8 +36,8 @@ async fn request_key( }; let location = format!( - "{}:{}/keys/v1/evidence/{}", - data.args.baseurl, data.args.port, challenge.challenge_id + "{}/keys/v1/evidence/{}", + data.base_url, challenge.challenge_id ); // TODO: Remove this println - tracer message for dev purposes only. @@ -139,12 +139,20 @@ async fn submit_evidence( #[derive(Clone, Parser, Debug)] #[command(version, about, long_about = None)] struct Args { - /// The port on which the web server will listen + /// The interface on which this server will listen (use 0.0.0.0 to listen on all interfaces) + #[arg(short, long, default_value = "127.0.0.1")] + addr: String, + + /// The port on which this server will listen #[arg(short, long, default_value_t = 8088)] port: u16, - #[arg(short, long, default_value = "http://127.0.0.1")] - baseurl: String, + /// The base URL at which this server can be reached back for evidence submission. + /// If not specified on the command line, it will be set to 'http://{addr}', but + /// this value can be overridden with an FQDN for {addr} in order to use name resolution. + /// The port number will be appended, so don't leave a trailing '/' to the URL. + #[arg(short, long, default_value = None)] + base_url: Option, /// The URL where the verifier can be reached #[arg(short, long, default_value = "http://veraison.test.linaro.org:8080")] @@ -153,6 +161,7 @@ struct Args { struct ServerState { args: Args, + base_url: String, keystore: Mutex, challenger: Mutex, } @@ -172,6 +181,10 @@ async fn main() -> std::io::Result<()> { let server_state = ServerState { args: args.clone(), + base_url : match args.base_url { + Some(url) => format!("{}:{}", url, args.port), + None => format!("http://{}:{}", args.addr, args.port), + }, keystore: Mutex::new(keystore), challenger: Mutex::new(challenger), }; @@ -184,7 +197,7 @@ async fn main() -> std::io::Result<()> { .service(submit_evidence); App::new().app_data(app_data.clone()).service(scope) }) - .bind(("127.0.0.1", args.port))? + .bind((args.addr, args.port))? .run() .await } From a6d098e85e02a8b4bb28905564d83538c6d4f558 Mon Sep 17 00:00:00 2001 From: Arnaud de Grandmaison Date: Mon, 16 Sep 2024 17:59:48 +0200 Subject: [PATCH 3/6] fix(keybroker-server): typo fixes Signed-off-by: Arnaud de Grandmaison --- rust-keybroker/keybroker-server/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust-keybroker/keybroker-server/src/main.rs b/rust-keybroker/keybroker-server/src/main.rs index 461c3ef..fbb99f7 100644 --- a/rust-keybroker/keybroker-server/src/main.rs +++ b/rust-keybroker/keybroker-server/src/main.rs @@ -118,7 +118,7 @@ async fn submit_evidence( } else { let error_info = ErrorInformation { r#type: "AttestationFailure".to_string(), - detail: "The attestation result is not in policy..".to_string(), + detail: "The attestation result is not in policy.".to_string(), }; HttpResponse::Forbidden().json(error_info) @@ -127,7 +127,7 @@ async fn submit_evidence( Err(_) => { let error_info = ErrorInformation { r#type: "AttestationFailure".to_string(), - detail: "No attestation result was obtained..".to_string(), + detail: "No attestation result was obtained.".to_string(), }; HttpResponse::Forbidden().json(error_info) From 2ad54f458a5a6f78caf210d1b67777bb34377f60 Mon Sep 17 00:00:00 2001 From: Arnaud de Grandmaison Date: Mon, 16 Sep 2024 18:04:39 +0200 Subject: [PATCH 4/6] feat(keybroker-server): add a verbose flag The verbose flag is generally useful for debug purpose obviously, but also for new comers to the code base who want to understand the flow of operations. Signed-off-by: Arnaud de Grandmaison --- rust-keybroker/keybroker-server/src/main.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rust-keybroker/keybroker-server/src/main.rs b/rust-keybroker/keybroker-server/src/main.rs index fbb99f7..869c033 100644 --- a/rust-keybroker/keybroker-server/src/main.rs +++ b/rust-keybroker/keybroker-server/src/main.rs @@ -40,8 +40,9 @@ async fn request_key( data.base_url, challenge.challenge_id ); - // TODO: Remove this println - tracer message for dev purposes only. - println!("Created attestation challenge at {}", location); + if data.args.verbose { + println!("Created attestation challenge at {}", location); + } HttpResponse::Created() .append_header((http::header::LOCATION, location)) @@ -155,8 +156,12 @@ struct Args { base_url: Option, /// The URL where the verifier can be reached - #[arg(short, long, default_value = "http://veraison.test.linaro.org:8080")] + #[arg(long, default_value = "http://veraison.test.linaro.org:8080")] verifier: String, + + /// Set the server verbosity + #[arg(short, long, default_value_t = false)] + verbose: bool, } struct ServerState { From befccb38498afa585fab232db44ee99f95339e97 Mon Sep 17 00:00:00 2001 From: Arnaud de Grandmaison Date: Mon, 16 Sep 2024 18:13:04 +0200 Subject: [PATCH 5/6] feat(keybroker-server): the server learned to provide a random challenge as well as to optionnally provide a mock challenge, reusing the CCA example token nonce. This can be useful during system bring-up for example. Signed-off-by: Arnaud de Grandmaison --- .../keybroker-server/src/challenge.rs | 31 ++++++++++++++++--- rust-keybroker/keybroker-server/src/main.rs | 12 +++++-- 2 files changed, 35 insertions(+), 8 deletions(-) diff --git a/rust-keybroker/keybroker-server/src/challenge.rs b/rust-keybroker/keybroker-server/src/challenge.rs index a71745c..a41486f 100644 --- a/rust-keybroker/keybroker-server/src/challenge.rs +++ b/rust-keybroker/keybroker-server/src/challenge.rs @@ -18,6 +18,7 @@ //! use crate::error::Result; use keybroker_common::PublicWrappingKey; +use rand::{rngs::StdRng, Rng, SeedableRng}; use std::collections::HashMap; /// Represents a single challenge, and provides the challenge value ("nonce") while also remembering the information @@ -43,6 +44,8 @@ pub struct Challenge { /// This structure provides a hash map of challenges, keyed on the integer challenge identifier. pub struct Challenger { challenge_table: HashMap, + rng: StdRng, + pub verbose: bool, } // This is the challenge value from from https://git.trustedfirmware.org/TF-M/tf-m-tools/+/refs/heads/main/iat-verifier/tests/data/cca_example_token.cbor @@ -59,6 +62,8 @@ impl Challenger { pub fn new() -> Challenger { Challenger { challenge_table: HashMap::new(), + rng: StdRng::from_entropy(), + verbose: false, } } @@ -70,27 +75,43 @@ impl Challenger { &mut self, key_id: &str, wrapping_key: &PublicWrappingKey, + mock_challenge: bool, ) -> Challenge { // All challenges are given random u32 identities - let mut challenge_id: u32 = rand::random(); + let mut challenge_id: u32 = self.rng.gen(); // Simple lightweight collision avoidance - probably not needed given u32 distribution space, // but it's easy to do. Also allows us to throw out zero if we get it. while challenge_id == 0 || self.challenge_table.contains_key(&challenge_id) { - challenge_id = rand::random(); + challenge_id = self.rng.gen(); } let challenge = Challenge { challenge_id, key_id: key_id.to_owned(), wrapping_key: wrapping_key.clone(), - // TODO: We should create a random nonce here. The use of this mock value allows the - // server to be tested with a hard-coded example attestation token during development. - challenge_value: CCA_EXAMPLE_TOKEN_NONCE.to_vec(), + challenge_value: if mock_challenge { + CCA_EXAMPLE_TOKEN_NONCE.to_vec() + } else { + let mut v: Vec = vec![0; 64]; + self.rng.fill(&mut v[..]); + v + }, }; self.challenge_table.insert(challenge_id, challenge.clone()); + if self.verbose { + println!("Created challenge:"); + println!(" - challenge_id: {}", challenge_id); + println!(" - key_id: {}", challenge.key_id); + println!( + " - challenge value ({} bytes): {:02x?}", + challenge.challenge_value.len(), + challenge.challenge_value + ); + } + challenge } diff --git a/rust-keybroker/keybroker-server/src/main.rs b/rust-keybroker/keybroker-server/src/main.rs index 869c033..2bee8ba 100644 --- a/rust-keybroker/keybroker-server/src/main.rs +++ b/rust-keybroker/keybroker-server/src/main.rs @@ -25,7 +25,8 @@ async fn request_key( // Get a new challenge from the challenger. let mut challenger = data.challenger.lock().expect("Poisoned challenger lock."); - let challenge = challenger.create_challenge(&key_id, &key_request.pubkey); + let challenge = + challenger.create_challenge(&key_id, &key_request.pubkey, data.args.mock_challenge); // TODO: The "accept" list is being hardcoded for Arm CCA here - it should come from the verifier. let attestation_challenge = AttestationChallenge { @@ -159,6 +160,10 @@ struct Args { #[arg(long, default_value = "http://veraison.test.linaro.org:8080")] verifier: String, + /// Use the static CCA example token nonce instead of a randomly generated one + #[arg(short, long, default_value_t = false)] + mock_challenge: bool, + /// Set the server verbosity #[arg(short, long, default_value_t = false)] verbose: bool, @@ -176,7 +181,8 @@ async fn main() -> std::io::Result<()> { let args = Args::parse(); let mut keystore = KeyStore::new(); - let challenger = Challenger::new(); + let mut challenger = Challenger::new(); + challenger.verbose = args.verbose; // TODO: Just storing one hard-coded item in the store. Would be better to read from an input file. keystore.store_key( @@ -186,7 +192,7 @@ async fn main() -> std::io::Result<()> { let server_state = ServerState { args: args.clone(), - base_url : match args.base_url { + base_url: match args.base_url { Some(url) => format!("{}:{}", url, args.port), None => format!("http://{}:{}", args.addr, args.port), }, From 0de4028a7345e48e57bc8d3c4b4db4fa86ae7bc4 Mon Sep 17 00:00:00 2001 From: Arnaud de Grandmaison Date: Mon, 16 Sep 2024 18:20:42 +0200 Subject: [PATCH 6/6] feat(keybroker-client): add a client library and a demo application The core of the functionality is provided as a library, with 2 main routines: - get_wrapped_key: is the core routine to get a key, with the crypto related to the ephemeral wrapping key left to the caller. - get_key: is a convenience routine which calls get_wrapped_key and handle the crypto when there is no specific requirement. The demo application illustrates how to use the client library to connect to the keybroker server and get an attestation. This enables running a demo of the 2 API calls needed to request the key, supply the evidence and RSA-decrypt the result. Co-authored-by: Paul Howard Signed-off-by: Arnaud de Grandmaison --- rust-keybroker/keybroker-app/Cargo.toml | 1 + rust-keybroker/keybroker-app/src/main.rs | 58 ++- rust-keybroker/keybroker-client/Cargo.toml | 6 + rust-keybroker/keybroker-client/src/error.rs | 62 +++ rust-keybroker/keybroker-client/src/lib.rs | 458 ++++++++++++++++++- rust-keybroker/keybroker-server/src/main.rs | 16 +- 6 files changed, 583 insertions(+), 18 deletions(-) create mode 100644 rust-keybroker/keybroker-client/src/error.rs diff --git a/rust-keybroker/keybroker-app/Cargo.toml b/rust-keybroker/keybroker-app/Cargo.toml index c78734f..a215d06 100644 --- a/rust-keybroker/keybroker-app/Cargo.toml +++ b/rust-keybroker/keybroker-app/Cargo.toml @@ -12,3 +12,4 @@ categories = ["cryptography", "hardware-support"] [dependencies] keybroker-client = { path = "../keybroker-client" } +clap = { version = "=4.3.24", features = ["derive", "std"] } \ No newline at end of file diff --git a/rust-keybroker/keybroker-app/src/main.rs b/rust-keybroker/keybroker-app/src/main.rs index dbb2a8a..6ea98fb 100644 --- a/rust-keybroker/keybroker-app/src/main.rs +++ b/rust-keybroker/keybroker-app/src/main.rs @@ -1,6 +1,62 @@ // Copyright 2024 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 +use clap::Parser; +use keybroker_client::error::Error as KeybrokerError; +use keybroker_client::{CcaExampleToken, KeyBrokerClient, TsmAttestationReport}; +use std::process; + +/// Structure for parsing and storing the command-line arguments +#[derive(Clone, Parser, Debug)] +#[command(version, about, long_about = None)] +struct Args { + /// The address this client should connect to to request a key. + #[arg(short, long, default_value = "http://127.0.0.1:8088")] + endpoint: String, + + /// Use a CCA example token (instead of the TSM report) + #[arg(short, long, default_value_t = false)] + mock_evidence: bool, + + /// Set the application verbosity + #[arg(short, long, default_value_t = false)] + verbose: bool, + + /// The key name to use + key_name: String, +} + fn main() { - println!("Hello, world!"); + let args = Args::parse(); + + let client = KeyBrokerClient::new(&args.endpoint, args.verbose); + + let attestation_result = if args.mock_evidence { + client.get_key(&args.key_name, &CcaExampleToken {}) + } else { + client.get_key(&args.key_name, &TsmAttestationReport {}) + }; + + // If the attestation was successful, print the key we got from the keybroker and exit with code 0. + // If the attestation failed for genuine attestation related error, print the reason and exit with code 1. + // For any other kind of error (crypto, network connectivity, ...), print an hopefully useful message to diagnose the issue and exit with code 2. + let code = match attestation_result { + Ok(key) => { + let plainstring_key = String::from_utf8(key).unwrap(); + println!("Attestation success :-) ! The key returned from the keybroker is '{plainstring_key}'"); + 0 + } + + Err(error) => { + if let KeybrokerError::AttestationFailure(reason, details) = error { + println!("Attestation failure :-( ! {reason}: {details}"); + 1 + } else { + eprintln!("The key request failed with: {error:?}"); + 2 + } + } + }; + + process::exit(code) } diff --git a/rust-keybroker/keybroker-client/Cargo.toml b/rust-keybroker/keybroker-client/Cargo.toml index b4214c7..e2a6bcf 100644 --- a/rust-keybroker/keybroker-client/Cargo.toml +++ b/rust-keybroker/keybroker-client/Cargo.toml @@ -12,3 +12,9 @@ categories = ["cryptography", "hardware-support"] [dependencies] keybroker-common = { path = "../keybroker-common" } +base64 = "0.22.1" +rand = "0.8.5" +reqwest = { version = "0.12.5", features = ["json", "rustls-tls", "blocking"] } +rsa = "0.9.6" +thiserror = "1.0" +tsm_report = { git = "https://github.com/veracruz-project/cca-utils-rs.git", rev = "cb88b76da722f2991365b159e3d575249dfbbe7d"} \ No newline at end of file diff --git a/rust-keybroker/keybroker-client/src/error.rs b/rust-keybroker/keybroker-client/src/error.rs new file mode 100644 index 0000000..5dbc171 --- /dev/null +++ b/rust-keybroker/keybroker-client/src/error.rs @@ -0,0 +1,62 @@ +// Copyright 2024 Contributors to the Veraison project. +// SPDX-License-Identifier: Apache-2.0 + +use thiserror::Error; + +/// Top-level error type for a keybroker client. +#[derive(Error, Debug)] +pub enum Error { + /// Represents genuine attestation failures. + #[error("Attestation failure: {0} ({1})")] + AttestationFailure(String, String), + /// Represents all kind of runtime errors that can be faced by a client, like a bogus HTTP connection for example. + #[error(transparent)] + RuntimeError(#[from] RuntimeErrorKind), +} + +/// Enumeration holding the different kind of runtime errors (in contrast to genuine +/// attestation failure) that a client can get. +#[derive(Error, Debug)] +pub enum RuntimeErrorKind { + /// Can not connect to the keybroker server. + #[error("HTTP connection to {0} failed with error: {1}")] + HTTPConnect(String, String), + + /// Unexpected response from the keybroker server. + #[error("Unhandled HTTP response: {0}")] + HTTPResponse(String), + + /// Represents errors due to base64 decoding. + #[error("Failed to base64-decode {0} with error: {1}")] + Base64Decode(String, String), + + /// Represents errors due to base64 decoding. + #[error("Failed to JSON-deserialize {0} with error: {1}")] + JSONDeserialize(String, String), + + /// Represents errors related to TSM report generation. + #[error(transparent)] + TSMReport(#[from] tsm_report::TsmReportError), + + /// Represents errors in the key decryption. + #[error("Failed to decrypt {0} with error: {1}")] + Decrypt(String, String), + + /// Represents errors in the retrieval of the challenge from the keybroker server. + #[error("Challenge retrieval error: {0}")] + ChallengeRetrieval(String), + + /// Represents the error when the challenge has an incorrect number of bytes. + #[error("Challenge length error, expecting {0} but got {1} instead")] + ChallengeLength(usize, usize), + + /// Represents error that occured when attempting to generate the evidence. + #[error("Evidence generation error: {0}")] + EvidenceGeneration(String), + + /// Used when the response from the keybroker is missing the location field. + #[error("Missing location field in HTTP requets")] + MissingLocation, +} + +pub type Result = std::result::Result; diff --git a/rust-keybroker/keybroker-client/src/lib.rs b/rust-keybroker/keybroker-client/src/lib.rs index 7c2006e..02fe64d 100644 --- a/rust-keybroker/keybroker-client/src/lib.rs +++ b/rust-keybroker/keybroker-client/src/lib.rs @@ -1,17 +1,457 @@ // Copyright 2024 Contributors to the Veraison project. // SPDX-License-Identifier: Apache-2.0 -pub fn add(left: usize, right: usize) -> usize { - left + right +use base64::engine::general_purpose::URL_SAFE_NO_PAD; +use base64::prelude::*; +use keybroker_common::{ + BackgroundCheckKeyRequest, ErrorInformation, PublicWrappingKey, WrappedKeyData, +}; +use reqwest::StatusCode; +use rsa::{traits::PublicKeyParts, BigUint, Pkcs1v15Encrypt, RsaPrivateKey, RsaPublicKey}; +use tsm_report::{TsmReportData, TsmReportPath, TsmReportProvider}; + +pub mod error; +use crate::error::Error as KeybrokerError; +use crate::error::Result; +use crate::error::RuntimeErrorKind; + +/// The trait that must be implemented so a KeybrokerClient can retrieve the evidence it has +/// to submit to the Keybroker server. +pub trait EvidenceProvider { + fn get_evidence(&self, challenge: &str, verbose: bool) -> Result>; +} + +/// The CCA example token. +const CCA_EXAMPLE_TOKEN: &[u8] = &[ + 0xd9, 0x01, 0x8f, 0xa2, 0x19, 0xac, 0xca, 0x59, 0x05, 0xe7, 0xd2, 0x84, 0x44, 0xa1, 0x01, 0x38, + 0x22, 0xa0, 0x59, 0x05, 0x7a, 0xa9, 0x19, 0x01, 0x09, 0x78, 0x1c, 0x68, 0x74, 0x74, 0x70, 0x3a, + 0x2f, 0x2f, 0x61, 0x72, 0x6d, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x43, 0x43, 0x41, 0x2d, 0x53, 0x53, + 0x44, 0x2f, 0x31, 0x2e, 0x30, 0x2e, 0x30, 0x0a, 0x58, 0x20, 0xb5, 0x97, 0x3c, 0xb6, 0x8b, 0xaa, + 0x9f, 0xc5, 0x55, 0x58, 0x78, 0x6b, 0x7e, 0xc6, 0x7f, 0x69, 0xe4, 0x0d, 0xf5, 0xba, 0x5a, 0xa9, + 0x21, 0xcd, 0x0c, 0x27, 0xf4, 0x05, 0x87, 0xa0, 0x11, 0xea, 0x19, 0x09, 0x5c, 0x58, 0x20, 0x7f, + 0x45, 0x4c, 0x46, 0x02, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + 0x00, 0x3e, 0x00, 0x01, 0x00, 0x00, 0x00, 0x50, 0x58, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, + 0x01, 0x00, 0x58, 0x21, 0x01, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00, 0x0f, 0x0e, 0x0d, + 0x0c, 0x0b, 0x0a, 0x09, 0x08, 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, 0x1f, 0x1e, 0x1d, + 0x1c, 0x1b, 0x1a, 0x19, 0x18, 0x19, 0x09, 0x61, 0x44, 0xcf, 0xcf, 0xcf, 0xcf, 0x19, 0x09, 0x5b, + 0x19, 0x30, 0x03, 0x19, 0x09, 0x62, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0x19, 0x09, + 0x60, 0x78, 0x3a, 0x68, 0x74, 0x74, 0x70, 0x73, 0x3a, 0x2f, 0x2f, 0x76, 0x65, 0x72, 0x61, 0x69, + 0x73, 0x6f, 0x6e, 0x2e, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2f, 0x2e, 0x77, 0x65, 0x6c, + 0x6c, 0x2d, 0x6b, 0x6e, 0x6f, 0x77, 0x6e, 0x2f, 0x76, 0x65, 0x72, 0x61, 0x69, 0x73, 0x6f, 0x6e, + 0x2f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x19, 0x09, 0x5f, + 0x8d, 0xa4, 0x01, 0x69, 0x52, 0x53, 0x45, 0x5f, 0x42, 0x4c, 0x31, 0x5f, 0x32, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0x9a, 0x27, 0x1f, 0x2a, 0x91, 0x6b, 0x0b, 0x6e, 0xe6, 0xce, 0xcb, 0x24, 0x26, + 0xf0, 0xb3, 0x20, 0x6e, 0xf0, 0x74, 0x57, 0x8b, 0xe5, 0x5d, 0x9b, 0xc9, 0x4f, 0x6f, 0x3f, 0xe3, + 0xab, 0x86, 0xaa, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x67, 0x52, + 0x53, 0x45, 0x5f, 0x42, 0x4c, 0x32, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, + 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, + 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x53, 0xc2, 0x34, 0xe5, + 0xe8, 0x47, 0x2b, 0x6a, 0xc5, 0x1c, 0x1a, 0xe1, 0xca, 0xb3, 0xfe, 0x06, 0xfa, 0xd0, 0x53, 0xbe, + 0xb8, 0xeb, 0xfd, 0x89, 0x77, 0xb0, 0x10, 0x65, 0x5b, 0xfd, 0xd3, 0xc3, 0x06, 0x67, 0x73, 0x68, + 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x65, 0x52, 0x53, 0x45, 0x5f, 0x53, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0x11, 0x21, 0xcf, 0xcc, 0xd5, 0x91, 0x3f, 0x0a, 0x63, 0xfe, 0xc4, 0x0a, 0x6f, + 0xfd, 0x44, 0xea, 0x64, 0xf9, 0xdc, 0x13, 0x5c, 0x66, 0x63, 0x4b, 0xa0, 0x01, 0xd1, 0x0b, 0xcf, + 0x43, 0x02, 0xa2, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x66, 0x41, + 0x50, 0x5f, 0x42, 0x4c, 0x31, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, + 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, + 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x15, 0x71, 0xb5, 0xec, 0x78, + 0xbd, 0x68, 0x51, 0x2b, 0xf7, 0x83, 0x0b, 0xb6, 0xa2, 0xa4, 0x4b, 0x20, 0x47, 0xc7, 0xdf, 0x57, + 0xbc, 0xe7, 0x9e, 0xb8, 0xa1, 0xc0, 0xe5, 0xbe, 0xa0, 0xa5, 0x01, 0x06, 0x67, 0x73, 0x68, 0x61, + 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x66, 0x41, 0x50, 0x5f, 0x42, 0x4c, 0x32, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0x10, 0x15, 0x9b, 0xaf, 0x26, 0x2b, 0x43, 0xa9, 0x2d, 0x95, 0xdb, 0x59, 0xda, + 0xe1, 0xf7, 0x2c, 0x64, 0x51, 0x27, 0x30, 0x16, 0x61, 0xe0, 0xa3, 0xce, 0x4e, 0x38, 0xb2, 0x95, + 0xa9, 0x7c, 0x58, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x67, 0x53, + 0x43, 0x50, 0x5f, 0x42, 0x4c, 0x31, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, + 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, + 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x10, 0x12, 0x2e, 0x85, + 0x6b, 0x3f, 0xcd, 0x49, 0xf0, 0x63, 0x63, 0x63, 0x17, 0x47, 0x61, 0x49, 0xcb, 0x73, 0x0a, 0x1a, + 0xa1, 0xcf, 0xaa, 0xd8, 0x18, 0x55, 0x2b, 0x72, 0xf5, 0x6d, 0x6f, 0x68, 0x06, 0x67, 0x73, 0x68, + 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x67, 0x53, 0x43, 0x50, 0x5f, 0x42, 0x4c, 0x32, 0x05, + 0x58, 0x20, 0xf1, 0x4b, 0x49, 0x87, 0x90, 0x4b, 0xcb, 0x58, 0x14, 0xe4, 0x45, 0x9a, 0x05, 0x7e, + 0xd4, 0xd2, 0x0f, 0x58, 0xa6, 0x33, 0x15, 0x22, 0x88, 0xa7, 0x61, 0x21, 0x4d, 0xcd, 0x28, 0x78, + 0x0b, 0x56, 0x02, 0x58, 0x20, 0xaa, 0x67, 0xa1, 0x69, 0xb0, 0xbb, 0xa2, 0x17, 0xaa, 0x0a, 0xa8, + 0x8a, 0x65, 0x34, 0x69, 0x20, 0xc8, 0x4c, 0x42, 0x44, 0x7c, 0x36, 0xba, 0x5f, 0x7e, 0xa6, 0x5f, + 0x42, 0x2c, 0x1f, 0xe5, 0xd8, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, + 0x67, 0x41, 0x50, 0x5f, 0x42, 0x4c, 0x33, 0x31, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, + 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, + 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x2e, 0x6d, + 0x31, 0xa5, 0x98, 0x3a, 0x91, 0x25, 0x1b, 0xfa, 0xe5, 0xae, 0xfa, 0x1c, 0x0a, 0x19, 0xd8, 0xba, + 0x3c, 0xf6, 0x01, 0xd0, 0xe8, 0xa7, 0x06, 0xb4, 0xcf, 0xa9, 0x66, 0x1a, 0x6b, 0x8a, 0x06, 0x67, + 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x63, 0x52, 0x4d, 0x4d, 0x05, 0x58, 0x20, + 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, + 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, + 0x02, 0x58, 0x20, 0xa1, 0xfb, 0x50, 0xe6, 0xc8, 0x6f, 0xae, 0x16, 0x79, 0xef, 0x33, 0x51, 0x29, + 0x6f, 0xd6, 0x71, 0x34, 0x11, 0xa0, 0x8c, 0xf8, 0xdd, 0x17, 0x90, 0xa4, 0xfd, 0x05, 0xfa, 0xe8, + 0x68, 0x81, 0x64, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x69, 0x48, + 0x57, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, + 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, + 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x1a, 0x25, + 0x24, 0x02, 0x97, 0x2f, 0x60, 0x57, 0xfa, 0x53, 0xcc, 0x17, 0x2b, 0x52, 0xb9, 0xff, 0xca, 0x69, + 0x8e, 0x18, 0x31, 0x1f, 0xac, 0xd0, 0xf3, 0xb0, 0x6e, 0xca, 0xae, 0xf7, 0x9e, 0x17, 0x06, 0x67, + 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0xa4, 0x01, 0x69, 0x46, 0x57, 0x5f, 0x43, 0x4f, 0x4e, + 0x46, 0x49, 0x47, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, + 0x8b, 0x15, 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, + 0xc0, 0xfa, 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x9a, 0x92, 0xad, 0xbc, 0x0c, 0xee, 0x38, + 0xef, 0x65, 0x8c, 0x71, 0xce, 0x1b, 0x1b, 0xf8, 0xc6, 0x56, 0x68, 0xf1, 0x66, 0xbf, 0xb2, 0x13, + 0x64, 0x4c, 0x89, 0x5c, 0xcb, 0x1a, 0xd0, 0x7a, 0x25, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, + 0x35, 0x36, 0xa4, 0x01, 0x6c, 0x54, 0x42, 0x5f, 0x46, 0x57, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, + 0x47, 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, + 0xa2, 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, + 0x97, 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0x23, 0x89, 0x03, 0x18, 0x0c, 0xc1, 0x04, 0xec, 0x2c, + 0x5d, 0x8b, 0x3f, 0x20, 0xc5, 0xbc, 0x61, 0xb3, 0x89, 0xec, 0x0a, 0x96, 0x7d, 0xf8, 0xcc, 0x20, + 0x8c, 0xdc, 0x7c, 0xd4, 0x54, 0x17, 0x4f, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, + 0xa4, 0x01, 0x6d, 0x53, 0x4f, 0x43, 0x5f, 0x46, 0x57, 0x5f, 0x43, 0x4f, 0x4e, 0x46, 0x49, 0x47, + 0x05, 0x58, 0x20, 0x53, 0x78, 0x79, 0x63, 0x07, 0x53, 0x5d, 0xf3, 0xec, 0x8d, 0x8b, 0x15, 0xa2, + 0xe2, 0xdc, 0x56, 0x41, 0x41, 0x9c, 0x3d, 0x30, 0x60, 0xcf, 0xe3, 0x22, 0x38, 0xc0, 0xfa, 0x97, + 0x3f, 0x7a, 0xa3, 0x02, 0x58, 0x20, 0xe6, 0xc2, 0x1e, 0x8d, 0x26, 0x0f, 0xe7, 0x18, 0x82, 0xde, + 0xbd, 0xb3, 0x39, 0xd2, 0x40, 0x2a, 0x2c, 0xa7, 0x64, 0x85, 0x29, 0xbc, 0x23, 0x03, 0xf4, 0x86, + 0x49, 0xbc, 0xe0, 0x38, 0x00, 0x17, 0x06, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0x58, + 0x60, 0x21, 0x51, 0x20, 0x92, 0xd6, 0xd0, 0x2a, 0xe6, 0xbe, 0x2f, 0xe3, 0x93, 0x0e, 0xa5, 0x1f, + 0xd6, 0x98, 0x96, 0x32, 0x24, 0x56, 0xe9, 0xdf, 0xc7, 0x32, 0x5e, 0x0b, 0x78, 0x68, 0xb6, 0x90, + 0x73, 0x2a, 0x0c, 0x0f, 0x07, 0x77, 0xc1, 0x15, 0x40, 0x4b, 0xe1, 0xfc, 0x83, 0x9b, 0x7d, 0x30, + 0x4f, 0x4f, 0xe6, 0xfa, 0x46, 0xae, 0x12, 0xa3, 0x08, 0x3a, 0xcf, 0x24, 0x06, 0x67, 0x91, 0x06, + 0xbf, 0xae, 0x50, 0x31, 0x79, 0xdd, 0x50, 0x33, 0x49, 0x12, 0xbf, 0xc6, 0xda, 0x33, 0x6d, 0xd6, + 0x18, 0x25, 0x43, 0x54, 0x4d, 0xb5, 0x88, 0xd6, 0xae, 0x67, 0x35, 0x7a, 0xfd, 0xb0, 0x5f, 0x95, + 0xb7, 0x19, 0xac, 0xd1, 0x59, 0x02, 0x23, 0xd2, 0x84, 0x44, 0xa1, 0x01, 0x38, 0x22, 0xa0, 0x59, + 0x01, 0xb6, 0xa7, 0x0a, 0x58, 0x40, 0x6e, 0x86, 0xd6, 0xd9, 0x7c, 0xc7, 0x13, 0xbc, 0x6d, 0xd4, + 0x3d, 0xbc, 0xe4, 0x91, 0xa6, 0xb4, 0x03, 0x11, 0xc0, 0x27, 0xa8, 0xbf, 0x85, 0xa3, 0x9d, 0xa6, + 0x3e, 0x9c, 0xe4, 0x4c, 0x13, 0x2a, 0x8a, 0x11, 0x9d, 0x29, 0x6f, 0xae, 0x6a, 0x69, 0x99, 0xe9, + 0xbf, 0x3e, 0x44, 0x71, 0xb0, 0xce, 0x01, 0x24, 0x5d, 0x88, 0x94, 0x24, 0xc3, 0x1e, 0x89, 0x79, + 0x3b, 0x3b, 0x1d, 0x6b, 0x15, 0x04, 0x19, 0xac, 0xcc, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, + 0x36, 0x19, 0xac, 0xd0, 0x67, 0x73, 0x68, 0x61, 0x2d, 0x32, 0x35, 0x36, 0x19, 0xac, 0xcb, 0x58, + 0x40, 0x54, 0x68, 0x65, 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, + 0x20, 0x66, 0x6f, 0x78, 0x20, 0x6a, 0x75, 0x6d, 0x70, 0x73, 0x20, 0x6f, 0x76, 0x65, 0x72, 0x20, + 0x31, 0x33, 0x20, 0x6c, 0x61, 0x7a, 0x79, 0x20, 0x64, 0x6f, 0x67, 0x73, 0x2e, 0x54, 0x68, 0x65, + 0x20, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x20, 0x62, 0x72, 0x6f, 0x77, 0x6e, 0x20, 0x66, 0x6f, 0x78, + 0x20, 0x19, 0xac, 0xcd, 0x58, 0x61, 0x04, 0x76, 0xf9, 0x88, 0x09, 0x1b, 0xe5, 0x85, 0xed, 0x41, + 0x80, 0x1a, 0xec, 0xfa, 0xb8, 0x58, 0x54, 0x8c, 0x63, 0x05, 0x7e, 0x16, 0xb0, 0xe6, 0x76, 0x12, + 0x0b, 0xbd, 0x0d, 0x2f, 0x9c, 0x29, 0xe0, 0x56, 0xc5, 0xd4, 0x1a, 0x01, 0x30, 0xeb, 0x9c, 0x21, + 0x51, 0x78, 0x99, 0xdc, 0x23, 0x14, 0x6b, 0x28, 0xe1, 0xb0, 0x62, 0xbd, 0x3e, 0xa4, 0xb3, 0x15, + 0xfd, 0x21, 0x9f, 0x1c, 0xbb, 0x52, 0x8c, 0xb6, 0xe7, 0x4c, 0xa4, 0x9b, 0xe1, 0x67, 0x73, 0x73, + 0x4f, 0x61, 0xa1, 0xca, 0x61, 0x03, 0x1b, 0x2b, 0xbf, 0x3d, 0x91, 0x8f, 0x2f, 0x94, 0xff, 0xc4, + 0x22, 0x8e, 0x50, 0x91, 0x95, 0x44, 0xae, 0x19, 0xac, 0xce, 0x58, 0x20, 0x31, 0x13, 0x14, 0xab, + 0x73, 0x62, 0x03, 0x50, 0xcf, 0x75, 0x88, 0x34, 0xae, 0x5c, 0x65, 0xd9, 0xe8, 0xc2, 0xdc, 0x7f, + 0xeb, 0xe6, 0xe7, 0xd9, 0x65, 0x4b, 0xbe, 0x86, 0x4e, 0x30, 0x0d, 0x49, 0x19, 0xac, 0xcf, 0x84, + 0x58, 0x20, 0x24, 0xd5, 0xb0, 0xa2, 0x96, 0xcc, 0x05, 0xcb, 0xd8, 0x06, 0x8c, 0x50, 0x67, 0xc5, + 0xbd, 0x47, 0x3b, 0x77, 0x0d, 0xda, 0x6a, 0xe0, 0x82, 0xfe, 0x3b, 0xa3, 0x0a, 0xbe, 0x3f, 0x9a, + 0x6a, 0xb1, 0x58, 0x20, 0x78, 0x8f, 0xc0, 0x90, 0xbf, 0xc6, 0xb8, 0xed, 0x90, 0x31, 0x52, 0xba, + 0x84, 0x14, 0xe7, 0x3d, 0xaf, 0x5b, 0x8c, 0x7b, 0xb1, 0xe7, 0x9a, 0xd5, 0x02, 0xab, 0x06, 0x99, + 0xb6, 0x59, 0xed, 0x16, 0x58, 0x20, 0xda, 0xc4, 0x6a, 0x58, 0x41, 0x5d, 0xc3, 0xa0, 0x0d, 0x7a, + 0x74, 0x18, 0x52, 0x00, 0x8e, 0x9c, 0xae, 0x64, 0xf5, 0x2d, 0x03, 0xb9, 0xf7, 0x6d, 0x76, 0xf4, + 0xb3, 0x64, 0x4f, 0xef, 0xc4, 0x16, 0x58, 0x20, 0x32, 0xc6, 0xaf, 0xc6, 0x27, 0xe5, 0x55, 0x85, + 0xc0, 0x31, 0x55, 0x35, 0x9f, 0x33, 0x1a, 0x0e, 0x22, 0x5f, 0x68, 0x40, 0xdb, 0x94, 0x7d, 0xd9, + 0x6e, 0xfa, 0xb8, 0x1b, 0xe2, 0x67, 0x19, 0x39, 0x58, 0x60, 0x6e, 0xaf, 0x8c, 0x12, 0xcd, 0x0f, + 0xc0, 0xd6, 0x2b, 0x3d, 0x3b, 0x84, 0xa1, 0x6a, 0x6b, 0x76, 0x28, 0xdb, 0xc8, 0xe3, 0x46, 0xfb, + 0x9f, 0x09, 0x44, 0xda, 0x5a, 0x81, 0xd0, 0x09, 0x32, 0x33, 0x9c, 0x6d, 0x6b, 0xd1, 0xf8, 0x91, + 0xea, 0x64, 0xc0, 0xb0, 0x42, 0xf5, 0x1c, 0xa3, 0x31, 0x87, 0xed, 0xbb, 0xe7, 0xf0, 0xa3, 0x95, + 0x98, 0x55, 0x06, 0x89, 0x09, 0x3b, 0x0d, 0x99, 0xbb, 0xf3, 0x8e, 0x93, 0x2c, 0x5e, 0x2e, 0x18, + 0x26, 0xc4, 0xdb, 0xc3, 0xed, 0x3e, 0x49, 0x44, 0x18, 0xb0, 0x4f, 0x79, 0x1d, 0x88, 0xda, 0x64, + 0xec, 0x3a, 0x3f, 0x16, 0x50, 0x96, 0x51, 0xe7, 0x30, 0x97, +]; + +/// An EvidenceProvider mock. +/// +/// The CcaExampleToken implementation of the EvidenceProvider trait is really +/// nothing but a mock, ignoring the challenge and returning the static evidence +/// that is used in the CCA exemple. This is useful during bring-up or debug to +/// un-tie the link between the key request and the evidence submission. +pub struct CcaExampleToken {} + +impl EvidenceProvider for CcaExampleToken { + fn get_evidence(&self, _challenge: &str, _verbose: bool) -> Result> { + Ok(CCA_EXAMPLE_TOKEN.to_vec()) + } } -#[cfg(test)] -mod tests { - use super::*; +/// A TSM attestation report implementation of EvidenceProvider. +/// +/// The TsmAttestationReport implementation of the EvidenceProvider trait uses +/// Linux's TSM attestation report infrastructure to construct an evidence from +/// a challenge. +pub struct TsmAttestationReport {} + +impl EvidenceProvider for TsmAttestationReport { + fn get_evidence(&self, challenge: &str, verbose: bool) -> Result> { + match TsmReportPath::new(TsmReportProvider::Cca) { + Ok(tsm_report_path) => match URL_SAFE_NO_PAD.decode(challenge) { + Ok(challenge) => { + if verbose { + println!("Challenge ({} bytes) = {:02x?}", challenge.len(), challenge); + } + if challenge.len() != 64 { + return Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::ChallengeLength(64, challenge.len()), + )); + }; + match tsm_report_path.attestation_report(TsmReportData::Cca(challenge)) { + Ok(ar) => Ok(ar), + Err(error) => Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::TSMReport(error), + )), + } + } + Err(error) => Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::Base64Decode( + "the attestation challenge".to_string(), + format!("{error:?}"), + ), + )), + }, + Err(error) => Err(KeybrokerError::RuntimeError(RuntimeErrorKind::TSMReport( + error, + ))), + } + } +} + +#[derive(Debug)] +struct AttestationChallenge { + pub challenge: String, + pub evidence_submission_url: String, +} + +/// The KeyBrokerSession models the communication with a keybroker server. +#[derive(Debug)] +pub struct KeyBrokerClient { + /// The client this session will use to interact with the keybroker server + /// over HTTP with post calls. A blocking client is used for simplicity. + client: reqwest::blocking::Client, + + /// The keybroker URL base address. + keybroker_url_base: String, + + /// The session verbosity. + /// + /// The verbose flag serves 2 purposes: help the developer when diagnosing some + /// issue, but also the new comer to the code base when understanding the overall + /// flow is intended. + verbose: bool, +} + +impl KeyBrokerClient { + /// Create a session to the keybroker server located at addr:port. + pub fn new(endpoint: &str, verbose: bool) -> KeyBrokerClient { + KeyBrokerClient { + client: reqwest::blocking::Client::new(), + keybroker_url_base: endpoint.to_string(), + verbose, + } + } + + /// The first API call to request the key. This gets all the required + /// attestation challenge material: the challenge it self, and the url + /// where to submit the evidence. + fn request_key( + self: &KeyBrokerClient, + key_name: &str, + pub_key: &RsaPublicKey, + ) -> Result { + // Create base64 strings for the public key modulus and exponent parts. + let k_mod_base64 = URL_SAFE_NO_PAD.encode(BigUint::to_bytes_be(pub_key.n())); + let k_exp_base64 = URL_SAFE_NO_PAD.encode(BigUint::to_bytes_be(pub_key.e())); + + // ... and turn them into an API-level input + let key_request = BackgroundCheckKeyRequest { + pubkey: PublicWrappingKey { + kty: "RSA".to_string(), + alg: "RSA1_5".to_string(), + n: k_mod_base64, + e: k_exp_base64, + }, + }; + + // Construct the URL to request the key. + let key_request_url = format!("{}/keys/v1/key/{}", self.keybroker_url_base, key_name); + + if self.verbose { + println!( + "Requesting key named '{key_name}' from the keybroker server with URL {key_request_url}" + ); + } + + // Make the first API call to request the key. + match self.client.post(&key_request_url).json(&key_request).send() { + Ok(resp) => { + let evidence_submission_url = match resp.headers().get(reqwest::header::LOCATION) { + Some(url) => url.to_str().unwrap().to_owned(), + None => { + return Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::MissingLocation, + )) + } + }; + + let ac = match resp.json::() { + Ok(ac) => ac, + Err(error) => { + return Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::JSONDeserialize( + "the attestation challenge".to_string(), + format!("{error:?}"), + ), + )) + } + }; + + Ok(AttestationChallenge { + challenge: ac.challenge, + evidence_submission_url, + }) + } + Err(error) => Err(KeybrokerError::RuntimeError(RuntimeErrorKind::HTTPConnect( + key_request_url, + format!("{error:?}"), + ))), + } + } + + /// Submit the evidence. + /// In case of success, this returns the decoded key from the server. + fn submit_evidence( + self: &KeyBrokerClient, + evidence_submission_url: &str, + evidence: &[u8], + ) -> Result> { + if self.verbose { + println!("Submitting evidence to URL {evidence_submission_url}") + } + + // Make the second API call to submit the evidence. + match self + .client + .post(evidence_submission_url) + .header( + reqwest::header::CONTENT_TYPE, + "application/eat-collection; profile=\"http://arm.com/CCA-SSD/1.0.0\"", + ) + .body(URL_SAFE_NO_PAD.encode(evidence)) + .send() + { + Ok(resp) => { + let wrapped_data_name = "the wrapped data from the server".to_string(); + match resp.status() { + // Assume first that we are following the happy path: our evidence was "accepted". + StatusCode::OK => { + let wrapped_data = match resp.json::() { + Ok(wrapped_data) => wrapped_data, + Err(error) => { + return Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::JSONDeserialize( + "the evidence WrappedKeyData".to_string(), + format!("{error:?}"), + ), + )) + } + }; + match URL_SAFE_NO_PAD.decode(wrapped_data.data) { + Ok(ciphertext) => Ok(ciphertext), + Err(error) => Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::Base64Decode( + wrapped_data_name, + format!("{error:?}"), + ), + )), + } + } + + // Our evidence has been rejected for some "good" reasons. + StatusCode::FORBIDDEN => { + let wrapped_data = match resp.json::() { + Ok(wrapped_data) => wrapped_data, + Err(error) => { + return Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::JSONDeserialize( + "the evidence ErrorInformation".to_string(), + format!("{error:?}"), + ), + )) + } + }; + Err(crate::error::Error::AttestationFailure( + wrapped_data.r#type, + wrapped_data.detail, + )) + } + + // We have a genuine and/or unhandled error :-() + status => Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::HTTPResponse(format!("{status:?}")), + )), + } + } + + Err(error) => Err(KeybrokerError::RuntimeError(RuntimeErrorKind::HTTPConnect( + evidence_submission_url.to_string(), + format!("{error:?}"), + ))), + } + } + + /// Get the wrapped key, decryption left to the caller. + pub fn get_wrapped_key( + self: &KeyBrokerClient, + key_name: &str, + evidence_provider: &EP, + pub_key: &RsaPublicKey, + ) -> Result> { + // First API call: request the challenge. + let data = match self.request_key(key_name, pub_key) { + Ok(data) => data, + Err(error) => { + return Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::ChallengeRetrieval(format!("{error:?}")), + )) + } + }; + + // Produce the evidence. + let evidence = match evidence_provider.get_evidence(&data.challenge, self.verbose) { + Ok(evidence) => evidence, + Err(error) => { + // TODO: we may want to notify the keybroker server that something went wrong on our side and that it + // should release any resource it has allocated for us. This could possibly be done using some + // form of "abandon" request, or we could simulate this by submitting a bogus evidence (which will + // not give a successful attestation, but that's expected). OTOH, the server will also need to + // have some form of timeout associated with all the key requests and have some garbage collection + // pass to be run from time to time. A well behaved client could be kind though and notify the + // keybroker server. + return Err(KeybrokerError::RuntimeError( + RuntimeErrorKind::EvidenceGeneration(format!("{error:?}")), + )); + } + }; + + // Second API call: submit the evidence, and return the attestation result. + self.submit_evidence(&data.evidence_submission_url, &evidence) + } + + /// This returns the plain text. + pub fn get_key( + self: &KeyBrokerClient, + key_name: &str, + evidence_provider: &EP, + ) -> Result> { + // Create an ephemeral wrapping key-pair for our own use. + let mut rng = rand::thread_rng(); + let priv_key = RsaPrivateKey::new(&mut rng, 1024 /* bits */) + .expect("Failed to generate ephemeral wrapping key."); + let pub_key = RsaPublicKey::from(&priv_key); - #[test] - fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + match self.get_wrapped_key(key_name, evidence_provider, &pub_key) { + Ok(ciphertext) => match priv_key.decrypt(Pkcs1v15Encrypt, &ciphertext) { + Ok(plaintext) => Ok(plaintext), + Err(error) => Err(KeybrokerError::RuntimeError(RuntimeErrorKind::Decrypt( + "ciphertext".to_string(), + format!("{error:?}"), + ))), + }, + other => other, + } } } diff --git a/rust-keybroker/keybroker-server/src/main.rs b/rust-keybroker/keybroker-server/src/main.rs index 2bee8ba..d2571f8 100644 --- a/rust-keybroker/keybroker-server/src/main.rs +++ b/rust-keybroker/keybroker-server/src/main.rs @@ -38,7 +38,7 @@ async fn request_key( let location = format!( "{}/keys/v1/evidence/{}", - data.base_url, challenge.challenge_id + data.endpoint, challenge.challenge_id ); if data.args.verbose { @@ -149,12 +149,12 @@ struct Args { #[arg(short, long, default_value_t = 8088)] port: u16, - /// The base URL at which this server can be reached back for evidence submission. - /// If not specified on the command line, it will be set to 'http://{addr}', but - /// this value can be overridden with an FQDN for {addr} in order to use name resolution. - /// The port number will be appended, so don't leave a trailing '/' to the URL. + /// The address at which this server can be reached to request a key or submit an evidence. + /// It will be set by default to 'http://{addr}', but this value can be overridden with + /// an FQDN for {addr} in order to use name resolution for example. + /// The port number will be appended, so don't leave a trailing '/' to the FQDN. #[arg(short, long, default_value = None)] - base_url: Option, + endpoint: Option, /// The URL where the verifier can be reached #[arg(long, default_value = "http://veraison.test.linaro.org:8080")] @@ -171,7 +171,7 @@ struct Args { struct ServerState { args: Args, - base_url: String, + endpoint: String, keystore: Mutex, challenger: Mutex, } @@ -192,7 +192,7 @@ async fn main() -> std::io::Result<()> { let server_state = ServerState { args: args.clone(), - base_url: match args.base_url { + endpoint: match args.endpoint { Some(url) => format!("{}:{}", url, args.port), None => format!("http://{}:{}", args.addr, args.port), },