Add support for IASv4 for remote attestation

This commit is contained in:
Jon Chambers 2022-01-13 18:16:40 -05:00 committed by GitHub
parent 0d9e9aaaad
commit f3c5c9efe1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 82 additions and 22 deletions

View File

@ -4,14 +4,14 @@ Binary: kbupd-enclave
Architecture: amd64
Version: 1.0
Checksums-Md5:
7eaa99647090389e9ac1beeba147341f 590772 kbupd-enclave_1.0_amd64.deb
ba23fd0b25f3204da0cf3ba59ed7c54b 592228 kbupd-enclave_1.0_amd64.deb
Checksums-Sha1:
6b5aabb72f1123decede3f65ad80e3ed8e5d50f6 590772 kbupd-enclave_1.0_amd64.deb
51130f444f5a0c61289975ba10cb27d60e7301b6 592228 kbupd-enclave_1.0_amd64.deb
Checksums-Sha256:
61602c437d9dcf8c56bb06a89a1acc1d8af02043cf8333b7605598d45b5ad7fe 590772 kbupd-enclave_1.0_amd64.deb
9b3b543c52e23008f2e56151b6cb2db89dc14c06921132c7545993ce10709f2d 592228 kbupd-enclave_1.0_amd64.deb
Build-Origin: Debian
Build-Architecture: amd64
Build-Date: Tue, 01 Sep 2020 18:24:01 +0000
Build-Date: Mon, 03 Jan 2022 23:24:02 +0000
Installed-Build-Depends:
autoconf (= 2.69-11),
automake (= 1:1.16.1-4),

View File

