Skip to content

Commit

Permalink
kms: Implement derive key
Browse files Browse the repository at this point in the history
  • Loading branch information
kvinwang committed Sep 25, 2024
1 parent 195a78b commit 03cf1ff
Show file tree
Hide file tree
Showing 10 changed files with 123 additions and 64 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions kms-rpc/proto/kms_rpc.proto
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,5 @@ service KMS {
message AppKeyResponse {
string app_key = 1;
string disk_crypt_key = 2;
string certificate_chain = 3;
string comment = 4;
repeated string certificate_chain = 3;
}
2 changes: 2 additions & 0 deletions kms/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@ edition = "2021"
[dependencies]
anyhow = "1.0.88"
hex = "0.4.3"
hex_fmt = "0.3.0"
kms-rpc = { version = "0.1.0", path = "../kms-rpc" }
ra-rpc = { version = "0.1.0", path = "../ra-rpc" }
ra-tls = { version = "0.1.0", path = "../ra-tls" }
rocket = { git = "https://github.com/rwf2/Rocket", branch = "master", features = ["mtls"] }
serde = { version = "1.0.210", features = ["derive"] }
tracing = "0.1.40"
tracing-subscriber = "0.3.18"
yasna = "0.5.2"
Expand Down
11 changes: 11 additions & 0 deletions kms/assets/tmp-ca.cert
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
-----BEGIN CERTIFICATE-----
MIIBjDCCATGgAwIBAgIUWXh1RpxZtF5Jp5cKhqhTYsRoEckwCgYIKoZIzj0EAwIw
LzEWMBQGA1UECgwNUGhhbGEgTmV0d29yazEVMBMGA1UEAwwMUGhhbGEgS01TIENB
MCAXDTc1MDEwMTAwMDAwMFoYDzQwOTYwMTAxMDAwMDAwWjAjMSEwHwYDVQQDDBhQ
aGFsYSBLTVMgQ2xpZW50IFRlbXAgQ0EwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC
AASBvSl/4NYZDSE5fw0iH0mYNl23zCOQ5EzkmNM5C/05tMHUhmMFnqH1QNq/JkAi
TkRxWYtpuKF0VayktddtUORhozUwMzAdBgNVHQ4EFgQUWfxxVFm7zz1v/Olvr691
WhWl7LswEgYDVR0TAQH/BAgwBgEB/wIBATAKBggqhkjOPQQDAgNJADBGAiEA63HF
gJl9PLKhhaGf745E0JklnEVp6oFYdYPEZGLybEoCIQDu5oDNww9pu60GXJ28K3Lf
ihCFs0CQqf9RT9gfS71h8A==
-----END CERTIFICATE-----
5 changes: 5 additions & 0 deletions kms/assets/tmp-ca.key
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
-----BEGIN PRIVATE KEY-----
MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgp0RkgbxCg0ytHj+f
Ox8zsDT4tvFCdUGqYLfEnkRFXsehRANCAASBvSl/4NYZDSE5fw0iH0mYNl23zCOQ
5EzkmNM5C/05tMHUhmMFnqH1QNq/JkAiTkRxWYtpuKF0VayktddtUORh
-----END PRIVATE KEY-----
14 changes: 7 additions & 7 deletions kms/kms.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ ident = "Phala KMS"
temp_dir = "/tmp"
keep_alive = 10
log_level = "debug"
address = "0.0.0.0"
port = 8000

[default.tls]
key = "assets/kms-www.key"
certs = "assets/kms-www.cert"

[default.tls.mutual]
ca_certs = "assets/ca.cert"
ca_certs = "assets/tmp-ca.cert"
mandatory = false

[default.limits]
Expand All @@ -23,9 +25,10 @@ json = "1MiB"
msgpack = "1MiB"
string = "8KiB"

[public]
address = "0.0.0.0"
port = 8000
[core]
root_ca_cert = "assets/ca.cert"
root_ca_key = "assets/ca.key"
subject_postfix = ".phala.network"

[core.allowed_mr]
mrtd = [
Expand All @@ -40,6 +43,3 @@ rtmr1 = [
rtmr2 = [
"569755c97d572d9ba2de9a655e9e786ae1cb3f73d28b6047d79678cce24036adba9fb080547e84b2ec98910032828ddd",
]
rtmr3 = [
"000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
]
67 changes: 36 additions & 31 deletions kms/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use anyhow::{Context, Result};
use anyhow::Result;
use rocket::figment::{
providers::{Format, Toml},
Figment,
};
use serde::Deserialize;

pub const CONFIG_FILENAME: &str = "kms.toml";
pub const SYSTEM_CONFIG_FILENAME: &str = "/etc/kms/kms.toml";
Expand All @@ -15,19 +16,18 @@ pub fn load_config_figment() -> Figment {
.merge(Toml::file(CONFIG_FILENAME).nested())
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Deserialize)]
pub(crate) struct KmsConfig {
pub allowed_mr: AllowedMr,
pub root_ca_cert: String,
pub root_ca_key: String,
pub subject_postfix: String,
}

impl KmsConfig {
pub fn load() -> Result<Self> {
let figment = load_config_figment();
Self::from_figment(figment.select("core"))
}
pub fn from_figment(figment: Figment) -> Result<Self> {
let allowed_mr = AllowedMr::from_figment(figment.focus("allowed_mr"))?;
Ok(Self { allowed_mr })
Ok(figment.select("core").extract()?)
}
}

Expand All @@ -37,36 +37,41 @@ pub(crate) struct AllowedMr {
pub rtmr0: Vec<[u8; 48]>,
pub rtmr1: Vec<[u8; 48]>,
pub rtmr2: Vec<[u8; 48]>,
pub rtmr3: Vec<[u8; 48]>,
}

impl AllowedMr {
pub fn from_figment(figment: Figment) -> Result<Self> {
fn read_mrlist(figment: &Figment, name: &str) -> Result<Vec<[u8; 48]>> {
let list = figment
.extract_inner::<Vec<String>>(name)
.unwrap_or_default()
.into_iter()
impl<'de> Deserialize<'de> for AllowedMr {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
struct RawAllowedMr {
mrtd: Vec<String>,
rtmr0: Vec<String>,
rtmr1: Vec<String>,
rtmr2: Vec<String>,
}

let raw = RawAllowedMr::deserialize(deserializer)?;

fn parse_mrlist<'d, D: serde::Deserializer<'d>>(
list: Vec<String>,
) -> Result<Vec<[u8; 48]>, D::Error> {
list.into_iter()
.map(|s| {
let bytes = hex::decode(s)?;
let mr: [u8; 48] = bytes.try_into().ok().context("invalid MR config")?;
Ok(mr)
let bytes = hex::decode(&s).map_err(serde::de::Error::custom)?;
bytes
.try_into()
.map_err(|_| serde::de::Error::custom("invalid MR config"))
})
.collect::<Result<Vec<_>>>()?;
Ok(list)
.collect()
}
let mrtd = read_mrlist(&figment, "mrtd")?;
let rtmr0 = read_mrlist(&figment, "rtmr0")?;
let rtmr1 = read_mrlist(&figment, "rtmr1")?;
let rtmr2 = read_mrlist(&figment, "rtmr2")?;
let rtmr3 = read_mrlist(&figment, "rtmr3")?;

Ok(Self {
mrtd,
rtmr0,
rtmr1,
rtmr2,
rtmr3,
Ok(AllowedMr {
mrtd: parse_mrlist::<D>(raw.mrtd)?,
rtmr0: parse_mrlist::<D>(raw.rtmr0)?,
rtmr1: parse_mrlist::<D>(raw.rtmr1)?,
rtmr2: parse_mrlist::<D>(raw.rtmr2)?,
})
}
}
4 changes: 2 additions & 2 deletions kms/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,11 @@ async fn main() -> Result<()> {
info!("Starting KMS");
info!("Supported methods:");
for method in main_service::rpc_methods() {
info!(" {method}");
info!(" /prpc/{method}");
}

let config = config::KmsConfig::load().context("Failed to read config file")?;
let state = main_service::KmsState::new(config);
let state = main_service::KmsState::new(config).context("Failed to initialize KMS state")?;

let figment = config::load_config_figment().select("public");
let rocket = rocket::custom(figment)
Expand Down
76 changes: 57 additions & 19 deletions kms/src/main_service.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
use std::sync::Arc;

use anyhow::{bail, Result};
use anyhow::{bail, Context, Result};
use kms_rpc::{
kms_server::{KmsRpc, KmsServer},
AppKeyResponse,
};
use ra_rpc::RpcCall;
use ra_tls::{
attestation::Attestation,
cert::{CaCert, CertRequest},
kdf::derive_ecdsa_key_pair,
qvl::quote::{Report, TDReport10},
};
use tracing::info;

use crate::config::{AllowedMr, KmsConfig};

Expand All @@ -20,13 +23,23 @@ pub struct KmsState {

struct KmsStateInner {
config: KmsConfig,
root_ca: CaCert,
}

impl KmsState {
pub fn new(config: KmsConfig) -> Self {
Self {
inner: Arc::new(KmsStateInner { config }),
}
fn lock(&self) -> &KmsStateInner {
&self.inner
}

pub fn new(config: KmsConfig) -> Result<Self> {
let ca_cert = CaCert::load(&config.root_ca_cert, &config.root_ca_key)
.context("Failed to load root CA certificate")?;
Ok(Self {
inner: Arc::new(KmsStateInner {
config,
root_ca: ca_cert,
}),
})
}
}

Expand All @@ -41,19 +54,9 @@ impl AllowedMr {
&& self.rtmr0.contains(&report.rt_mr0)
&& self.rtmr1.contains(&report.rt_mr1)
&& self.rtmr2.contains(&report.rt_mr2)
&& self.rtmr3.contains(&report.rt_mr3)
}
}

#[derive(Debug)]
struct AttestedInfo {
mrtd: [u8; 48],
rtmr0: [u8; 48],
rtmr1: [u8; 48],
rtmr2: [u8; 48],
rtmr3: [u8; 48],
}

impl RpcHandler {
fn ensure_attested(&self) -> Result<&Attestation> {
let Some(attestation) = &self.attestation else {
Expand All @@ -66,6 +69,12 @@ impl RpcHandler {
Report::TD10(r) => r,
Report::TD15(r) => r.base,
};
info!("Incoming report:");
info!("MRTD: {:?}", hex_fmt::HexFmt(&report.mr_td));
info!("RTMR0: {:?}", hex_fmt::HexFmt(&report.rt_mr0));
info!("RTMR1: {:?}", hex_fmt::HexFmt(&report.rt_mr1));
info!("RTMR2: {:?}", hex_fmt::HexFmt(&report.rt_mr2));
info!("RTMR3: {:?}", hex_fmt::HexFmt(&report.rt_mr3));

if !self.state.inner.config.allowed_mr.is_allowed(&report) {
bail!("Forbidden MR");
Expand All @@ -77,11 +86,40 @@ impl RpcHandler {
impl KmsRpc for RpcHandler {
async fn get_app_key(self) -> Result<AppKeyResponse> {
let attest = self.ensure_attested()?;
let app_id = attest.decode_app_id().context("Failed to decode app ID")?;
let state = self.state.lock();

let app_key = derive_ecdsa_key_pair(
&state.root_ca.key,
&[app_id.as_bytes(), "app-key".as_bytes()],
)
.context("Failed to derive app key")?;

let app_disk_key = derive_ecdsa_key_pair(
&state.root_ca.key,
&[app_id.as_bytes(), "app-disk-key".as_bytes()],
)
.context("Failed to derive app disk key")?;

let subject = format!("{app_id}{}", state.config.subject_postfix);
let req = CertRequest::builder()
.subject(&subject)
.ca_level(1)
.quote(&attest.quote)
.event_log(&attest.event_log)
.app_info(&attest.app_info)
.key(&app_key)
.build();

let cert = state
.root_ca
.sign(req)
.context("Failed to sign certificate")?;

Ok(AppKeyResponse {
app_key: "example app key".to_string(),
disk_crypt_key: "example disk crypt key".to_string(),
certificate_chain: "example chain".to_string(),
comment: hex::encode(&attest.quote),
app_key: app_key.serialize_pem(),
disk_crypt_key: app_disk_key.serialize_pem(),
certificate_chain: vec![cert.pem(), state.root_ca.cert.pem()],
})
}
}
Expand Down
3 changes: 0 additions & 3 deletions tappd/src/rpc_service.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
use std::sync::Arc;

use anyhow::{Context, Result};
use fs_err as fs;
use ra_rpc::{Attestation, RpcCall};
use ra_tls::{
cert::{CaCert, CertRequest},
kdf::derive_ecdsa_key_pair,
rcgen,
};
use rcgen::{Certificate, CertificateParams, KeyPair};
use tappd_rpc::{
tappd_server::{TappdRpc, TappdServer},
DeriveKeyArgs, DeriveKeyResponse, TdxQuoteArgs, TdxQuoteResponse,
Expand Down

0 comments on commit 03cf1ff

Please sign in to comment.