use std::sync::Arc;
use std::io::Read;
pub use em_client::models;
use hyper::client::Pool;
use hyper::net::HttpsConnector;
use em_client::{Api, Client};
use mbedtls::alloc::{List as MbedtlsList};
use mbedtls::pk::Pk;
use mbedtls::ssl::Config;
use mbedtls::ssl::config::{Endpoint, Preset, Transport, AuthMode, Version};
use mbedtls::x509::{Certificate, Crl};
use mbedtls::hash::{Md, Type};
use sdkms::api_model::Blob;
use uuid::Uuid;
use url::Url;
use std::time::Duration;
use uuid_sdkms::{Uuid as SdkmsUuid};
use crate::mbedtls_hyper::MbedSSLClient;
pub fn convert_uuid(api_uuid: Uuid) -> SdkmsUuid {
SdkmsUuid::from_bytes(*api_uuid.as_bytes())
}
pub fn compute_sha256(input: &[u8]) -> Result<[u8; 32], String> {
let mut digest = [0; 32];
Md::hash(Type::Sha256, input, &mut digest)
.map_err(|e| format!("Error in calculating digest: {:?}", e))?;
Ok(digest)
}
pub fn get_runtime_configuration(
server: &str,
port: u16,
cert: Arc<MbedtlsList<Certificate>>,
key: Arc<Pk>,
ca_cert_list: Option<Arc<MbedtlsList<Certificate>>>,
ca_crl: Option<Arc<Crl>>,
expected_hash: &[u8; 32]
) -> Result<models::RuntimeAppConfig, String> {
let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
config.set_rng(Arc::new(mbedtls::rng::Rdrand));
config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
if let Some(ca_cert_list) = ca_cert_list {
config.set_ca_list(ca_cert_list, ca_crl);
config.set_authmode(AuthMode::Required);
} else {
config.set_authmode(AuthMode::Optional);
}
config.push_cert(cert, key).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
let tls_client = if (server.starts_with("ccm.") || server.starts_with("em.")) && server.ends_with("fortanix.com") {
MbedSSLClient::new_with_sni(Arc::new(config), true, Some(format!("nodes.{}", server)))
} else {
MbedSSLClient::new(Arc::new(config), true)
};
let connector = HttpsConnector::new(tls_client);
let mut client = Client::try_new_with_connector(&format!("https://{}:{}", server, port), None, connector)
.map_err(|e| format!("CCM client construction failed: {:?}", e))?;
client.set_use_new_paths(true);
let response = client.get_runtime_application_config(expected_hash).map_err(|e| format!("Failed requesting workflow config response: {:?}", e))?;
Ok(response)
}
pub fn get_sdkms_dataset(
sdkms_url: String,
dataset_id: String,
app_id: Uuid,
cert: Arc<MbedtlsList<Certificate>>,
key: Arc<Pk>,
ca_cert_list: Option<Arc<MbedtlsList<Certificate>>>,
ca_crl: Option<Arc<Crl>>
) -> Result<Blob, String> {
let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
config.set_rng(Arc::new(mbedtls::rng::Rdrand));
config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
if let Some(ca_cert_list) = ca_cert_list {
config.set_ca_list(ca_cert_list, ca_crl);
config.set_authmode(AuthMode::Required);
} else {
config.set_authmode(AuthMode::Optional);
}
config.push_cert(cert, key).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
let ssl = MbedSSLClient::new(Arc::new(config), true);
let connector = HttpsConnector::new(ssl);
let client = Arc::new(hyper::Client::with_connector(Pool::with_connector(Default::default(), connector)));
let client = sdkms::SdkmsClient::builder()
.with_api_endpoint(&sdkms_url)
.with_hyper_client(client)
.build().map_err(|e| format!("SDKMS Build failed: {:?}", e))?
.authenticate_with_cert(Some(&convert_uuid(app_id))).map_err(|e| format!("SDKMS authenticate failed: {:?}", e))?;
let key_id = sdkms::api_model::SobjectDescriptor::Name(dataset_id);
let result = client.export_sobject(&key_id).map_err(|e| format!("Failed SDKMS export operation: {:?}", e))?;
result.value.ok_or("Missing value in exported object".to_string())
}
pub fn https_get(url: Url,
ca_cert_list: Option<Arc<MbedtlsList<Certificate>>>,
ca_crl: Option<Arc<Crl>>
) -> Result<Vec<u8>, String> {
let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
config.set_rng(Arc::new(mbedtls::rng::Rdrand));
config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
if let Some(ca_cert_list) = ca_cert_list {
config.set_ca_list(ca_cert_list, ca_crl);
config.set_authmode(AuthMode::Required);
} else {
config.set_authmode(AuthMode::Optional);
}
let ssl = MbedSSLClient::new(Arc::new(config), true);
let connector = HttpsConnector::new(ssl);
let client = hyper::Client::with_connector(Pool::with_connector(Default::default(), connector));
let mut response = client.get(url).send().map_err(|e| format!("Failed downloading, error: {:?}", e))?;
if response.status != hyper::status::StatusCode::Ok {
return Err(format!("Request failed, result: {:?}", response));
}
let mut body = vec![];
response.read_to_end(&mut body).map_err(|e| format!("Failed reading body, error: {:?}", e))?;
Ok(body)
}
pub fn https_put(url: Url,
body: Vec<u8>,
ca_cert_list: Option<Arc<MbedtlsList<Certificate>>>,
ca_crl: Option<Arc<Crl>>
) -> Result<(), String> {
let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
config.set_rng(Arc::new(mbedtls::rng::Rdrand));
config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
if let Some(ca_cert_list) = ca_cert_list {
config.set_ca_list(ca_cert_list, ca_crl);
config.set_authmode(AuthMode::Required);
} else {
config.set_authmode(AuthMode::Optional);
}
let ssl = MbedSSLClient::new(Arc::new(config), true);
let connector = HttpsConnector::new(ssl);
let client = hyper::Client::with_connector(Pool::with_connector(Default::default(), connector));
let result = client.put(url).body(body.as_slice()).send().map_err(|e| format!("Failed upload, error: {:?}", e))?;
if result.status != hyper::status::StatusCode::Ok {
return Err(format!("Request failed, result: {:?}", result));
}
Ok(())
}
const CONNECTION_IDLE_TIMEOUT_SECS: u64 = 30;
pub fn get_hyper_connector_pool(ca_chain: Vec<Vec<u8>>) -> Result<Arc<hyper::Client>, String> {
get_mbedtls_hyper_connector_pool(ca_chain, None)
}
pub fn get_mbedtls_hyper_connector_pool(ca_chain: Vec<Vec<u8>>, client_pki: Option<(Arc<MbedtlsList<Certificate>>, Arc<Pk>)>) -> Result<Arc<hyper::Client>, String> {
let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
config.set_rng(Arc::new(mbedtls::rng::Rdrand));
config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
if !ca_chain.is_empty() {
let mut list = MbedtlsList::<Certificate>::new();
for i in ca_chain {
list.push(Certificate::from_der(&i).map_err(|e| format!("Failed parsing ca cert, error: {:?}", e))?);
}
config.set_ca_list(Arc::new(list), None);
config.set_authmode(AuthMode::Required);
} else {
config.set_authmode(AuthMode::Optional);
}
if let Some((cert, key)) = client_pki {
config.push_cert(cert, key).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
}
let ssl = MbedSSLClient::new(Arc::new(config), true);
let connector = HttpsConnector::new(ssl);
let mut pool = Pool::with_connector(Default::default(), connector);
pool.set_idle_timeout(Some(Duration::from_secs(CONNECTION_IDLE_TIMEOUT_SECS)));
Ok(Arc::new(hyper::Client::with_connector(pool)))
}