@ -109,6 +109,7 @@ message EnclaveFrontendConfig {
required uint32 pending_request_count = 5;
required uint32 pending_request_ttl = 6;
required uint32 max_backup_data_length = 7;
required uint32 ias_version = 8;
}
message SourcePartitionConfig {
@ -132,6 +133,7 @@ message EnclaveReplicaConfig {
required uint32 storage_page_cache_size = 10;
required uint32 raft_log_index_page_cache_size = 13;
required uint32 max_frontend_count = 14;
required uint32 ias_version = 15;
}
message StartReplicaGroupRequest {

View File

@ -144,6 +144,8 @@ pub struct EnclaveFrontendConfig {
pub pending_request_ttl: u32,
#[prost(uint32, required, tag = "7")]
pub max_backup_data_length: u32,
#[prost(uint32, required, tag = "8")]
pub ias_version: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct SourcePartitionConfig {
@ -181,6 +183,8 @@ pub struct EnclaveReplicaConfig {
pub raft_log_index_page_cache_size: u32,
#[prost(uint32, required, tag = "14")]
pub max_frontend_count: u32,
#[prost(uint32, required, tag = "15")]
pub ias_version: u32,
}
#[derive(Clone, PartialEq, ::prost::Message)]
pub struct StartReplicaGroupRequest {

View File

@ -56,6 +56,7 @@ pub struct NodeParams {
node_key: Rc<[u8]>,
node_id: NodeId,
node_type: NodeType,
ias_version: u32,
}
pub struct RemoteSender<M>
@ -462,7 +463,7 @@ where
match self.accept_connection(&connect_request.noise_data) {
Ok((noise, their_handshake_hash)) => match self.auth_type {
RemoteAuthorizationType::Mutual | RemoteAuthorizationType::RemoteOnly => {
match validate_ias_report(connect_request.ias_report.as_ref(), &their_handshake_hash.hash) {
match validate_ias_report(connect_request.ias_report.as_ref(), self.node_params.ias_version, &their_handshake_hash.hash) {
Ok(attestation) => {
*session = SessionState::Accepted {
noise,
@ -611,7 +612,7 @@ where
} => (noise, their_handshake_hash, final_handshake_hash),
_ => unreachable!(),
};
match validate_ias_report(Some(&ias_report), &their_handshake_hash.hash) {
match validate_ias_report(Some(&ias_report), self.node_params.ias_version, &their_handshake_hash.hash) {
Ok(attestation) => {
let handshake_hash = final_handshake_hash;
*session = SessionState::Authorized {
@ -634,7 +635,7 @@ where
attestation,
handshake_hash,
..
} => match validate_ias_report(Some(&ias_report), &handshake_hash.get_hash_for_node(&self.remote_node_id)) {
} => match validate_ias_report(Some(&ias_report), self.node_params.ias_version, &handshake_hash.get_hash_for_node(&self.remote_node_id)) {
Ok(new_attestation) => {
verbose!("validated attestation report for {}: {}", &self.remote_node_id, &new_attestation);
*attestation = Some(new_attestation);
@ -823,6 +824,7 @@ fn parse_ias_timestamp(timestamp: &str) -> Result<u64, AttestationVerificationEr
fn validate_ias_report(
maybe_ias_report: Option<&IasReport>,
ias_version: u32,
expected_report_data: &[u8],
) -> Result<AttestationParameters, AttestationVerificationError>
{
@ -845,12 +847,32 @@ fn validate_ias_report(
let body: IasReportBody = serde_json::from_slice(&ias_report.body[..]).map_err(AttestationVerificationError::InvalidJson)?;
if body.version != 3 {
if body.version != ias_version as u64 {
return Err(AttestationVerificationError::WrongVersion(body.version));
}
match body.isvEnclaveQuoteStatus.as_str() {
"OK" => {}
"SW_HARDENING_NEEDED" => {
// To quote from Intel's documentation:
//
// > An attestation response may report “SW_HARDENING_NEEDED” for attestation requests
// > originating from Intel® SGX-enabled platforms that have applied the microcode and
// > SGX platform software update and are properly configured but are affected by
// > INTEL-SA-00334. In this case a Remote Attestation Verifier should evaluate the
// > potential risk of an attack on these platforms and whether the attesting enclave
// > employs adequate software hardening to mitigate the risk.
//
// We have, indeed, applied software mitigations for INTEL-SA-00334, and can consider
// SW_HARDENING_NEEDED an acceptable status as long as the only named advisory is the
// one we've already mitigated.
//
// The check for INTEL-SA-00334 was introduced in IASv4, and should never appear under
// IASv3.
if ias_version < 4 || body.advisoryIDs != vec![String::from("INTEL-SA-00334")] {
return Err(AttestationVerificationError::AttestationError(body.isvEnclaveQuoteStatus));
}
}
#[cfg(feature = "insecure")]
"GROUP_OUT_OF_DATE" | "CONFIGURATION_NEEDED" => {}
"SIGRL_VERSION_MISMATCH" => {
@ -911,6 +933,8 @@ pub struct IasReportBody {
pub version: u64,
pub timestamp: String,
pub advisoryIDs: Vec<String>,
}
impl fmt::Display for AttestationVerificationError {
@ -963,7 +987,7 @@ impl Deref for NodeId {
//
impl NodeParams {
pub fn generate(node_type: NodeType) -> Self {
pub fn generate(node_type: NodeType, ias_version: u32) -> Self {
let params = NOISE_PARAMS.parse().unwrap_or_else(|_| unreachable!());
let builder = snow::Builder::with_resolver(params, Box::new(SnowResolver));
let keypair = builder.generate_keypair().unwrap_or_else(|_| unreachable!());
@ -972,6 +996,7 @@ impl NodeParams {
node_key: keypair.private.into(),
node_id: keypair.public.into(),
node_type,
ias_version,
}
}

View File

@ -66,9 +66,9 @@ enum QeInfoRequestState {
impl<T> PeerManager<T>
where T: Peer
{
pub fn new(node_type: NodeType) -> Self {
pub fn new(node_type: NodeType, ias_version: u32) -> Self {
Self {
node_params: Rc::new(NodeParams::generate(node_type)),
node_params: Rc::new(NodeParams::generate(node_type, ias_version)),
noise_buffers: Default::default(),
connecting_peers: Default::default(),
qe_info_req: QeInfoRequestState::None,

View File

@ -87,9 +87,11 @@ pub struct PendingClientRequest {
impl FrontendState {
pub fn init(request: StartFrontendRequest) -> Self {
let ias_version = request.config.ias_version;
let mut state = Self {
config: request.config,
replicas: PeerManager::new(NODE_TYPE),
replicas: PeerManager::new(NODE_TYPE, ias_version),
partitions: Default::default(),
key_ranges: Default::default(),
last_request_id: Default::default(),

View File

@ -80,8 +80,10 @@ enum PeerMessage {
impl ReplicaState {
pub fn init(request: StartReplicaRequest) -> Self {
let ias_version = request.config.ias_version;
let state = Self {
peers: PeerManager::new(NodeType::Replica),
peers: PeerManager::new(NodeType::Replica, ias_version),
config: request.config,
frontends: Lru::new(),
partition: None,

2
service/.gitignore vendored
View File

@ -5,3 +5,5 @@
/config/*.staging.pem
/config/*.testing.yml
/config/*.testing.pem
/config/frontend.staging*.yml
/config/frontend.production*.yml

2
service/Cargo.lock generated
View File

@ -648,7 +648,7 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e"
[[package]]
name = "kbupd"
version = "1.0.104"
version = "1.0.105"
dependencies = [
"base64",
"byteorder",

View File

@ -1,7 +1,7 @@
[package]
authors = ["Open Whisper Systems"]
name = "kbupd"
version = "1.0.104"
version = "1.0.105"
license = "AGPL-3.0-or-later"
description = "Key Backup Service Daemon"
edition = "2018"

View File

@ -13,11 +13,12 @@ use std::sync::Arc;
use std::thread;
use std::time::Duration;
use failure::{format_err, ResultExt};
use failure::{bail, format_err, ResultExt};
use futures::future;
use futures::prelude::*;
use hyper::Uri;
use hyper::client::connect::HttpConnector;
use ias_client::IasApiVersion;
use kbupd_config::metrics::*;
use kbupd_config::FrontendConfig;
use kbuptlsd::prelude::*;
@ -96,8 +97,15 @@ impl FrontendService {
hostname: TlsClientProxyHostnameArgument::Hostname(hostname)
})
.context("error creating intel attestation tls proxy client")?;
let ias_version = match config.attestation.iasVersion {
None | Some(3) => IasApiVersion::ApiVer3,
Some(4) => IasApiVersion::ApiVer4,
_ => bail!("unrecognized IAS version: {}", config.attestation.iasVersion.unwrap())
};
let new_intel_client =
new_ias_client(&config.attestation.host, &config.attestation.apiKey, intel_client_proxy).context("error creating intel attestation client")?;
new_ias_client(&config.attestation.host, ias_version, &config.attestation.apiKey, intel_client_proxy).context("error creating intel attestation client")?;
handshake_manager = Some(HandshakeManager::new(
enclave_manager_tx.clone(),
new_intel_client.clone(),
@ -137,6 +145,7 @@ impl FrontendService {
let enclave_spid = config.attestation.spid;
let enclave_executor = runtime.executor();
let enclave_directory = cmdline_config.enclave_directory.to_owned();
let ias_version = config.attestation.iasVersion.unwrap_or(3);
let enclave_thread = thread::spawn(move || -> Result<(), failure::Error> {
let mut enclaves = Vec::with_capacity(enclave_configs.len());
for (enclave_config, partitions) in enclave_configs {
@ -160,6 +169,7 @@ impl FrontendService {
pending_request_ttl: util::duration::as_ticks(pending_request_ttl, timer_tick_interval),
pending_request_count: enclave_config.pendingRequestCount,
max_backup_data_length: enclave_config.maxBackupDataLength,
ias_version,
};
let mut partition_configs = Vec::new();

View File

@ -13,11 +13,11 @@ use kbuptlsd::prelude::*;
pub type KbupdIasClient = IasClient<TlsProxyConnector<HttpConnector>>;
pub fn new_ias_client(host: &str, api_key: &str, tls_proxy: TlsClientProxySpawner) -> Result<KbupdIasClient, failure::Error> {
pub fn new_ias_client(host: &str, ias_version: IasApiVersion, api_key: &str, tls_proxy: TlsClientProxySpawner) -> Result<KbupdIasClient, failure::Error> {
let mut http_connector = HttpConnector::new(1);
http_connector.enforce_http(false);
let tls_connector = TlsProxyConnector::new(Arc::new(tls_proxy), http_connector);
IasClient::new(host, Some(IasApiVersion::ApiVer3), Some(api_key), tls_connector)
IasClient::new(host, Some(ias_version), Some(api_key), tls_connector)
}

View File

@ -109,6 +109,7 @@ message EnclaveFrontendConfig {
required uint32 pending_request_count = 5;
required uint32 pending_request_ttl = 6;
required uint32 max_backup_data_length = 7;
required uint32 ias_version = 8;
}
message SourcePartitionConfig {
@ -132,6 +133,7 @@ message EnclaveReplicaConfig {
required uint32 storage_page_cache_size = 10;
required uint32 raft_log_index_page_cache_size = 13;
required uint32 max_frontend_count = 14;
required uint32 ias_version = 15;
}
message StartReplicaGroupRequest {

View File

@ -11,10 +11,11 @@ use std::sync::Arc;
use std::thread;
use std::time::Duration;
use failure::ResultExt;
use failure::{bail, ResultExt};
use futures::prelude::*;
use hyper::Uri;
use hyper::client::connect::HttpConnector;
use ias_client::IasApiVersion;
use kbupd_config::metrics::*;
use kbupd_config::ReplicaConfig;
use kbuptlsd::prelude::*;
@ -82,6 +83,12 @@ impl ReplicaService {
.host()
.expect("attestation host does not contain a hostname"));
let ias_version = match config.attestation.iasVersion {
None | Some(3) => IasApiVersion::ApiVer3,
Some(4) => IasApiVersion::ApiVer4,
_ => bail!("unrecognized IAS version: {}", config.attestation.iasVersion.unwrap())
};
let intel_client_proxy =
TlsClientProxySpawner::new(cmdline_config.kbuptlsd_bin_path.to_owned(), TlsClientProxyArguments::NoConfig {
ca: TlsClientProxyCaArgument::System,
@ -89,7 +96,7 @@ impl ReplicaService {
hostname: TlsClientProxyHostnameArgument::Hostname(hostname)
})
.context("error creating intel attestation tls client proxy")?;
Some(new_ias_client(&config.attestation.host, &config.attestation.apiKey, intel_client_proxy).context("error creating intel attestation client")?)
Some(new_ias_client(&config.attestation.host, ias_version, &config.attestation.apiKey, intel_client_proxy).context("error creating intel attestation client")?)
} else {
None
};
@ -126,8 +133,8 @@ impl ReplicaService {
transfer_chunk_size: config.enclave.transferChunkSize,
storage_page_cache_size: Default::default(), // unused
max_frontend_count: config.enclave.maxFrontendCount,
raft_log_index_page_cache_size: 10,
ias_version: config.attestation.iasVersion.unwrap_or(3),
};
info!(

View File

@ -76,6 +76,8 @@ pub struct FrontendAttestationConfig {
pub disabled: bool,
pub apiKey: String,
pub iasVersion: Option<u32>,
}
#[derive(Deserialize)]

View File

@ -39,6 +39,8 @@ pub struct ReplicaAttestationConfig {
pub disabled: bool,
pub apiKey: String,
pub iasVersion: Option<u32>,
}
#[derive(Deserialize)]