From 4938282ea34fde3c1d6827728eaf97f7c6e58b87 Mon Sep 17 00:00:00 2001 From: "Zheng, Qi" Date: Fri, 24 Mar 2023 17:35:53 +0800 Subject: [PATCH] Add init with aecs kms client support --- etc/template/init_aecs.json | 38 ++++++ tools/Makefile | 2 + tools/init_aecs/Cargo.lock | 96 +++++++++++++ tools/init_aecs/Cargo.toml | 13 ++ tools/init_aecs/Makefile | 16 +++ tools/init_aecs/build.rs | 9 ++ tools/init_aecs/src/main.rs | 265 ++++++++++++++++++++++++++++++++++++ 7 files changed, 439 insertions(+) create mode 100644 etc/template/init_aecs.json create mode 100644 tools/init_aecs/Cargo.lock create mode 100644 tools/init_aecs/Cargo.toml create mode 100644 tools/init_aecs/Makefile create mode 100644 tools/init_aecs/build.rs create mode 100644 tools/init_aecs/src/main.rs diff --git a/etc/template/init_aecs.json b/etc/template/init_aecs.json new file mode 100644 index 00000000..cc502774 --- /dev/null +++ b/etc/template/init_aecs.json @@ -0,0 +1,38 @@ +{ + "kms_server": "localhost:19527", + "kms_keys": [ + { + "key": "demo_key", + "path": "/etc/demo_key", + "service": "service" + } + ], + "ua_env_pccs_url": "", + "ra_config": { + "ua_ias_url": "https://api.trustedservices.intel.com/sgx/dev/attestation/v4", + "ua_ias_spid": "", + "ua_ias_apk_key": "", + "ua_dcap_lib_path": "", + "ua_dcap_pccs_url": "", + "ua_uas_url": "", + "ua_uas_app_key": "", + "ua_uas_app_secret": "", + "ua_policy_str_tee_platform": "", + "ua_policy_hex_platform_hw_version": "", + "ua_policy_hex_platform_sw_version": "", + "ua_policy_hex_secure_flags": "", + "ua_policy_hex_platform_measurement": "", + "ua_policy_hex_boot_measurement": "", + "ua_policy_str_tee_identity": "", + "ua_policy_hex_ta_measurement": "", + "ua_policy_hex_ta_dyn_measurement": "", + "ua_policy_hex_signer": "", + "ua_policy_hex_prod_id": "", + "ua_policy_str_min_isvsvn": "", + "ua_policy_hex_user_data": "", + "ua_policy_bool_debug_disabled": "", + "ua_policy_hex_hash_or_pem_pubkey": "", + "ua_policy_hex_nonce": "", + "ua_policy_hex_spid": "" + } +} diff --git a/tools/Makefile b/tools/Makefile index a6234fec..0c232036 100644 --- a/tools/Makefile +++ b/tools/Makefile @@ -13,6 +13,7 @@ all: @$(MAKE) --no-print-directory -C copy_bom @$(MAKE) --no-print-directory -C init @$(MAKE) --no-print-directory -C init_grpc_ratls + @$(MAKE) --no-print-directory -C init_aecs format: @$(MAKE) --no-print-directory -C protect-integrity format @@ -26,3 +27,4 @@ clean: @$(MAKE) --no-print-directory -C copy_bom clean @$(MAKE) --no-print-directory -C init clean @$(MAKE) --no-print-directory -C init_grpc_ratls clean + @$(MAKE) --no-print-directory -C init_aecs clean diff --git a/tools/init_aecs/Cargo.lock b/tools/init_aecs/Cargo.lock new file mode 100644 index 00000000..329855fb --- /dev/null +++ b/tools/init_aecs/Cargo.lock @@ -0,0 +1,96 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "init_aecs" +version = "0.1.0" +dependencies = [ + "libc", + "serde", + "serde_json", +] + +[[package]] +name = "itoa" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" + +[[package]] +name = "libc" +version = "0.2.140" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c" + +[[package]] +name = "proc-macro2" +version = "1.0.52" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d0e1ae9e836cc3beddd63db0df682593d7e2d3d891ae8c9083d2113e1744224" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "ryu" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f91339c0467de62360649f8d3e185ca8de4224ff281f66000de5eb2a77a79041" + +[[package]] +name = "serde" +version = "1.0.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.158" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "serde_json" +version = "1.0.94" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c533a59c9d8a93a09c6ab31f0fd5e5f4dd1b8fc9434804029839884765d04ea" +dependencies = [ + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "syn" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c622ae390c9302e214c31013517c2061ecb2699935882c60a9b37f82f8625ae" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "unicode-ident" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4" diff --git a/tools/init_aecs/Cargo.toml b/tools/init_aecs/Cargo.toml new file mode 100644 index 00000000..6588bb09 --- /dev/null +++ b/tools/init_aecs/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "init_aecs" +version = "0.1.0" +build = "build.rs" +authors = ["Zheng Qi huaiqing.zq@antgroup.com"] +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +libc = "0.2.84" +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" \ No newline at end of file diff --git a/tools/init_aecs/Makefile b/tools/init_aecs/Makefile new file mode 100644 index 00000000..07443fdb --- /dev/null +++ b/tools/init_aecs/Makefile @@ -0,0 +1,16 @@ +include ../../src/sgxenv.mk + +SRC_FILES := $(shell find . -type f -name '*.rs') Cargo.toml +RUST_TARGET_DIR := $(BUILD_DIR)/internal/tools/init_aecs/cargo-target +RUST_OUT_DIR := $(BUILD_DIR)/bin +TARGET_BINARY := $(RUST_OUT_DIR)/init_aecs + +.PHONY: all clean + +all: $(SRC_FILES) + @RUSTC_BOOTSTRAP=1 cargo build --release --target-dir=$(RUST_TARGET_DIR) -Z unstable-options --out-dir=$(RUST_OUT_DIR) + @echo "CARGO (release) => init_aecs" + +clean: + @cargo clean --target-dir=$(RUST_TARGET_DIR) + @-$(RM) -f $(TARGET_BINARY) diff --git a/tools/init_aecs/build.rs b/tools/init_aecs/build.rs new file mode 100644 index 00000000..5ac7327e --- /dev/null +++ b/tools/init_aecs/build.rs @@ -0,0 +1,9 @@ +fn main() { + println!("cargo:rustc-link-search=native=/opt/occlum/toolchains/aecs_client"); + println!("cargo:rustc-link-lib=dylib=aecs_client"); + println!("cargo:rustc-link-lib=dylib=ual"); + println!("cargo:rustc-link-lib=curl_static"); + println!("cargo:rustc-link-lib=dylib=ssl"); + println!("cargo:rustc-link-lib=dylib=z"); + println!("cargo:rustc-link-lib=dylib=crypto"); +} \ No newline at end of file diff --git a/tools/init_aecs/src/main.rs b/tools/init_aecs/src/main.rs new file mode 100644 index 00000000..3848a247 --- /dev/null +++ b/tools/init_aecs/src/main.rs @@ -0,0 +1,265 @@ +extern crate libc; +extern crate serde; +extern crate serde_json; + +use libc::syscall; +use serde::{Deserialize, Serialize}; + +use std::error::Error; +use std::fs; +use std::fs::File; +use std::io::{ErrorKind, Read}; +use std::str; +use std::env; + +use std::ffi::CString; +use std::os::raw::{c_int, c_char}; + +#[link(name = "aecs_client")] +extern "C" { + fn aecs_client_get_secret_and_save_file( + aec_server_endpoint: *const c_char, + aecs_server_policy: *const c_char, + secret_service: *const c_char, + secret_name: *const c_char, + nonce: *const c_char, + save_file_name: *const c_char + ) -> c_int; +} + +#[derive(Deserialize, Serialize, Debug)] +#[warn(dead_code)] +struct MRsValue { + pub mr_enclave: String, + pub mr_signer: String, + pub isv_prod_id: u32, + pub isv_svn: u32, + pub config_svn: u32, + pub debuggable: bool, +} + +#[derive(Deserialize, Serialize, Debug)] +#[warn(dead_code)] +struct RAConfig { + ua_ias_url: String, + ua_ias_spid: String, + ua_ias_apk_key: String, + ua_dcap_lib_path: String, + ua_dcap_pccs_url: String, + ua_uas_url: String, + ua_uas_app_key: String, + ua_uas_app_secret: String, + ua_policy_str_tee_platform: String, + ua_policy_hex_platform_hw_version: String, + ua_policy_hex_platform_sw_version: String, + ua_policy_hex_secure_flags: String, + ua_policy_hex_platform_measurement: String, + ua_policy_hex_boot_measurement: String, + ua_policy_str_tee_identity: String, + ua_policy_hex_ta_measurement: String, + ua_policy_hex_ta_dyn_measurement: String, + ua_policy_hex_signer: String, + ua_policy_hex_prod_id: String, + ua_policy_str_min_isvsvn: String, + ua_policy_hex_user_data: String, + ua_policy_bool_debug_disabled: String, + ua_policy_hex_hash_or_pem_pubkey: String, + ua_policy_hex_nonce: String, + ua_policy_hex_spid: String, +} + +#[derive(Deserialize, Serialize, Debug, Clone)] +#[warn(dead_code)] +struct KmsKeys { + key: String, + path: String, + service: String, +} + +#[derive(Deserialize, Serialize, Debug)] +#[warn(dead_code)] +struct InitRAConfig { + kms_server: String, + kms_keys: Vec, + ua_env_pccs_url: String, + ra_config: RAConfig +} + +fn load_ra_config(ra_conf_path: &str) -> Result> { + let mut ra_conf_file = File::open(ra_conf_path)?; + let ra_conf = { + let mut ra_conf = String::new(); + ra_conf_file.read_to_string(&mut ra_conf)?; + ra_conf + }; + let config: InitRAConfig = serde_json::from_str(&ra_conf)?; + Ok(config) +} + + +fn main() -> Result<(), Box> { + // Load the configuration from initfs + const IMAGE_CONFIG_FILE: &str = "/etc/image_config.json"; + const INIT_RA_CONF: &str = "/etc/init_ra_conf.json"; + let image_config = load_config(IMAGE_CONFIG_FILE)?; + + // Get the MAC of Occlum.json.protected file + let occlum_json_mac = { + let mut mac: sgx_aes_gcm_128bit_tag_t = Default::default(); + parse_str_to_bytes(&image_config.occlum_json_mac, &mut mac)?; + mac + }; + let occlum_json_mac_ptr = &occlum_json_mac as *const sgx_aes_gcm_128bit_tag_t; + + // Do parse to get Init RA information + let init_ra_conf = load_ra_config(INIT_RA_CONF)?; + // Extract RA config part + let ra_conf_string = serde_json::to_string_pretty(&init_ra_conf.ra_config).unwrap(); + fs::create_dir_all("/etc/kubetee")?; + fs::write("/etc/kubetee/unified_attestation.json", ra_conf_string.clone().into_bytes())?; + + let server_addr = CString::new(init_ra_conf.kms_server).unwrap(); + env::set_var("UA_ENV_PCCS_URL", init_ra_conf.ua_env_pccs_url.clone()); + + // Get the key of FS image if needed + let key = match &image_config.image_type[..] { + "encrypted" => { + // Get the image encrypted key through RA + let secret = CString::new("image_key").unwrap(); + let service = CString::new("service1").unwrap(); + let filename = CString::new("/etc/image_key").unwrap(); + + let ret = unsafe { + aecs_client_get_secret_and_save_file( + server_addr.as_ptr(), + std::ptr::null(), + service.as_ptr(), + secret.as_ptr(), + std::ptr::null(), + filename.as_ptr()) + }; + + if ret != 0 { + println!("grpc_ratls_get_secret failed return {}", ret); + return Err(Box::new(std::io::Error::last_os_error())); + } + + const IMAGE_KEY_FILE: &str = "/etc/image_key"; + let key_str = load_key(IMAGE_KEY_FILE)?; + // Remove key file which is not needed any more + fs::remove_file(IMAGE_KEY_FILE)?; + let mut key: sgx_key_128bit_t = Default::default(); + parse_str_to_bytes(&key_str, &mut key)?; + Some(key) + } + "integrity-only" => None, + _ => unreachable!(), + }; + let key_ptr = key + .as_ref() + .map(|key| key as *const sgx_key_128bit_t) + .unwrap_or(std::ptr::null()); + + // Do one time key acquire to force all necessary libraries got loaded to memory. + // Thus after mount rootfs, the API could run successfully. + // The key got this time will be dropped. + unsafe { + let kms_key = init_ra_conf.kms_keys[0].clone(); + let key = CString::new(kms_key.key).unwrap(); + let file = CString::new(kms_key.path).unwrap(); + let service =CString::new(kms_key.service).unwrap(); + + aecs_client_get_secret_and_save_file( + server_addr.as_ptr(), + std::ptr::null(), + service.as_ptr(), + key.as_ptr(), + std::ptr::null(), + file.as_ptr()); + // Remove key file which is not needed any more + fs::remove_file(file.into_string().unwrap())?; + }; + + // Mount the image + const SYS_MOUNT_FS: i64 = 363; + let ret = unsafe { syscall(SYS_MOUNT_FS, key_ptr, occlum_json_mac_ptr) }; + if ret < 0 { + return Err(Box::new(std::io::Error::last_os_error())); + } + + // Rewrite ra_config to rootfs + fs::create_dir_all("/etc/kubetee")?; + fs::write("/etc/kubetee/unified_attestation.json", ra_conf_string.clone().into_bytes())?; + + // Get keys and save to path + for keys in init_ra_conf.kms_keys { + let key = CString::new(keys.key).unwrap(); + let file = CString::new(keys.path).unwrap(); + let service =CString::new(keys.service).unwrap(); + + let ret = unsafe { + aecs_client_get_secret_and_save_file( + server_addr.as_ptr(), + std::ptr::null(), + service.as_ptr(), + key.as_ptr(), + std::ptr::null(), + file.as_ptr()) + }; + + if ret != 0 { + println!("Failed to get key {:?}, return {}", key, ret); + // return Err(Box::new(std::io::Error::last_os_error())); + } + } + + Ok(()) +} + +#[allow(non_camel_case_types)] +type sgx_key_128bit_t = [u8; 16]; +#[allow(non_camel_case_types)] +type sgx_aes_gcm_128bit_tag_t = [u8; 16]; + +#[derive(Deserialize, Debug)] +#[serde(deny_unknown_fields)] +struct ImageConfig { + occlum_json_mac: String, + image_type: String, +} + +fn load_config(config_path: &str) -> Result> { + let mut config_file = File::open(config_path)?; + let config_json = { + let mut config_json = String::new(); + config_file.read_to_string(&mut config_json)?; + config_json + }; + let config: ImageConfig = serde_json::from_str(&config_json)?; + Ok(config) +} + +fn load_key(key_path: &str) -> Result> { + let mut key_file = File::open(key_path)?; + let mut key = String::new(); + key_file.read_to_string(&mut key)?; + Ok(key.trim_end_matches(|c| c == '\r' || c == '\n').to_string()) +} + +fn parse_str_to_bytes(arg_str: &str, bytes: &mut [u8]) -> Result<(), Box> { + let bytes_str_vec = { + let bytes_str_vec: Vec<&str> = arg_str.split('-').collect(); + if bytes_str_vec.len() != bytes.len() { + return Err(Box::new(std::io::Error::new( + ErrorKind::InvalidData, + "The length or format of Key/MAC string is invalid", + ))); + } + bytes_str_vec + }; + + for (byte_i, byte_str) in bytes_str_vec.iter().enumerate() { + bytes[byte_i] = u8::from_str_radix(byte_str, 16)?; + } + Ok(()) +}