Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions attestation-agent/attestation-agent/config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ url = "http://127.0.0.1:8000"
[token_configs.kbs]
url = "https://127.0.0.1:8080"
tee_key_algorithm = "ECDH-ES+A256KW-P256"
# The hint that KBS resolves to the attestation policies that evaluate this guest's
# evidence. Empty by default, which leaves the selection to KBS. The accepted
# values are specific to a KBS deployment and an unknown id is rejected.
# policy_selector = "alice"
cert = '''
-----BEGIN CERTIFICATE-----
MIIDljCCAn6gAwIBAgIUR/UNh13GFam4emgludtype/S9BIwDQYJKoZIhvcNAQEL
Expand Down
10 changes: 10 additions & 0 deletions attestation-agent/attestation-agent/src/config/kbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,15 @@ pub struct KbsConfig {

#[serde(default)]
pub tee_key_algorithm: TeeKeyAlgorithm,

/// The hint that KBS resolves to the attestation policies that evaluate this
/// guest's evidence. The accepted values are specific to a KBS
/// deployment, so this has to be agreed with the KBS administrator.
///
/// Empty by default, which leaves the policy selection to KBS. An empty
/// policy_selector is not sent, because KBS rejects a policy_selector it does not know.
#[serde(default)]
pub policy_selector: String,
}

impl KbsConfig {
Expand All @@ -35,6 +44,7 @@ impl KbsConfig {
url: aa_kbc_params.uri,
cert: None,
tee_key_algorithm: TeeKeyAlgorithm::default(),
policy_selector: String::new(),
})
}
}
24 changes: 24 additions & 0 deletions attestation-agent/attestation-agent/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ M9QaC1mzQ/OStg==
-----END CERTIFICATE-----
".to_string()),
tee_key_algorithm: kbs_protocol::TeeKeyAlgorithm::EcdhEsA256KwP256,
policy_selector: String::new(),
})
},
eventlog_config: EventlogConfig {
Expand Down Expand Up @@ -224,6 +225,7 @@ M9QaC1mzQ/OStg==
-----END CERTIFICATE-----
".to_string()),
tee_key_algorithm: kbs_protocol::TeeKeyAlgorithm::EcdhEsA256KwP256,
policy_selector: String::new(),
})
},
eventlog_config: EventlogConfig {
Expand All @@ -245,6 +247,7 @@ M9QaC1mzQ/OStg==
url: "https://127.0.0.1:8080".to_string(),
cert: Some("cert".to_string()),
tee_key_algorithm: kbs_protocol::TeeKeyAlgorithm::EcdhEsA256KwP256,
policy_selector: String::new(),
})
},
eventlog_config: EventlogConfig {
Expand All @@ -264,6 +267,27 @@ M9QaC1mzQ/OStg==
url: "https://127.0.0.1:8080".to_string(),
cert: Some("cert".to_string()),
tee_key_algorithm: kbs_protocol::TeeKeyAlgorithm::EcdhEsA256KwP256,
policy_selector: String::new(),
})
},
eventlog_config: EventlogConfig {
init_pcr: 17,
enable_eventlog: false,
},
log: LogConfig { level: "warn".to_string() },
})]
#[case(
"test/config7.toml",
Config {
token_configs: TokenConfigs {
#[cfg(feature = "coco_as")]
coco_as: None,
#[cfg(feature = "kbs")]
kbs: Some(crate::config::kbs::KbsConfig {
url: "https://127.0.0.1:8080".to_string(),
cert: Some("cert".to_string()),
tee_key_algorithm: kbs_protocol::TeeKeyAlgorithm::EcdhEsA256KwP256,
policy_selector: "alice".to_string(),
})
},
eventlog_config: EventlogConfig {
Expand Down
6 changes: 6 additions & 0 deletions attestation-agent/attestation-agent/src/token/kbs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ pub struct KbsTokenGetter {
kbs_host_url: String,
cert: Option<String>,
tee_key_algorithm: TeeKeyAlgorithm,
policy_selector: String,
}

impl KbsTokenGetter {
Expand All @@ -34,6 +35,10 @@ impl KbsTokenGetter {
}
builder = builder.set_tee_key_algorithm(self.tee_key_algorithm);

if !self.policy_selector.is_empty() {
builder = builder.set_policy_selector(&self.policy_selector);
}

if let Some(initdata) = initdata {
builder = builder.add_initdata(initdata.to_string());
}
Expand All @@ -57,6 +62,7 @@ impl KbsTokenGetter {
kbs_host_url: config.url.clone(),
cert: config.cert.clone(),
tee_key_algorithm: config.tee_key_algorithm,
policy_selector: config.policy_selector.clone(),
}
}
}
12 changes: 12 additions & 0 deletions attestation-agent/attestation-agent/test/config7.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[log]
level = "warn"

[token_configs]
[token_configs.kbs]
url = "https://127.0.0.1:8080"
cert = "cert"
policy_selector = "alice"

[eventlog_config]
init_pcr = 17
enable_eventlog = false
35 changes: 35 additions & 0 deletions attestation-agent/kbs_protocol/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ pub struct KbsClientBuilder<T> {
tee_key: Option<String>,
tee_key_algorithm: TeeKeyAlgorithm,
initdata: Option<String>,
policy_selector: Option<String>,
}

impl KbsClientBuilder<Box<dyn EvidenceProvider>> {
Expand All @@ -42,6 +43,7 @@ impl KbsClientBuilder<Box<dyn EvidenceProvider>> {
tee_key: None,
tee_key_algorithm: TeeKeyAlgorithm::default(),
initdata: None,
policy_selector: None,
}
}
}
Expand All @@ -56,6 +58,7 @@ impl KbsClientBuilder<Box<dyn TokenProvider>> {
tee_key: None,
tee_key_algorithm: TeeKeyAlgorithm::default(),
initdata: None,
policy_selector: None,
}
}
}
Expand Down Expand Up @@ -86,6 +89,18 @@ impl<T> KbsClientBuilder<T> {
self
}

/// Set the policy selector which KBS resolves to the attestation policies that
/// evaluate this client's evidence.
///
/// The accepted values are specific to a KBS deployment, so a policy selector has to
/// be known in advance. KBS rejects the RCAR handshake of a client that
/// sends a policy selector it does not know, while a client that sends no policy selector at all
/// is evaluated against the default policy.
pub fn set_policy_selector(mut self, policy_selector: &str) -> Self {
self.policy_selector = Some(policy_selector.to_string());
self
}

pub fn build(self) -> Result<KbsClient<T>> {
let mut http_client_builder = reqwest::Client::builder()
.cookie_store(true)
Expand Down Expand Up @@ -126,6 +141,7 @@ impl<T> KbsClientBuilder<T> {
.context("Build KBS http client")?,
kbs_host_url: self.kbs_host_url,
_initdata: self.initdata,
_policy_selector: self.policy_selector,
};

Ok(client)
Expand Down Expand Up @@ -163,4 +179,23 @@ x13TMfDeczAFBgMrZXADQQBpP6ABBkzVj3mF55nWUtP5vxwq3t91wqQJ6NyC7WsT
.build()
.expect("build client failed");
}

#[rstest]
#[case(None)]
#[case(Some("alice"))]
#[tokio::test]
async fn test_build_client_with_policy_selector(#[case] policy_selector: Option<&str>) {
let mut builder = KbsClientBuilder::with_evidence_provider(
Box::<MockedEvidenceProvider>::default(),
"test.io",
);

if let Some(policy_selector) = policy_selector {
builder = builder.set_policy_selector(policy_selector);
}

let client = builder.build().expect("build client failed");

assert_eq!(client._policy_selector.as_deref(), policy_selector);
}
}
4 changes: 4 additions & 0 deletions attestation-agent/kbs_protocol/src/client/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ pub struct KbsClient<T> {

/// initdata toml plaintext (if any)
pub(crate) _initdata: Option<String>,

/// id selecting the attestation policies that evaluate this client's
/// evidence (if any)
pub(crate) _policy_selector: Option<String>,
}

pub const KBS_PROTOCOL_VERSION: &str = "0.4.0";
Expand Down
53 changes: 41 additions & 12 deletions attestation-agent/kbs_protocol/src/client/rcar_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,10 @@ const RCAR_RETRY_TIMEOUT_SECOND: u64 = 1;
/// JSON object added to a 'Request's extra parameters.
const SUPPORTED_HASH_ALGORITHMS_JSON_KEY: &str = "supported-hash-algorithms";

/// JSON key in a 'Request's extra parameters with which the client selects
/// the attestation policies that evaluate its evidence.
const POLICY_SELECTOR_JSON_KEY: &str = "policy-selector";

/// JSON object returned in the Challenge whose value is based on
/// SUPPORTED_HASH_ALGORITHMS_JSON_KEY and the TEE.
const SELECTED_HASH_ALGORITHM_JSON_KEY: &str = "selected-hash-algorithm";
Expand All @@ -54,10 +58,14 @@ struct AttestationResponseData {
token: String,
}

async fn get_request_extra_params() -> serde_json::Value {
async fn get_request_extra_params(policy_selector: Option<&str>) -> serde_json::Value {
let supported_hash_algorithms = HashAlgorithm::list_all();

let extra_params = json!({SUPPORTED_HASH_ALGORITHMS_JSON_KEY: supported_hash_algorithms});
let mut extra_params = json!({SUPPORTED_HASH_ALGORITHMS_JSON_KEY: supported_hash_algorithms});

if let Some(policy_selector) = policy_selector {
extra_params[POLICY_SELECTOR_JSON_KEY] = json!(policy_selector);
}

extra_params
}
Expand Down Expand Up @@ -86,8 +94,8 @@ fn serialize_json_canonically<T: Serialize>(value: T) -> anyhow::Result<Vec<u8>>
Ok(serde_json_canonicalizer::to_vec(&value)?)
}

async fn build_request(tee: Tee) -> Request {
let extra_params = get_request_extra_params().await;
async fn build_request(tee: Tee, policy_selector: Option<&str>) -> Request {
let extra_params = get_request_extra_params(policy_selector).await;

// Note that the Request includes the list of supported hash algorithms.
// The Challenge response will return which TEE-specific algorithm should
Expand Down Expand Up @@ -230,7 +238,7 @@ impl KbsClient<Box<dyn EvidenceProvider>> {
ClientTee::_Initialized(tee) => *tee,
};

let request = build_request(tee).await;
let request = build_request(tee, self._policy_selector.as_deref()).await;

debug!("send auth request {request:?} to {auth_endpoint}");

Expand Down Expand Up @@ -417,8 +425,8 @@ mod test {

use crate::client::rcar_client::{
build_request, get_hash_algorithm, get_request_extra_params, Result,
DEFAULT_HASH_ALGORITHM, KBS_PROTOCOL_VERSION, SELECTED_HASH_ALGORITHM_JSON_KEY,
SUPPORTED_HASH_ALGORITHMS_JSON_KEY,
DEFAULT_HASH_ALGORITHM, KBS_PROTOCOL_VERSION, POLICY_SELECTOR_JSON_KEY,
SELECTED_HASH_ALGORITHM_JSON_KEY, SUPPORTED_HASH_ALGORITHMS_JSON_KEY,
};
use kbs_types::Tee;

Expand Down Expand Up @@ -540,13 +548,24 @@ mod test {
println!("Get key: {key:?}");
}

#[rstest]
#[case(None)]
#[case(Some("alice"))]
#[tokio::test]
#[serial_test::serial]
async fn test_get_request_extra_params() {
let extra_params = get_request_extra_params().await;
async fn test_get_request_extra_params(#[case] policy_selector: Option<&str>) {
let extra_params = get_request_extra_params(policy_selector).await;

assert!(extra_params.is_object());

assert_eq!(
extra_params
.get(POLICY_SELECTOR_JSON_KEY)
.and_then(Value::as_str),
policy_selector,
"the id is only sent when it is set"
);

let algos_json = extra_params
.get(SUPPORTED_HASH_ALGORITHMS_JSON_KEY)
.unwrap();
Expand All @@ -565,9 +584,12 @@ mod test {
}
}

#[rstest]
#[case(None)]
#[case(Some("alice"))]
#[tokio::test]
#[serial_test::serial]
async fn test_build_request() {
async fn test_build_request(#[case] policy_selector: Option<&str>) {
let tees = vec![
Tee::AzSnpVtpm,
Tee::AzTdxVtpm,
Expand All @@ -580,14 +602,21 @@ mod test {
];

let expected_version = String::from(KBS_PROTOCOL_VERSION);
let expected_extra_params = get_request_extra_params().await;
let expected_extra_params = get_request_extra_params(policy_selector).await;

for tee in tees {
let request = build_request(tee).await;
let request = build_request(tee, policy_selector).await;

assert_eq!(request.version, expected_version);
assert_eq!(request.tee, tee);
assert_eq!(request.extra_params, expected_extra_params);
assert_eq!(
request
.extra_params
.get(POLICY_SELECTOR_JSON_KEY)
.and_then(Value::as_str),
policy_selector
);
}
}

Expand Down
Loading