em_app/
utils.rs

1/* Copyright (c) Fortanix, Inc.
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6use std::sync::Arc;
7use std::io::Read;
8
9pub use em_client::models;
10
11use hyper::client::Pool;
12use hyper::net::HttpsConnector;
13use em_client::{Api, Client};
14use mbedtls::alloc::{List as MbedtlsList};
15use mbedtls::pk::Pk;
16use mbedtls::ssl::Config;
17use mbedtls::ssl::config::{Endpoint, Preset, Transport, AuthMode, Version};
18use mbedtls::x509::{Certificate, Crl};
19use mbedtls::hash::{Md, Type};
20use sdkms::api_model::Blob;
21use uuid::Uuid;
22use url::Url;
23use std::time::Duration;
24use uuid_sdkms::{Uuid as SdkmsUuid};
25
26use crate::mbedtls_hyper::MbedSSLClient;
27
28pub fn convert_uuid(api_uuid: Uuid) -> SdkmsUuid {
29    SdkmsUuid::from_bytes(*api_uuid.as_bytes())
30}
31
32/// Computes a Sha256 hash of an input
33pub fn compute_sha256(input: &[u8]) -> Result<[u8; 32], String> {
34    let mut digest = [0; 32];
35    Md::hash(Type::Sha256, input, &mut digest)
36        .map_err(|e| format!("Error in calculating digest: {:?}", e))?;
37
38    Ok(digest)
39}
40
41pub fn get_runtime_configuration(
42    server: &str,
43    port: u16,
44    cert: Arc<MbedtlsList<Certificate>>,
45    key: Arc<Pk>,
46    ca_cert_list: Option<Arc<MbedtlsList<Certificate>>>,
47    ca_crl: Option<Arc<Crl>>,
48    expected_hash: &[u8; 32]
49) -> Result<models::RuntimeAppConfig, String> {
50
51    let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
52
53    config.set_rng(Arc::new(mbedtls::rng::Rdrand));
54    config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
55
56    if let Some(ca_cert_list) = ca_cert_list {
57        config.set_ca_list(ca_cert_list, ca_crl);
58        config.set_authmode(AuthMode::Required);
59    } else {
60        config.set_authmode(AuthMode::Optional);
61    }
62    
63    config.push_cert(cert, key).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
64    
65    let ssl = MbedSSLClient::new_with_sni(Arc::new(config), true, Some(format!("nodes.{}", server)));
66    let connector = HttpsConnector::new(ssl);
67    let client = Client::try_new_with_connector(&format!("https://{}:{}/v1/runtime/app_configs", server, port), None, connector).map_err(|e| format!("EM SaaS request failed: {:?}", e))?;
68    let response = client.get_runtime_application_config(expected_hash).map_err(|e| format!("Failed requesting workflow config response: {:?}", e))?;
69
70    Ok(response)
71}
72
73pub fn get_sdkms_dataset(
74    sdkms_url: String,
75    dataset_id: String,
76    app_id: Uuid,
77    cert: Arc<MbedtlsList<Certificate>>,
78    key: Arc<Pk>,
79    ca_cert_list: Option<Arc<MbedtlsList<Certificate>>>,
80    ca_crl: Option<Arc<Crl>>
81) -> Result<Blob, String> {
82
83    let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
84    
85    config.set_rng(Arc::new(mbedtls::rng::Rdrand));
86    config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
87
88    if let Some(ca_cert_list) = ca_cert_list {
89        config.set_ca_list(ca_cert_list, ca_crl);
90        config.set_authmode(AuthMode::Required);
91    } else {
92        config.set_authmode(AuthMode::Optional);
93    }
94    
95    config.push_cert(cert, key).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
96    
97    let ssl = MbedSSLClient::new(Arc::new(config), true);
98    let connector = HttpsConnector::new(ssl);
99    let client = Arc::new(hyper::Client::with_connector(Pool::with_connector(Default::default(), connector)));
100
101    let client = sdkms::SdkmsClient::builder()
102        .with_api_endpoint(&sdkms_url)
103        .with_hyper_client(client)
104        .build().map_err(|e| format!("SDKMS Build failed: {:?}", e))?
105        .authenticate_with_cert(Some(&convert_uuid(app_id))).map_err(|e| format!("SDKMS authenticate failed: {:?}", e))?;
106
107    let key_id = sdkms::api_model::SobjectDescriptor::Name(dataset_id);
108    
109    let result = client.export_sobject(&key_id).map_err(|e| format!("Failed SDKMS export operation: {:?}", e))?;
110    
111    result.value.ok_or("Missing value in exported object".to_string())
112}
113
114pub fn https_get(url: Url,
115                 ca_cert_list: Option<Arc<MbedtlsList<Certificate>>>,
116                 ca_crl: Option<Arc<Crl>>
117) -> Result<Vec<u8>, String> {
118    let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
119    
120    config.set_rng(Arc::new(mbedtls::rng::Rdrand));
121    config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
122
123    if let Some(ca_cert_list) = ca_cert_list {
124        config.set_ca_list(ca_cert_list, ca_crl);
125        config.set_authmode(AuthMode::Required);
126    } else {
127        config.set_authmode(AuthMode::Optional);
128    }
129
130    let ssl = MbedSSLClient::new(Arc::new(config), true);
131    let connector = HttpsConnector::new(ssl);
132    let client = hyper::Client::with_connector(Pool::with_connector(Default::default(), connector));
133    let mut response = client.get(url).send().map_err(|e| format!("Failed downloading, error: {:?}", e))?;
134
135    if response.status != hyper::status::StatusCode::Ok {
136        return Err(format!("Request failed, result: {:?}", response));
137    }
138    
139    let mut body = vec![];
140    response.read_to_end(&mut body).map_err(|e| format!("Failed reading body, error: {:?}", e))?;
141
142    Ok(body)
143}
144
145pub fn https_put(url: Url,
146                 body: Vec<u8>,
147                 ca_cert_list: Option<Arc<MbedtlsList<Certificate>>>,
148                 ca_crl: Option<Arc<Crl>>
149) -> Result<(), String> {
150    let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
151    
152    config.set_rng(Arc::new(mbedtls::rng::Rdrand));
153    config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
154
155    if let Some(ca_cert_list) = ca_cert_list {
156        config.set_ca_list(ca_cert_list, ca_crl);
157        config.set_authmode(AuthMode::Required);
158    } else {
159        config.set_authmode(AuthMode::Optional);
160    }
161
162    let ssl = MbedSSLClient::new(Arc::new(config), true);
163    let connector = HttpsConnector::new(ssl);
164    let client = hyper::Client::with_connector(Pool::with_connector(Default::default(), connector));
165    let result = client.put(url).body(body.as_slice()).send().map_err(|e| format!("Failed upload, error: {:?}", e))?;
166
167    if result.status != hyper::status::StatusCode::Ok {
168        return Err(format!("Request failed, result: {:?}", result));
169    }
170    
171    Ok(())
172}
173
174const CONNECTION_IDLE_TIMEOUT_SECS: u64 = 30;
175
176pub fn get_hyper_connector_pool(ca_chain: Vec<Vec<u8>>) -> Result<Arc<hyper::Client>, String> {
177    get_mbedtls_hyper_connector_pool(ca_chain, None)
178}
179
180pub fn get_mbedtls_hyper_connector_pool(ca_chain: Vec<Vec<u8>>, client_pki: Option<(Arc<MbedtlsList<Certificate>>, Arc<Pk>)>) -> Result<Arc<hyper::Client>, String> {
181    let mut config = Config::new(Endpoint::Client, Transport::Stream, Preset::Default);
182
183    config.set_rng(Arc::new(mbedtls::rng::Rdrand));
184    config.set_min_version(Version::Tls1_2).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
185
186    if !ca_chain.is_empty() {
187        let mut list = MbedtlsList::<Certificate>::new();
188        for i in ca_chain {
189            list.push(Certificate::from_der(&i).map_err(|e| format!("Failed parsing ca cert, error: {:?}", e))?);
190        }
191
192        config.set_ca_list(Arc::new(list), None);
193        config.set_authmode(AuthMode::Required);
194    } else {
195        config.set_authmode(AuthMode::Optional);
196    }
197
198    if let Some((cert, key)) = client_pki {
199        config.push_cert(cert, key).map_err(|e| format!("TLS configuration failed: {:?}", e))?;
200    }
201    
202    let ssl = MbedSSLClient::new(Arc::new(config), true);
203    let connector = HttpsConnector::new(ssl);
204
205    let mut pool = Pool::with_connector(Default::default(), connector);
206    pool.set_idle_timeout(Some(Duration::from_secs(CONNECTION_IDLE_TIMEOUT_SECS)));
207
208    Ok(Arc::new(hyper::Client::with_connector(pool)))
209}