samplecode/pcl/pcl-seal/enclave/src/lib.rs (515 lines of code) (raw):

// Licensed to the Apache Software Foundation (ASF) under one // or more contributor license agreements. See the NOTICE file // distributed with this work for additional information // regarding copyright ownership. The ASF licenses this file // to you under the Apache License, Version 2.0 (the // "License"); you may not use this file except in compliance // with the License. You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License.. #![crate_name = "pcl_seal"] #![crate_type = "staticlib"] #![cfg_attr(not(target_env = "sgx"), no_std)] #![cfg_attr(target_env = "sgx", feature(rustc_private))] #![warn(unused_extern_crates)] extern crate sgx_types; extern crate sgx_tcrypto; extern crate sgx_tse; extern crate sgx_tseal; #[cfg(not(target_env = "sgx"))] #[macro_use] extern crate sgx_tstd as std; extern crate sgx_rand; extern crate rustls; extern crate webpki; extern crate webpki_roots; extern crate itertools; extern crate base64; extern crate httparse; extern crate yasna; extern crate bit_vec; extern crate num_bigint; extern crate chrono; extern crate ue_send_recv; use sgx_types::*; use sgx_tse::*; use sgx_tcrypto::*; use sgx_rand::*; use std::prelude::v1::*; use std::sync::Arc; use std::net::TcpStream; use std::string::String; use std::ptr; use std::str; use std::sgxfs::SgxFile; use std::io::{self, Write, Read, BufReader}; use std::untrusted::fs; use std::vec::Vec; use itertools::Itertools; use ue_send_recv::{tls_receive_vec, tls_send_vec}; mod cert; mod hex; pub const DEV_HOSTNAME:&'static str = "api.trustedservices.intel.com"; pub const SIGRL_SUFFIX:&'static str = "/sgx/dev/attestation/v3/sigrl/"; pub const REPORT_SUFFIX:&'static str = "/sgx/dev/attestation/v3/report"; pub const CERTEXPIRYDAYS: i64 = 90i64; pub const KEYFILE:&'static str = "prov_key.bin"; #[allow(dead_code)] const PCL_SEALED_KEY_SIZE: usize = 560 + SGX_AESGCM_KEY_SIZE + SGX_PCL_GUID_SIZE; // sizeof sgx_sealed_data_t = 560 extern "C" { pub fn ocall_sgx_init_quote ( ret_val : *mut sgx_status_t, ret_ti : *mut sgx_target_info_t, ret_gid : *mut sgx_epid_group_id_t) -> sgx_status_t; pub fn ocall_get_ias_socket ( ret_val : *mut sgx_status_t, ret_fd : *mut i32) -> sgx_status_t; pub fn ocall_get_quote (ret_val : *mut sgx_status_t, p_sigrl : *const u8, sigrl_len : u32, p_report : *const sgx_report_t, quote_type : sgx_quote_sign_type_t, p_spid : *const sgx_spid_t, p_nonce : *const sgx_quote_nonce_t, p_qe_report : *mut sgx_report_t, p_quote : *mut u8, maxlen : u32, p_quote_len : *mut u32) -> sgx_status_t; } fn parse_response_attn_report(resp : &[u8]) -> (String, String, String){ println!("parse_response_attn_report"); let mut headers = [httparse::EMPTY_HEADER; 16]; let mut respp = httparse::Response::new(&mut headers); let result = respp.parse(resp); println!("parse result {:?}", result); let msg : &'static str; match respp.code { Some(200) => msg = "OK Operation Successful", Some(401) => msg = "Unauthorized Failed to authenticate or authorize request.", Some(404) => msg = "Not Found GID does not refer to a valid EPID group ID.", Some(500) => msg = "Internal error occurred", Some(503) => msg = "Service is currently not able to process the request (due to a temporary overloading or maintenance). This is a temporary state – the same request can be repeated after some time. ", _ => {println!("DBG:{}", respp.code.unwrap()); msg = "Unknown error occured"}, } println!("{}", msg); let mut len_num : u32 = 0; let mut sig = String::new(); let mut cert = String::new(); let mut attn_report = String::new(); for i in 0..respp.headers.len() { let h = respp.headers[i]; //println!("{} : {}", h.name, str::from_utf8(h.value).unwrap()); match h.name{ "content-length" => { let len_str = String::from_utf8(h.value.to_vec()).unwrap(); len_num = len_str.parse::<u32>().unwrap(); println!("content length = {}", len_num); } "x-iasreport-signature" => sig = str::from_utf8(h.value).unwrap().to_string(), "x-iasreport-signing-certificate" => cert = str::from_utf8(h.value).unwrap().to_string(), _ => (), } } // Remove %0A from cert, and only obtain the signing cert cert = cert.replace("%0A", ""); cert = cert::percent_decode(cert); let v: Vec<&str> = cert.split("-----").collect(); let sig_cert = v[2].to_string(); if len_num != 0 { let header_len = result.unwrap().unwrap(); let resp_body = &resp[header_len..]; attn_report = str::from_utf8(resp_body).unwrap().to_string(); println!("Attestation report: {}", attn_report); } // len_num == 0 (attn_report, sig, sig_cert) } fn parse_response_sigrl(resp : &[u8]) -> Vec<u8> { println!("parse_response_sigrl"); let mut headers = [httparse::EMPTY_HEADER; 16]; let mut respp = httparse::Response::new(&mut headers); let result = respp.parse(resp); println!("parse result {:?}", result); println!("parse response{:?}", respp); let msg : &'static str; match respp.code { Some(200) => msg = "OK Operation Successful", Some(401) => msg = "Unauthorized Failed to authenticate or authorize request.", Some(404) => msg = "Not Found GID does not refer to a valid EPID group ID.", Some(500) => msg = "Internal error occurred", Some(503) => msg = "Service is currently not able to process the request (due to a temporary overloading or maintenance). This is a temporary state – the same request can be repeated after some time. ", _ => msg = "Unknown error occured", } println!("{}", msg); let mut len_num : u32 = 0; for i in 0..respp.headers.len() { let h = respp.headers[i]; if h.name == "content-length" { let len_str = String::from_utf8(h.value.to_vec()).unwrap(); len_num = len_str.parse::<u32>().unwrap(); println!("content length = {}", len_num); } } if len_num != 0 { let header_len = result.unwrap().unwrap(); let resp_body = &resp[header_len..]; println!("Base64-encoded SigRL: {:?}", resp_body); return base64::decode(str::from_utf8(resp_body).unwrap()).unwrap(); } // len_num == 0 Vec::new() } pub fn make_ias_client_config() -> rustls::ClientConfig { let mut config = rustls::ClientConfig::new(); config.root_store.add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS); let certs = load_certs("client.crt"); let privkey = load_private_key("client.key"); config.set_single_client_cert(certs, privkey).unwrap(); config } pub fn get_sigrl_from_intel(fd : c_int, gid : u32) -> Vec<u8> { println!("get_sigrl_from_intel fd = {:?}", fd); let config = make_ias_client_config(); let req = format!("GET {}{:08x} HTTP/1.1\r\nHOST: {}\r\n\r\n", SIGRL_SUFFIX, gid, SIGRL_SUFFIX); println!("{}", req); let dns_name = webpki::DNSNameRef::try_from_ascii_str(DEV_HOSTNAME).unwrap(); let mut sess = rustls::ClientSession::new(&Arc::new(config), dns_name); let mut sock = TcpStream::new(fd).unwrap(); let mut tls = rustls::Stream::new(&mut sess, &mut sock); let _result = tls.write(req.as_bytes()); let mut plaintext = Vec::new(); println!("write complete"); match tls.read_to_end(&mut plaintext) { Ok(_) => (), Err(e) => { println!("get_sigrl_from_intel tls.read_to_end: {:?}", e); panic!("haha"); } } println!("read_to_end complete"); let resp_string = String::from_utf8(plaintext.clone()).unwrap(); println!("{}", resp_string); parse_response_sigrl(&plaintext) } // TODO: support pse pub fn get_report_from_intel(fd : c_int, quote : Vec<u8>) -> (String, String, String) { println!("get_report_from_intel fd = {:?}", fd); let config = make_ias_client_config(); let encoded_quote = base64::encode(&quote[..]); let encoded_json = format!("{{\"isvEnclaveQuote\":\"{}\"}}\r\n", encoded_quote); let req = format!("POST {} HTTP/1.1\r\nHOST: {}\r\nContent-Length:{}\r\nContent-Type: application/json\r\n\r\n{}", REPORT_SUFFIX, DEV_HOSTNAME, encoded_json.len(), encoded_json); println!("{}", req); let dns_name = webpki::DNSNameRef::try_from_ascii_str(DEV_HOSTNAME).unwrap(); let mut sess = rustls::ClientSession::new(&Arc::new(config), dns_name); let mut sock = TcpStream::new(fd).unwrap(); let mut tls = rustls::Stream::new(&mut sess, &mut sock); let _result = tls.write(req.as_bytes()); let mut plaintext = Vec::new(); println!("write complete"); tls.read_to_end(&mut plaintext).unwrap(); println!("read_to_end complete"); let resp_string = String::from_utf8(plaintext.clone()).unwrap(); println!("resp_string = {}", resp_string); let (attn_report, sig, cert) = parse_response_attn_report(&plaintext); (attn_report, sig, cert) } fn as_u32_le(array: &[u8; 4]) -> u32 { ((array[0] as u32) << 0) + ((array[1] as u32) << 8) + ((array[2] as u32) << 16) + ((array[3] as u32) << 24) } #[allow(const_err)] pub fn create_attestation_report(pub_k: &sgx_ec256_public_t, sign_type: sgx_quote_sign_type_t) -> Result<(String, String, String), sgx_status_t> { // Workflow: // (1) ocall to get the target_info structure (ti) and epid group id (eg) // (1.5) get sigrl // (2) call sgx_create_report with ti+data, produce an sgx_report_t // (3) ocall to sgx_get_quote to generate (*mut sgx-quote_t, uint32_t) // (1) get ti + eg let mut ti : sgx_target_info_t = sgx_target_info_t::default(); let mut eg : sgx_epid_group_id_t = sgx_epid_group_id_t::default(); let mut rt : sgx_status_t = sgx_status_t::SGX_ERROR_UNEXPECTED; let res = unsafe { ocall_sgx_init_quote(&mut rt as *mut sgx_status_t, &mut ti as *mut sgx_target_info_t, &mut eg as *mut sgx_epid_group_id_t) }; println!("eg = {:?}", eg); if res != sgx_status_t::SGX_SUCCESS { return Err(res); } if rt != sgx_status_t::SGX_SUCCESS { return Err(rt); } let eg_num = as_u32_le(&eg); // (1.5) get sigrl let mut ias_sock : i32 = 0; let res = unsafe { ocall_get_ias_socket(&mut rt as *mut sgx_status_t, &mut ias_sock as *mut i32) }; if res != sgx_status_t::SGX_SUCCESS { return Err(res); } if rt != sgx_status_t::SGX_SUCCESS { return Err(rt); } //println!("Got ias_sock = {}", ias_sock); // Now sigrl_vec is the revocation list, a vec<u8> let sigrl_vec : Vec<u8> = get_sigrl_from_intel(ias_sock, eg_num); // (2) Generate the report // Fill ecc256 public key into report_data let mut report_data: sgx_report_data_t = sgx_report_data_t::default(); let mut pub_k_gx = pub_k.gx.clone(); pub_k_gx.reverse(); let mut pub_k_gy = pub_k.gy.clone(); pub_k_gy.reverse(); report_data.d[..32].clone_from_slice(&pub_k_gx); report_data.d[32..].clone_from_slice(&pub_k_gy); let rep = match rsgx_create_report(&ti, &report_data) { Ok(r) =>{ println!("Report creation => success {:?}", r.body.mr_signer.m); Some(r) }, Err(e) =>{ println!("Report creation => failed {:?}", e); None }, }; let mut quote_nonce = sgx_quote_nonce_t { rand : [0;16] }; let mut os_rng = os::SgxRng::new().unwrap(); os_rng.fill_bytes(&mut quote_nonce.rand); println!("rand finished"); let mut qe_report = sgx_report_t::default(); const RET_QUOTE_BUF_LEN : u32 = 2048; let mut return_quote_buf : [u8; RET_QUOTE_BUF_LEN as usize] = [0;RET_QUOTE_BUF_LEN as usize]; let mut quote_len : u32 = 0; // (3) Generate the quote // Args: // 1. sigrl: ptr + len // 2. report: ptr 432bytes // 3. linkable: u32, unlinkable=0, linkable=1 // 4. spid: sgx_spid_t ptr 16bytes // 5. sgx_quote_nonce_t ptr 16bytes // 6. p_sig_rl + sigrl size ( same to sigrl) // 7. [out]p_qe_report need further check // 8. [out]p_quote // 9. quote_size let (p_sigrl, sigrl_len) = if sigrl_vec.len() == 0 { (ptr::null(), 0) } else { (sigrl_vec.as_ptr(), sigrl_vec.len() as u32) }; let p_report = (&rep.unwrap()) as * const sgx_report_t; let quote_type = sign_type; let spid : sgx_spid_t = load_spid("spid.txt"); let p_spid = &spid as *const sgx_spid_t; let p_nonce = &quote_nonce as * const sgx_quote_nonce_t; let p_qe_report = &mut qe_report as *mut sgx_report_t; let p_quote = return_quote_buf.as_mut_ptr(); let maxlen = RET_QUOTE_BUF_LEN; let p_quote_len = &mut quote_len as *mut u32; let result = unsafe { ocall_get_quote(&mut rt as *mut sgx_status_t, p_sigrl, sigrl_len, p_report, quote_type, p_spid, p_nonce, p_qe_report, p_quote, maxlen, p_quote_len) }; if result != sgx_status_t::SGX_SUCCESS { return Err(result); } if rt != sgx_status_t::SGX_SUCCESS { println!("ocall_get_quote returned {}", rt); return Err(rt); } // Added 09-28-2018 // Perform a check on qe_report to verify if the qe_report is valid match rsgx_verify_report(&qe_report) { Ok(()) => println!("rsgx_verify_report passed!"), Err(x) => { println!("rsgx_verify_report failed with {:?}", x); return Err(x); }, } // Check if the qe_report is produced on the same platform if ti.mr_enclave.m != qe_report.body.mr_enclave.m || ti.attributes.flags != qe_report.body.attributes.flags || ti.attributes.xfrm != qe_report.body.attributes.xfrm { println!("qe_report does not match current target_info!"); return Err(sgx_status_t::SGX_ERROR_UNEXPECTED); } println!("qe_report check passed"); // Debug // for i in 0..quote_len { // print!("{:02X}", unsafe {*p_quote.offset(i as isize)}); // } // println!(""); // Check qe_report to defend against replay attack // The purpose of p_qe_report is for the ISV enclave to confirm the QUOTE // it received is not modified by the untrusted SW stack, and not a replay. // The implementation in QE is to generate a REPORT targeting the ISV // enclave (target info from p_report) , with the lower 32Bytes in // report.data = SHA256(p_nonce||p_quote). The ISV enclave can verify the // p_qe_report and report.data to confirm the QUOTE has not be modified and // is not a replay. It is optional. let mut rhs_vec : Vec<u8> = quote_nonce.rand.to_vec(); rhs_vec.extend(&return_quote_buf[..quote_len as usize]); let rhs_hash = rsgx_sha256_slice(&rhs_vec[..]).unwrap(); let lhs_hash = &qe_report.body.report_data.d[..32]; println!("rhs hash = {:02X}", rhs_hash.iter().format("")); println!("report hs= {:02X}", lhs_hash.iter().format("")); if rhs_hash != lhs_hash { println!("Quote is tampered!"); return Err(sgx_status_t::SGX_ERROR_UNEXPECTED); } let quote_vec : Vec<u8> = return_quote_buf[..quote_len as usize].to_vec(); let res = unsafe { ocall_get_ias_socket(&mut rt as *mut sgx_status_t, &mut ias_sock as *mut i32) }; if res != sgx_status_t::SGX_SUCCESS { return Err(res); } if rt != sgx_status_t::SGX_SUCCESS { return Err(rt); } let (attn_report, sig, cert) = get_report_from_intel(ias_sock, quote_vec); Ok((attn_report, sig, cert)) } fn load_spid(filename: &str) -> sgx_spid_t { let mut spidfile = fs::File::open(filename).expect("cannot open spid file"); let mut contents = String::new(); spidfile.read_to_string(&mut contents).expect("cannot read the spid file"); hex::decode_spid(&contents) } fn load_certs(filename: &str) -> Vec<rustls::Certificate> { let certfile = fs::File::open(filename).expect("cannot open certificate file"); let mut reader = BufReader::new(certfile); match rustls::internal::pemfile::certs(&mut reader) { Ok(r) => return r, Err(e) => { println!("Err in load_certs: {:?}", e); panic!(""); } } } fn load_private_key(filename: &str) -> rustls::PrivateKey { let rsa_keys = { let keyfile = fs::File::open(filename) .expect("cannot open private key file"); let mut reader = BufReader::new(keyfile); rustls::internal::pemfile::rsa_private_keys(&mut reader) .expect("file contains invalid rsa private key") }; let pkcs8_keys = { let keyfile = fs::File::open(filename) .expect("cannot open private key file"); let mut reader = BufReader::new(keyfile); rustls::internal::pemfile::pkcs8_private_keys(&mut reader) .expect("file contains invalid pkcs8 private key (encrypted keys not supported)") }; if !pkcs8_keys.is_empty() { pkcs8_keys[0].clone() } else { assert!(!rsa_keys.is_empty()); rsa_keys[0].clone() } } #[no_mangle] pub extern "C" fn key_provision(socket_fd : c_int, sign_type: sgx_quote_sign_type_t) -> sgx_status_t { // Generate Keypair let ecc_handle = SgxEccHandle::new(); let _result = ecc_handle.open(); let (prv_k, pub_k) = ecc_handle.create_key_pair().unwrap(); let (attn_report, sig, cert) = match create_attestation_report(&pub_k, sign_type) { Ok(r) => r, Err(e) => { println!("Error in create_attestation_report: {:?}", e); return e; } }; let payload = attn_report + "|" + &sig + "|" + &cert; let (key_der, cert_der) = match cert::gen_ecc_cert(payload, &prv_k, &pub_k, &ecc_handle) { Ok(r) => r, Err(e) => { println!("Error in gen_ecc_cert: {:?}", e); return e; } }; let _result = ecc_handle.close(); let root_ca_bin = include_bytes!("../../../cert/ca.crt"); let mut ca_reader = BufReader::new(&root_ca_bin[..]); let mut rc_store = rustls::RootCertStore::empty(); // Build a root ca storage rc_store.add_pem_file(&mut ca_reader).unwrap(); // Build a default authenticator which allow every authenticated client let authenticator = rustls::AllowAnyAuthenticatedClient::new(rc_store); let mut cfg = rustls::ServerConfig::new(authenticator); let mut certs = Vec::new(); certs.push(rustls::Certificate(cert_der)); let privkey = rustls::PrivateKey(key_der); cfg.set_single_cert_with_ocsp_and_sct(certs, privkey, vec![], vec![]).unwrap(); let mut sess = rustls::ServerSession::new(&Arc::new(cfg)); let mut conn = TcpStream::new(socket_fd).unwrap(); let mut tls = rustls::Stream::new(&mut sess, &mut conn); let key_bin : Vec<u8>; match tls_receive_vec(&mut tls) { Ok(v) => { println!("Server received: {:?}", v); if v.len() != SGX_AESGCM_KEY_SIZE { println!("Keylen {} != {}", v.len(), SGX_AESGCM_KEY_SIZE); return sgx_status_t::SGX_ERROR_UNEXPECTED; } key_bin = v; }, Err(ref err) if err.kind() == io::ErrorKind::ConnectionAborted => { println!("EOF (tls)"); return sgx_status_t::SGX_ERROR_UNEXPECTED; }, Err(e) => { println!("Error in read_to_end: {:?}", e); return sgx_status_t::SGX_ERROR_UNEXPECTED; }, } tls_send_vec(&mut tls, b"RECV complete".to_vec()).unwrap(); println!("Provision complete"); println!("Incoming key_bin = {:02X}", key_bin.iter().format("")); match SgxFile::create(KEYFILE) { Ok(mut f) => { match f.write_all(&key_bin) { Ok(()) => { println!("SgxFile write key file success!"); sgx_status_t::SGX_SUCCESS }, Err(x) => { println!("SgxFile write key file failed! {}", x); sgx_status_t::SGX_ERROR_UNEXPECTED } } }, Err(x) => { println!("SgxFile create file {} error {}", KEYFILE, x); sgx_status_t::SGX_ERROR_UNEXPECTED }, } } #[no_mangle] pub extern "C" fn get_sealed_pcl_key_len() -> u32 { sgx_tseal::SgxSealedData::<[u8;SGX_AESGCM_KEY_SIZE]>::calc_raw_sealed_data_size(SGX_PCL_GUID_SIZE as u32, SGX_AESGCM_KEY_SIZE as u32) } #[no_mangle] pub extern "C" fn get_sealed_pcl_key(key_buf : *mut u8, key_len: u32) -> sgx_status_t { println!("Entering get_sealed_pcl_key"); let mut key_array: [u8;SGX_AESGCM_KEY_SIZE] = [0;SGX_AESGCM_KEY_SIZE]; match SgxFile::open(KEYFILE) { Ok(mut f) => { let mut keyvec : Vec<u8> = Vec::new(); match f.read_to_end(&mut keyvec) { Ok(SGX_AESGCM_KEY_SIZE) => { println!("SgxFs read success: {:02X}", keyvec.iter().format("")); for i in 0..SGX_AESGCM_KEY_SIZE { key_array[i] = keyvec[i]; } }, Ok(len) => { println!("SgxFs read len {} incorrect", len); return sgx_status_t::SGX_ERROR_UNEXPECTED; }, Err(x) => { println!("Read keyfile failed {}", x); return sgx_status_t::SGX_ERROR_UNEXPECTED; } } }, Err(x) => { println!("get_sealed_pcl_key cannot open keyfile, please check if key is provisioned successfully! {}", x); return sgx_status_t::SGX_ERROR_UNEXPECTED; } }; let sealed_blob = match sgx_tseal::SgxSealedData::<[u8;SGX_AESGCM_KEY_SIZE]>::seal_data(&SGX_PCL_GUID, &key_array) { Ok(b) => b, Err(x) => { println!("sgx seal data failed {}!", x); return x; } }; match unsafe { sealed_blob.to_raw_sealed_data_t(key_buf as *mut sgx_types::sgx_sealed_data_t, key_len) } { Some(_) => { sgx_status_t::SGX_SUCCESS }, None => { println!("to_raw_sealed_data_t error!"); return sgx_status_t::SGX_ERROR_INVALID_PARAMETER; } } }