diff --git a/enclave/debian/buildinfo b/enclave/debian/buildinfo index 03dcaaa..9884a45 100644 --- a/enclave/debian/buildinfo +++ b/enclave/debian/buildinfo @@ -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), diff --git a/enclave/include/kbupd.proto b/enclave/include/kbupd.proto index 3cd3449..8eacadc 100644 --- a/enclave/include/kbupd.proto +++ b/enclave/include/kbupd.proto @@ -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 { diff --git a/enclave/kbupd_enclave/src/protobufs/kbupd.rs b/enclave/kbupd_enclave/src/protobufs/kbupd.rs index 34bdc19..aef7c5b 100644 --- a/enclave/kbupd_enclave/src/protobufs/kbupd.rs +++ b/enclave/kbupd_enclave/src/protobufs/kbupd.rs @@ -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 { diff --git a/enclave/kbupd_enclave/src/remote/mod.rs b/enclave/kbupd_enclave/src/remote/mod.rs index 65a231f..d4fcb23 100644 --- a/enclave/kbupd_enclave/src/remote/mod.rs +++ b/enclave/kbupd_enclave/src/remote/mod.rs @@ -56,6 +56,7 @@ pub struct NodeParams { node_key: Rc<[u8]>, node_id: NodeId, node_type: NodeType, + ias_version: u32, } pub struct RemoteSender @@ -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, + ias_version: u32, expected_report_data: &[u8], ) -> Result { @@ -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, } 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, } } diff --git a/enclave/kbupd_enclave/src/remote/peer_manager.rs b/enclave/kbupd_enclave/src/remote/peer_manager.rs index eb4bf18..d67aab2 100644 --- a/enclave/kbupd_enclave/src/remote/peer_manager.rs +++ b/enclave/kbupd_enclave/src/remote/peer_manager.rs @@ -66,9 +66,9 @@ enum QeInfoRequestState { impl PeerManager 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, diff --git a/enclave/kbupd_enclave/src/service/frontend.rs b/enclave/kbupd_enclave/src/service/frontend.rs index 822228d..3df265e 100644 --- a/enclave/kbupd_enclave/src/service/frontend.rs +++ b/enclave/kbupd_enclave/src/service/frontend.rs @@ -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(), diff --git a/enclave/kbupd_enclave/src/service/replica/mod.rs b/enclave/kbupd_enclave/src/service/replica/mod.rs index f0acf35..e1734bc 100644 --- a/enclave/kbupd_enclave/src/service/replica/mod.rs +++ b/enclave/kbupd_enclave/src/service/replica/mod.rs @@ -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, diff --git a/service/.gitignore b/service/.gitignore index d6999b6..be372f6 100644 --- a/service/.gitignore +++ b/service/.gitignore @@ -5,3 +5,5 @@ /config/*.staging.pem /config/*.testing.yml /config/*.testing.pem +/config/frontend.staging*.yml +/config/frontend.production*.yml diff --git a/service/Cargo.lock b/service/Cargo.lock index 5450745..d9d86d3 100644 --- a/service/Cargo.lock +++ b/service/Cargo.lock @@ -648,7 +648,7 @@ checksum = "b8b7a7c0c47db5545ed3fef7468ee7bb5b74691498139e4b3f6a20685dc6dd8e" [[package]] name = "kbupd" -version = "1.0.104" +version = "1.0.105" dependencies = [ "base64", "byteorder", diff --git a/service/kbupd/Cargo.toml b/service/kbupd/Cargo.toml index 603f9e2..3c97fea 100644 --- a/service/kbupd/Cargo.toml +++ b/service/kbupd/Cargo.toml @@ -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" diff --git a/service/kbupd/res/enclave/ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba.so b/service/kbupd/res/enclave/ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba.so new file mode 100644 index 0000000..6631dcd Binary files /dev/null and b/service/kbupd/res/enclave/ee19f1965b1eefa3dc4204eb70c04f397755f771b8c1909d080c04dad2a6a9ba.so differ diff --git a/service/kbupd/src/frontend/mod.rs b/service/kbupd/src/frontend/mod.rs index aaf64a1..8b2db80 100644 --- a/service/kbupd/src/frontend/mod.rs +++ b/service/kbupd/src/frontend/mod.rs @@ -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(); diff --git a/service/kbupd/src/intel_client.rs b/service/kbupd/src/intel_client.rs index 314b12d..27838e8 100644 --- a/service/kbupd/src/intel_client.rs +++ b/service/kbupd/src/intel_client.rs @@ -13,11 +13,11 @@ use kbuptlsd::prelude::*; pub type KbupdIasClient = IasClient>; -pub fn new_ias_client(host: &str, api_key: &str, tls_proxy: TlsClientProxySpawner) -> Result { +pub fn new_ias_client(host: &str, ias_version: IasApiVersion, api_key: &str, tls_proxy: TlsClientProxySpawner) -> Result { 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) } diff --git a/service/kbupd/src/kbupd.proto b/service/kbupd/src/kbupd.proto index 1c7cf53..6fa06a9 100644 --- a/service/kbupd/src/kbupd.proto +++ b/service/kbupd/src/kbupd.proto @@ -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 { diff --git a/service/kbupd/src/replica/mod.rs b/service/kbupd/src/replica/mod.rs index 80f9ec2..a28a6e4 100644 --- a/service/kbupd/src/replica/mod.rs +++ b/service/kbupd/src/replica/mod.rs @@ -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!( diff --git a/service/kbupd_config/src/frontend/config.rs b/service/kbupd_config/src/frontend/config.rs index 01834ac..9dd324b 100644 --- a/service/kbupd_config/src/frontend/config.rs +++ b/service/kbupd_config/src/frontend/config.rs @@ -76,6 +76,8 @@ pub struct FrontendAttestationConfig { pub disabled: bool, pub apiKey: String, + + pub iasVersion: Option, } #[derive(Deserialize)] diff --git a/service/kbupd_config/src/replica/config.rs b/service/kbupd_config/src/replica/config.rs index fa35e4f..91a4e16 100644 --- a/service/kbupd_config/src/replica/config.rs +++ b/service/kbupd_config/src/replica/config.rs @@ -39,6 +39,8 @@ pub struct ReplicaAttestationConfig { pub disabled: bool, pub apiKey: String, + + pub iasVersion: Option, } #[derive(Deserialize)]