From dc67cd003edfcd1a7b12a8e80a6f64e63873c1e4 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Mon, 28 Aug 2023 11:00:40 +0000 Subject: [PATCH] pRuntime: Manually encrypted worker privkey and gk master key --- Cargo.lock | 1 + crates/phactory/api/proto | 2 +- crates/phactory/src/lib.rs | 2 +- crates/sgx-api-lite/Cargo.toml | 1 + crates/sgx-api-lite/src/egetkey.rs | 115 ++++++++++++++++++ crates/sgx-api-lite/src/lib.rs | 2 + standalone/pruntime/Cargo.lock | 2 + standalone/pruntime/Cargo.toml | 1 + .../gramine-build/pruntime.manifest.template | 3 +- standalone/pruntime/src/pal_gramine.rs | 72 ++++++++++- 10 files changed, 192 insertions(+), 9 deletions(-) create mode 100644 crates/sgx-api-lite/src/egetkey.rs diff --git a/Cargo.lock b/Cargo.lock index c73a590f24..8dd3534e60 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -13160,6 +13160,7 @@ name = "sgx-api-lite" version = "0.1.0" dependencies = [ "aes 0.8.1", + "bitflags 2.0.2", "cmac", ] diff --git a/crates/phactory/api/proto b/crates/phactory/api/proto index 8c1b23f60b..da0b252238 160000 --- a/crates/phactory/api/proto +++ b/crates/phactory/api/proto @@ -1 +1 @@ -Subproject commit 8c1b23f60bfba5b33d11d7223dfcbe9a0ef76501 +Subproject commit da0b25223851b2b0be6eef765a7a315b65ac9028 diff --git a/crates/phactory/src/lib.rs b/crates/phactory/src/lib.rs index ffd9df0c7c..1e6b3b6de8 100644 --- a/crates/phactory/src/lib.rs +++ b/crates/phactory/src/lib.rs @@ -840,7 +840,7 @@ fn new_sr25519_key() -> sr25519::Pair { } // TODO.kevin: Move to phactory-api when the std ready. -fn generate_random_iv() -> aead::IV { +pub fn generate_random_iv() -> aead::IV { let mut nonce_vec = [0u8; aead::IV_BYTES]; let rand = ring::rand::SystemRandom::new(); rand.fill(&mut nonce_vec).unwrap(); diff --git a/crates/sgx-api-lite/Cargo.toml b/crates/sgx-api-lite/Cargo.toml index 880b2325ad..3c1b5e928b 100644 --- a/crates/sgx-api-lite/Cargo.toml +++ b/crates/sgx-api-lite/Cargo.toml @@ -6,3 +6,4 @@ edition = "2021" [dependencies] cmac = "0.7.1" aes = "0.8.1" +bitflags = "2" diff --git a/crates/sgx-api-lite/src/egetkey.rs b/crates/sgx-api-lite/src/egetkey.rs new file mode 100644 index 0000000000..e31460b75c --- /dev/null +++ b/crates/sgx-api-lite/src/egetkey.rs @@ -0,0 +1,115 @@ +//! This file is copied from crate sgx_isa which is part of Fortanix, we can not directly use +//! sgx_isa because it requires compilation target=fortanix-sgx. + +#![allow(dead_code)] +#![allow(clippy::enum_variant_names)] + +use bitflags::bitflags; +use core::arch::asm; +use core::mem::MaybeUninit; + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u16)] +enum Keyname { + Einittoken = 0, + Provision = 1, + ProvisionSeal = 2, + Report = 3, + Seal = 4, +} + +bitflags! { + #[repr(C)] + struct Keypolicy: u16 { + const MRENCLAVE = 0b0000_0001; + const MRSIGNER = 0b0000_0010; + } +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[repr(u32)] +enum Enclu { + EReport = 0, + EGetkey = 1, + EEnter = 2, + EResume = 3, + EExit = 4, + EAccept = 5, + EModpe = 6, + EAcceptcopy = 7, +} + +#[repr(align(16))] +struct Align16(T); + +#[repr(align(512))] +struct Align512(T); + +/// Call the `EGETKEY` instruction to obtain a 128-bit secret key. +fn egetkey(request: &Align512<[u8; 512]>) -> Result, u32> { + unsafe { + let mut out = MaybeUninit::uninit(); + let error; + + asm!( + // rbx is reserved by LLVM + "xchg %rbx, {0}", + "enclu", + "mov {0}, %rbx", + inout(reg) request => _, + inlateout("eax") Enclu::EGetkey as u32 => error, + in("rcx") out.as_mut_ptr(), + options(att_syntax, nostack), + ); + + match error { + 0 => Ok(out.assume_init()), + err => Err(err), + } + } +} + +#[repr(C, align(512))] +struct Keyrequest { + keyname: u16, + keypolicy: Keypolicy, + isvsvn: u16, + _reserved1: u16, + cpusvn: [u8; 16], + attributemask: [u64; 2], + keyid: [u8; 32], + miscmask: u32, + _reserved2: [u8; 436], +} + +impl Keyrequest { + fn copy(&self) -> [u8; 512] { + unsafe { *(self as *const Keyrequest as *const [u8; 512]) } + } + + fn egetkey(&self) -> Result, u32> { + egetkey(&Align512(self.copy())) + } +} + +/// Derive a MRENCLAVE sealing key with the given `isvsvn`, `cpusvn` and `keyid`. +pub fn get_mrenclave_sealing_key( + isvsvn: u16, + cpusvn: [u8; 16], + keyid: [u8; 32], +) -> Result<[u8; 16], u32> { + let request = Keyrequest { + keyname: Keyname::Seal as u16, + keypolicy: Keypolicy::MRENCLAVE, + isvsvn, + _reserved1: 0, + cpusvn, + // Less mask to prevent the derived key changes during system upgrade. + attributemask: [0x03, 0x00], + keyid, + miscmask: 0, + _reserved2: [0; 436], + }; + let key = request.egetkey()?; + Ok(key.0) +} diff --git a/crates/sgx-api-lite/src/lib.rs b/crates/sgx-api-lite/src/lib.rs index 5a74b70d62..d3de42572b 100644 --- a/crates/sgx-api-lite/src/lib.rs +++ b/crates/sgx-api-lite/src/lib.rs @@ -25,8 +25,10 @@ use std::mem::{size_of, zeroed}; pub use sys::sgx_report_data_t as ReportData; pub use sys::sgx_report_t as Report; pub use sys::sgx_target_info_t as TargetInfo; +pub use egetkey::get_mrenclave_sealing_key; mod sys; +mod egetkey; #[repr(C, align(512))] struct SgxAligned(T); diff --git a/standalone/pruntime/Cargo.lock b/standalone/pruntime/Cargo.lock index 05dee62d03..b2a99cfeed 100644 --- a/standalone/pruntime/Cargo.lock +++ b/standalone/pruntime/Cargo.lock @@ -6132,6 +6132,7 @@ dependencies = [ "phactory-pal", "phala-allocator", "phala-clap-parsers", + "phala-crypto", "phala-git-revision", "phala-rocket-middleware", "phala-sanitized-logger", @@ -7281,6 +7282,7 @@ name = "sgx-api-lite" version = "0.1.0" dependencies = [ "aes", + "bitflags 2.0.2", "cmac", ] diff --git a/standalone/pruntime/Cargo.toml b/standalone/pruntime/Cargo.toml index 2783039e0a..e61487fc42 100644 --- a/standalone/pruntime/Cargo.toml +++ b/standalone/pruntime/Cargo.toml @@ -39,6 +39,7 @@ phala-rocket-middleware = { path = "../../crates/phala-rocket-middleware" } phala-types = { path = "../../crates/phala-types", features = ["enable_serde", "sgx"] } phala-git-revision = { path = "../../crates/phala-git-revision" } phala-clap-parsers = { path = "../../crates/phala-clap-parsers" } +phala-crypto = { path = "../../crates/phala-crypto" } sgx-api-lite = { path = "../../crates/sgx-api-lite" } tracing = "0.1" hex_fmt = "0.3.0" diff --git a/standalone/pruntime/gramine-build/pruntime.manifest.template b/standalone/pruntime/gramine-build/pruntime.manifest.template index e6fe19f531..8ab41daadf 100644 --- a/standalone/pruntime/gramine-build/pruntime.manifest.template +++ b/standalone/pruntime/gramine-build/pruntime.manifest.template @@ -47,8 +47,6 @@ uri = "file:{{ libdir }}" [[fs.mounts]] path = "/data/protected_files" uri = "file:{{ seal_dir }}" -type = "encrypted" -key_name = "_sgx_mrenclave" [[fs.mounts]] type = "chroot" @@ -89,4 +87,5 @@ allowed_files = [ "file:/etc/resolv.conf", "file:Rocket.toml", "file:{{ storage_dir }}/", + "file:{{ seal_dir }}/", ] diff --git a/standalone/pruntime/src/pal_gramine.rs b/standalone/pruntime/src/pal_gramine.rs index ebb36142e3..13152b4b93 100644 --- a/standalone/pruntime/src/pal_gramine.rs +++ b/standalone/pruntime/src/pal_gramine.rs @@ -1,5 +1,5 @@ use anyhow::anyhow; -use parity_scale_codec::Encode; +use parity_scale_codec::{Decode, Encode}; use std::alloc::System; use tracing::info; @@ -16,16 +16,70 @@ use phala_types::AttestationProvider; #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)] pub(crate) struct GraminePlatform; +#[derive(Encode, Decode)] +struct SealedData { + isvsvn: u16, + cpusvn: [u8; 16], + iv: [u8; 12], + data: Vec, +} + +fn get_sealing_key(isvsvn: u16, cpusvn: [u8; 16]) -> anyhow::Result<[u8; 32]> { + const KEY_ID: [u8; 32] = *b"pruntime-sealed-data-key-id\0\0\0\0\0"; + let key128 = sgx_api_lite::get_mrenclave_sealing_key(isvsvn, cpusvn, KEY_ID) + .or(Err(anyhow!("Failed to get sealing key")))?; + // phala_crypto API uses AES256, so extend the key to 256 bits + let mut key256 = [0; 32]; + key256[..16].copy_from_slice(&key128[..]); + Ok(key256) +} + +fn sgx_seal_data(data: &[u8]) -> anyhow::Result { + let this_target_info = + sgx_api_lite::target_info().or(Err(anyhow!("Failed to get target info")))?; + let report = sgx_api_lite::report(&this_target_info, &[0; 64]) + .or(Err(anyhow!("Failed to get SGX report")))?; + let isvsvn = report.body.isv_svn; + let cpusvn = report.body.cpu_svn.svn; + let key = get_sealing_key(isvsvn, cpusvn) + .or(Err(anyhow!("Failed to get sealing key")))?; + let iv = phactory::generate_random_iv(); + let mut data = data.to_vec(); + phala_crypto::aead::encrypt(&iv, &key, &mut data).or(Err(anyhow!("Failed to encrypt data")))?; + Ok(SealedData { + isvsvn, + cpusvn, + iv, + data, + }) +} + +fn sgx_unseal_data(data: &SealedData) -> anyhow::Result> { + let key = get_sealing_key(data.isvsvn, data.cpusvn) + .or(Err(anyhow!("Failed to get sealing key")))?; + let mut enccypted_data = data.data.clone(); + let decrypted = phala_crypto::aead::decrypt(&data.iv, &key, &mut enccypted_data[..]) + .or(Err(anyhow!("Failed to decrypt sealed data",)))?; + Ok(decrypted.to_vec()) +} + impl Sealing for GraminePlatform { - type SealError = std::io::Error; - type UnsealError = std::io::Error; + type SealError = anyhow::Error; + type UnsealError = anyhow::Error; fn seal_data( &self, path: impl AsRef, data: &[u8], ) -> Result<(), Self::SealError> { - std::fs::write(path, data)?; + if !is_gramine() { + std::fs::write(path, data)?; + return Ok(()); + } + info!("Sealing data to {:?}", path.as_ref()); + let data = sgx_seal_data(data)?; + let encoded = data.encode(); + std::fs::write(path, encoded)?; Ok(()) } @@ -35,7 +89,15 @@ impl Sealing for GraminePlatform { ) -> Result>, Self::UnsealError> { match std::fs::read(path) { Err(err) if matches!(err.kind(), ErrorKind::NotFound) => Ok(None), - other => other.map(Some), + Ok(data) => { + if !is_gramine() { + return Ok(Some(data)); + } + let data = SealedData::decode(&mut &data[..])?; + let data = sgx_unseal_data(&data)?; + Ok(Some(data)) + } + Err(err) => Err(err.into()), } } }