From 44f3eb58c8a0236fc7b18c4524498923b60dbf6b Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Tue, 24 Dec 2024 09:57:01 +0000 Subject: [PATCH 1/5] Add key-provider build files --- key-provider-build/Dockerfile | 55 ++++++++++++++++++++++++++ key-provider-build/docker-compose.yaml | 16 ++++++++ key-provider-build/entrypoint.sh | 5 +++ key-provider-build/run.sh | 2 + 4 files changed, 78 insertions(+) create mode 100644 key-provider-build/Dockerfile create mode 100644 key-provider-build/docker-compose.yaml create mode 100755 key-provider-build/entrypoint.sh create mode 100755 key-provider-build/run.sh diff --git a/key-provider-build/Dockerfile b/key-provider-build/Dockerfile new file mode 100644 index 0000000..c599961 --- /dev/null +++ b/key-provider-build/Dockerfile @@ -0,0 +1,55 @@ +FROM gramineproject/gramine:v1.5 + +# Prevent timezone prompt by setting noninteractive frontend and configuring tzdata +ENV DEBIAN_FRONTEND=noninteractive \ + TZ=Etc/UTC \ + TZDATA=Etc/UTC \ + LC_ALL=en_US.UTF-8 \ + LANG=en_US.UTF-8 + +# Set timezone +RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone + +# Install required packages +RUN apt update && apt install -y \ + build-essential \ + chrpath \ + diffstat \ + lz4 \ + python3 \ + locales \ + git \ + file \ + gawk \ + wget \ + curl \ + libclang-dev \ + xorriso + +# Runtime dependencies +RUN apt-get update && \ + DEBIAN_FRONTEND=noninteractive apt-get install -y \ + sgx-aesm-service \ + libsgx-aesm-launch-plugin \ + libsgx-aesm-quote-ex-plugin \ + libsgx-aesm-ecdsa-plugin \ + libsgx-dcap-quote-verify \ + psmisc + +# Install Rust +RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y +ENV PATH="/root/.cargo/bin:${PATH}" + +RUN git clone https://github.com/MoeMahhouk/gramine-sealing-key-provider.git + +WORKDIR /gramine-sealing-key-provider +# Build gramine-sealing-key-provider binary +RUN make target/release/gramine-sealing-key-provider +# Generate private key +RUN gramine-sgx-gen-private-key +# Build gramine manifest +RUN make RUST_LOG=info + +COPY entrypoint.sh /entrypoint.sh +RUN chmod +x /entrypoint.sh +ENTRYPOINT ["/entrypoint.sh"] diff --git a/key-provider-build/docker-compose.yaml b/key-provider-build/docker-compose.yaml new file mode 100644 index 0000000..f829639 --- /dev/null +++ b/key-provider-build/docker-compose.yaml @@ -0,0 +1,16 @@ +services: + gramine-sealing-key-provider: + container_name: gramine-sealing-key-provider + build: + context: . + dockerfile: Dockerfile + privileged: true + devices: + - "/dev/sgx_enclave:/dev/sgx_enclave" + - "/dev/sgx_provision:/dev/sgx_provision" + volumes: + - ./:/workspace + environment: + - SGX=1 + ports: + - "3443:3443" diff --git a/key-provider-build/entrypoint.sh b/key-provider-build/entrypoint.sh new file mode 100755 index 0000000..a74ee64 --- /dev/null +++ b/key-provider-build/entrypoint.sh @@ -0,0 +1,5 @@ +#!/bin/sh +set -e +AESM_PATH=/opt/intel/sgx-aesm-service/aesm LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm /opt/intel/sgx-aesm-service/aesm/aesm_service --no-syslog +echo "Starting Gramine Sealing Key Provider" +make SGX=1 run-provider diff --git a/key-provider-build/run.sh b/key-provider-build/run.sh new file mode 100755 index 0000000..511a5d7 --- /dev/null +++ b/key-provider-build/run.sh @@ -0,0 +1,2 @@ +#!/bin/sh +docker compose up --build \ No newline at end of file From 8b41acf245bd72a81516da07d13ae84f8888bae5 Mon Sep 17 00:00:00 2001 From: Kevin Wang Date: Wed, 25 Dec 2024 04:09:00 +0000 Subject: [PATCH 2/5] Support for local key provider --- Cargo.lock | 417 ++++++++----------------- Cargo.toml | 5 +- host-api/proto/host_api.proto | 10 + key-provider-build/Dockerfile | 4 +- key-provider-build/docker-compose.yaml | 3 +- key-provider-build/entrypoint.sh | 5 +- key-provider-build/run.sh | 2 +- key-provider-client/Cargo.toml | 13 + key-provider-client/src/guest.rs | 26 ++ key-provider-client/src/host.rs | 53 ++++ key-provider-client/src/lib.rs | 2 + ra-rpc/src/rocket_helper.rs | 4 +- ra-tls/src/kdf.rs | 10 +- tdxctl/Cargo.toml | 2 + tdxctl/src/fde_setup.rs | 180 ++++++++--- tdxctl/src/main.rs | 42 ++- tdxctl/src/notify_client.rs | 11 +- tdxctl/src/utils.rs | 38 ++- teepod/Cargo.toml | 1 + teepod/src/app.rs | 1 + teepod/src/config.rs | 12 + teepod/src/console.html | 8 +- teepod/src/host_api_service.rs | 20 +- teepod/teepod.toml | 7 + 24 files changed, 530 insertions(+), 346 deletions(-) create mode 100644 key-provider-client/Cargo.toml create mode 100644 key-provider-client/src/guest.rs create mode 100644 key-provider-client/src/host.rs create mode 100644 key-provider-client/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 5f6624a..c607078 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -309,12 +309,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf" -[[package]] -name = "base64" -version = "0.21.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" - [[package]] name = "base64" version = "0.22.1" @@ -433,15 +427,15 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97ccca1260af6a459d75994ad5acc1651bcabcbdbc41467cc9786519ab854c30" dependencies = [ - "base64 0.22.1", + "base64", "bollard-stubs", "bytes", "futures-core", "futures-util", "hex", - "http 1.2.0", + "http", "http-body-util", - "hyper 1.5.1", + "hyper", "hyper-named-pipe", "hyper-util", "hyperlocal", @@ -576,7 +570,7 @@ dependencies = [ "path-absolutize", "rand 0.8.5", "rcgen", - "reqwest 0.12.9", + "reqwest", "serde", "serde_json", "time", @@ -595,7 +589,7 @@ dependencies = [ "clap", "documented", "fs-err", - "rustls 0.23.19", + "rustls", "serde", "tokio", "toml_edit", @@ -870,7 +864,7 @@ dependencies = [ "hex_fmt", "ra-rpc", "regex", - "reqwest 0.12.9", + "reqwest", "serde", "serde_json", "tokio", @@ -984,13 +978,13 @@ checksum = "e8566979429cf69b49a5c740c60791108e86440e8be149bbea4fe54d2c32d6e2" [[package]] name = "dcap-qvl" -version = "0.1.6" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03e024d399632fb2e97a9501e79779ae9f150118a17ad06333de3bbd42834b84" +checksum = "964e66fcbdc7a51362e4e8a11c42ae7e76c7403daf5bcfb9b8a7ac1ea43c7e8a" dependencies = [ "anyhow", "asn1_der", - "base64 0.21.7", + "base64", "byteorder", "chrono", "const-oid", @@ -1000,12 +994,12 @@ dependencies = [ "log", "parity-scale-codec", "pem", - "reqwest 0.11.27", + "reqwest", "ring", - "rustls-webpki 0.102.8", + "rustls-webpki", "scale-info", "serde", - "serde_bytes", + "serde-human-bytes", "serde_json", "tracing", "urlencoding", @@ -1077,17 +1071,6 @@ dependencies = [ "serde", ] -[[package]] -name = "derive_more" -version = "0.99.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f33878137e4dafd7fa914ad4e259e18a4e8e532b9617a2d0150262bf53abfce" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - [[package]] name = "derive_more" version = "1.0.0" @@ -1240,10 +1223,19 @@ dependencies = [ "digest", "elliptic-curve", "rfc6979", - "signature", + "signature 2.2.0", "spki", ] +[[package]] +name = "ed25519" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91cff35c70bba8a626e3185d8cd48cc11b5437e1a5bcd15b9b5fa3c64b6dfee7" +dependencies = [ + "signature 1.6.4", +] + [[package]] name = "either" version = "1.13.0" @@ -1666,25 +1658,6 @@ dependencies = [ "serde_json", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.5.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.6" @@ -1696,7 +1669,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.2.0", + "http", "indexmap 2.5.0", "slab", "tokio", @@ -1713,7 +1686,7 @@ dependencies = [ "bytes", "fastrand", "futures-util", - "http 1.2.0", + "http", "pin-project-lite", "tokio", ] @@ -1881,17 +1854,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "http" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" -dependencies = [ - "bytes", - "fnv", - "itoa", -] - [[package]] name = "http" version = "1.2.0" @@ -1903,17 +1865,6 @@ dependencies = [ "itoa", ] -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -1921,7 +1872,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.2.0", + "http", ] [[package]] @@ -1932,8 +1883,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.2.0", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -1943,7 +1894,7 @@ version = "0.3.3" dependencies = [ "anyhow", "http-body-util", - "hyper 1.5.1", + "hyper", "hyper-util", "hyperlocal", "log", @@ -1982,30 +1933,6 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4" -[[package]] -name = "hyper" -version = "0.14.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - [[package]] name = "hyper" version = "1.5.1" @@ -2015,9 +1942,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.6", - "http 1.2.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa", @@ -2034,7 +1961,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73b7d8abf35697b81a825e386fc151e0d503e8cb5fcb93cc8669c376dfd6f278" dependencies = [ "hex", - "hyper 1.5.1", + "hyper", "hyper-util", "pin-project-lite", "tokio", @@ -2042,20 +1969,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "hyper-rustls" -version = "0.24.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" -dependencies = [ - "futures-util", - "http 0.2.12", - "hyper 0.14.30", - "rustls 0.21.12", - "tokio", - "tokio-rustls 0.24.1", -] - [[package]] name = "hyper-rustls" version = "0.27.3" @@ -2063,16 +1976,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" dependencies = [ "futures-util", - "http 1.2.0", - "hyper 1.5.1", + "http", + "hyper", "hyper-util", - "rustls 0.23.19", + "rustls", "rustls-native-certs", "rustls-pki-types", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls", "tower-service", - "webpki-roots 0.26.6", + "webpki-roots", ] [[package]] @@ -2084,9 +1997,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.2.0", - "http-body 1.0.1", - "hyper 1.5.1", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", @@ -2102,7 +2015,7 @@ checksum = "986c5ce3b994526b3cd75578e62554abd09f0899d6206de48b3e96ab34ccc8c7" dependencies = [ "hex", "http-body-util", - "hyper 1.5.1", + "hyper", "hyper-util", "pin-project-lite", "tokio", @@ -2254,13 +2167,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37221e690dcc5d0ea7c1f70decda6ae3495e72e8af06bca15e982193ffdf4fc4" dependencies = [ "async-trait", - "base64 0.22.1", + "base64", "bytes", - "http 1.2.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper", + "hyper-rustls", "hyper-util", "ring", "rustls-pki-types", @@ -2380,6 +2293,17 @@ dependencies = [ "cpufeatures", ] +[[package]] +name = "key-provider-client" +version = "0.3.3" +dependencies = [ + "anyhow", + "serde", + "serde_json", + "sodiumoxide", + "tokio", +] + [[package]] name = "kms" version = "0.3.3" @@ -2482,6 +2406,18 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libsodium-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b779387cd56adfbc02ea4a668e704f729be8d6a6abd2c27ca5ee537849a92fd" +dependencies = [ + "cc", + "libc", + "pkg-config", + "walkdir", +] + [[package]] name = "linked-hash-map" version = "0.5.6" @@ -2651,7 +2587,7 @@ dependencies = [ "bytes", "encoding_rs", "futures-util", - "http 1.2.0", + "http", "httparse", "log", "memchr", @@ -3077,7 +3013,7 @@ version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" dependencies = [ - "base64 0.22.1", + "base64", "serde", ] @@ -3190,6 +3126,12 @@ dependencies = [ "spki", ] +[[package]] +name = "pkg-config" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + [[package]] name = "polyval" version = "0.6.2" @@ -3427,7 +3369,7 @@ checksum = "e36dbacc22fa64d2059cc8df1e929ab9100804ae2011548b6679f923c1172f42" dependencies = [ "anyhow", "async-trait", - "derive_more 1.0.0", + "derive_more", "hex", "hex_fmt", "parity-scale-codec", @@ -3487,7 +3429,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash 2.0.0", - "rustls 0.23.19", + "rustls", "socket2", "thiserror 1.0.65", "tokio", @@ -3504,7 +3446,7 @@ dependencies = [ "rand 0.8.5", "ring", "rustc-hash 2.0.0", - "rustls 0.23.19", + "rustls", "slab", "thiserror 1.0.65", "tinyvec", @@ -3542,7 +3484,7 @@ dependencies = [ "bon", "prpc", "ra-tls", - "reqwest 0.12.9", + "reqwest", "rocket", "rocket-vsock-listener", "serde", @@ -3776,65 +3718,24 @@ version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" -[[package]] -name = "reqwest" -version = "0.11.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" -dependencies = [ - "base64 0.21.7", - "bytes", - "encoding_rs", - "futures-core", - "futures-util", - "h2 0.3.26", - "hickory-resolver", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-rustls 0.24.2", - "ipnet", - "js-sys", - "log", - "mime", - "once_cell", - "percent-encoding", - "pin-project-lite", - "rustls 0.21.12", - "rustls-pemfile 1.0.4", - "serde", - "serde_json", - "serde_urlencoded", - "sync_wrapper 0.1.2", - "system-configuration", - "tokio", - "tokio-rustls 0.24.1", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "webpki-roots 0.25.4", - "winreg", -] - [[package]] name = "reqwest" version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ - "base64 0.22.1", + "base64", "bytes", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "hickory-resolver", - "http 1.2.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", - "hyper 1.5.1", - "hyper-rustls 0.27.3", + "hyper", + "hyper-rustls", "hyper-util", "ipnet", "js-sys", @@ -3844,21 +3745,21 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.19", - "rustls-pemfile 2.1.3", + "rustls", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper 1.0.1", + "sync_wrapper", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 0.26.6", + "webpki-roots", "windows-registry", ] @@ -3951,8 +3852,8 @@ dependencies = [ "either", "figment", "futures", - "http 1.2.0", - "hyper 1.5.1", + "http", + "hyper", "hyper-util", "indexmap 2.5.0", "libc", @@ -3966,8 +3867,8 @@ dependencies = [ "ref-swap", "rocket_codegen", "rocket_http", - "rustls 0.23.19", - "rustls-pemfile 2.1.3", + "rustls", + "rustls-pemfile", "s2n-quic-h3", "serde", "serde_json", @@ -3977,7 +3878,7 @@ dependencies = [ "time", "tinyvec", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls", "tokio-stream", "tokio-util", "tracing", @@ -4001,7 +3902,7 @@ name = "rocket-vsock-listener" version = "0.3.3" dependencies = [ "anyhow", - "derive_more 1.0.0", + "derive_more", "pin-project", "rocket", "serde", @@ -4095,18 +3996,6 @@ dependencies = [ "windows-sys 0.59.0", ] -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", -] - [[package]] name = "rustls" version = "0.23.19" @@ -4118,7 +4007,7 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.8", + "rustls-webpki", "subtle", "zeroize", ] @@ -4130,28 +4019,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile", "rustls-pki-types", "schannel", "security-framework", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pemfile" version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425" dependencies = [ - "base64 0.22.1", + "base64", "rustls-pki-types", ] @@ -4161,16 +4041,6 @@ version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "16f1201b3c9a7ee8039bcadc17b7e605e2945b27eee7631788c1bd2b0643674b" -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted 0.9.0", -] - [[package]] name = "rustls-webpki" version = "0.102.8" @@ -4329,8 +4199,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c973d29e9b6669473f205b47d015480d7e79b103dfbd1c789184d9adc31af05" dependencies = [ "bytes", - "rustls 0.23.19", - "rustls-pemfile 2.1.3", + "rustls", + "rustls-pemfile", "s2n-codec 0.46.0", "s2n-quic-core 0.46.0", "s2n-quic-crypto", @@ -4374,27 +4244,27 @@ dependencies = [ [[package]] name = "scale-info" -version = "2.11.3" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eca070c12893629e2cc820a9761bedf6ce1dcddc9852984d1dc734b8bd9bd024" +checksum = "346a3b32eba2640d17a9cb5927056b08f3de90f65b72fe09402c2ad07d684d0b" dependencies = [ "bitvec", "cfg-if", - "derive_more 0.99.18", + "derive_more", "parity-scale-codec", "scale-info-derive", ] [[package]] name = "scale-info-derive" -version = "2.11.3" +version = "2.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d35494501194174bda522a32605929eefc9ecf7e0a326c26db1fdd85881eb62" +checksum = "c6630024bf739e2179b91fb424b28898baf819414262c5d376677dbff1fe7ebf" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 1.0.109", + "syn 2.0.87", ] [[package]] @@ -4437,16 +4307,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted 0.9.0", -] - [[package]] name = "sd-notify" version = "0.4.3" @@ -4597,7 +4457,7 @@ version = "3.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cecfa94848272156ea67b2b1a53f20fc7bc638c4a46d2f8abde08f05f4b857" dependencies = [ - "base64 0.22.1", + "base64", "chrono", "hex", "indexmap 1.9.3", @@ -4663,6 +4523,12 @@ dependencies = [ "libc", ] +[[package]] +name = "signature" +version = "1.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74233d3b3b2f6d4b006dc19dee745e73e2a6bfb6f93607cd3b02bd5b00797d7c" + [[package]] name = "signature" version = "2.2.0" @@ -4716,6 +4582,18 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "sodiumoxide" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e26be3acb6c2d9a7aac28482586a7856436af4cfe7100031d219de2d2ecb0028" +dependencies = [ + "ed25519", + "libc", + "libsodium-sys", + "serde", +] + [[package]] name = "spin" version = "0.9.8" @@ -4822,10 +4700,10 @@ dependencies = [ "clap", "fs-err", "futures", - "http 1.2.0", + "http", "http-body-util", "http-client", - "hyper 1.5.1", + "hyper", "hyper-util", "hyperlocal", "log", @@ -4858,12 +4736,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "sync_wrapper" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - [[package]] name = "sync_wrapper" version = "1.0.1" @@ -4940,7 +4812,7 @@ name = "tappd" version = "0.3.3" dependencies = [ "anyhow", - "base64 0.22.1", + "base64", "bollard", "chrono", "clap", @@ -4956,7 +4828,7 @@ dependencies = [ "ra-rpc", "ra-tls", "rcgen", - "reqwest 0.12.9", + "reqwest", "rinja", "rocket", "rocket-vsock-listener", @@ -5021,11 +4893,13 @@ dependencies = [ "clap", "cmd_lib", "curve25519-dalek", + "dcap-qvl", "fs-err", "getrandom 0.2.15", "hex", "hex_fmt", "host-api", + "key-provider-client", "kms-rpc", "parity-scale-codec", "ra-rpc", @@ -5060,6 +4934,7 @@ dependencies = [ "hex", "host-api", "humantime", + "key-provider-client", "kms-rpc", "load_config", "path-absolutize", @@ -5265,23 +5140,13 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.19", + "rustls", "rustls-pki-types", "tokio", ] @@ -5386,14 +5251,14 @@ dependencies = [ "rand 0.8.5", "rinja", "rocket", - "rustls 0.23.19", + "rustls", "safe-write", "serde", "serde_json", "shared_child", "smallvec", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls", "tproxy-rpc", "tracing", "tracing-subscriber", @@ -5751,12 +5616,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "webpki-roots" -version = "0.25.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" - [[package]] name = "webpki-roots" version = "0.26.6" diff --git a/Cargo.toml b/Cargo.toml index d6deb26..04818a7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,6 +32,7 @@ members = [ "host-api", "guest-api", "load_config", + "key-provider-client", ] resolver = "2" @@ -54,6 +55,7 @@ host-api = { path = "host-api", default-features = false } guest-api = { path = "guest-api", default-features = false } http-client = { path = "http-client", default-features = false } load_config = { path = "load_config" } +key-provider-client = { path = "key-provider-client" } # Core dependencies anyhow = "1.0.94" @@ -109,7 +111,7 @@ default-net = "0.22.0" # Cryptography/Security aes-gcm = "0.10.3" curve25519-dalek = "4.1.3" -dcap-qvl = "0.1.6" +dcap-qvl = "0.2.0" elliptic-curve = { version = "0.13.8", features = ["pkcs8"] } getrandom = "0.2.15" hkdf = "0.12.4" @@ -123,6 +125,7 @@ sha3 = "0.10.8" blake2 = "0.10.6" tokio-rustls = { version = "0.26.0", features = ["ring"] } x25519-dalek = { version = "2.0.1", features = ["static_secrets"] } +sodiumoxide = "0.2.7" # Certificate/DNS hickory-resolver = "0.24.1" diff --git a/host-api/proto/host_api.proto b/host-api/proto/host_api.proto index ca6fdb0..eb11e92 100644 --- a/host-api/proto/host_api.proto +++ b/host-api/proto/host_api.proto @@ -15,7 +15,17 @@ message Notification { string payload = 2; } +message GetSealingKeyRequest { + bytes quote = 1; +} + +message GetSealingKeyResponse { + bytes encrypted_key = 1; + bytes provider_quote = 2; +} + service HostApi { rpc Info(google.protobuf.Empty) returns (HostInfo); rpc Notify(Notification) returns (google.protobuf.Empty); + rpc GetSealingKey(GetSealingKeyRequest) returns (GetSealingKeyResponse); } diff --git a/key-provider-build/Dockerfile b/key-provider-build/Dockerfile index c599961..00fe66d 100644 --- a/key-provider-build/Dockerfile +++ b/key-provider-build/Dockerfile @@ -24,6 +24,7 @@ RUN apt update && apt install -y \ wget \ curl \ libclang-dev \ + rsyslog \ xorriso # Runtime dependencies @@ -34,13 +35,14 @@ RUN apt-get update && \ libsgx-aesm-quote-ex-plugin \ libsgx-aesm-ecdsa-plugin \ libsgx-dcap-quote-verify \ + libsgx-dcap-default-qpl \ psmisc # Install Rust RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y ENV PATH="/root/.cargo/bin:${PATH}" -RUN git clone https://github.com/MoeMahhouk/gramine-sealing-key-provider.git +RUN git clone -b patches https://github.com/kvinwang/gramine-sealing-key-provider WORKDIR /gramine-sealing-key-provider # Build gramine-sealing-key-provider binary diff --git a/key-provider-build/docker-compose.yaml b/key-provider-build/docker-compose.yaml index f829639..31fbe03 100644 --- a/key-provider-build/docker-compose.yaml +++ b/key-provider-build/docker-compose.yaml @@ -1,6 +1,7 @@ services: gramine-sealing-key-provider: container_name: gramine-sealing-key-provider + network_mode: host build: context: . dockerfile: Dockerfile @@ -9,7 +10,7 @@ services: - "/dev/sgx_enclave:/dev/sgx_enclave" - "/dev/sgx_provision:/dev/sgx_provision" volumes: - - ./:/workspace + - "/etc/sgx_default_qcnl.conf:/etc/sgx_default_qcnl.conf" environment: - SGX=1 ports: diff --git a/key-provider-build/entrypoint.sh b/key-provider-build/entrypoint.sh index a74ee64..8726468 100755 --- a/key-provider-build/entrypoint.sh +++ b/key-provider-build/entrypoint.sh @@ -1,5 +1,6 @@ #!/bin/sh -set -e -AESM_PATH=/opt/intel/sgx-aesm-service/aesm LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm /opt/intel/sgx-aesm-service/aesm/aesm_service --no-syslog +rsyslogd +AESM_PATH=/opt/intel/sgx-aesm-service/aesm LD_LIBRARY_PATH=/opt/intel/sgx-aesm-service/aesm /opt/intel/sgx-aesm-service/aesm/aesm_service + echo "Starting Gramine Sealing Key Provider" make SGX=1 run-provider diff --git a/key-provider-build/run.sh b/key-provider-build/run.sh index 511a5d7..592b7f3 100755 --- a/key-provider-build/run.sh +++ b/key-provider-build/run.sh @@ -1,2 +1,2 @@ #!/bin/sh -docker compose up --build \ No newline at end of file +docker compose up --build diff --git a/key-provider-client/Cargo.toml b/key-provider-client/Cargo.toml new file mode 100644 index 0000000..5e3f94b --- /dev/null +++ b/key-provider-client/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "key-provider-client" +version.workspace = true +authors.workspace = true +edition.workspace = true +license.workspace = true + +[dependencies] +anyhow.workspace = true +serde = { workspace = true, features = ["derive"] } +serde_json.workspace = true +sodiumoxide.workspace = true +tokio = { workspace = true, features = ["net", "io-util"] } diff --git a/key-provider-client/src/guest.rs b/key-provider-client/src/guest.rs new file mode 100644 index 0000000..4810076 --- /dev/null +++ b/key-provider-client/src/guest.rs @@ -0,0 +1,26 @@ +use sodiumoxide::crypto::box_::{self, PublicKey, SecretKey}; +use sodiumoxide::crypto::sealedbox; +use std::sync::OnceLock; + +pub use box_::PUBLICKEYBYTES; + +#[allow(clippy::result_unit_err)] +fn ensure_sodium_initialized() -> Result<(), ()> { + static SODIUM_INIT: OnceLock> = OnceLock::new(); + *SODIUM_INIT.get_or_init(sodiumoxide::init) +} + +pub fn generate_keypair() -> (PublicKey, SecretKey) { + ensure_sodium_initialized().expect("Failed to initialize sodium"); + box_::gen_keypair() +} + +#[allow(clippy::result_unit_err)] +pub fn open_sealed_box( + sealed_box: &[u8], + public_key: &PublicKey, + secret_key: &SecretKey, +) -> Result, ()> { + ensure_sodium_initialized().expect("Failed to initialize sodium"); + sealedbox::open(sealed_box, public_key, secret_key) +} diff --git a/key-provider-client/src/host.rs b/key-provider-client/src/host.rs new file mode 100644 index 0000000..a4a2f3f --- /dev/null +++ b/key-provider-client/src/host.rs @@ -0,0 +1,53 @@ +use anyhow::{bail, Context, Result}; +use serde::{Deserialize, Serialize}; +use std::net::IpAddr; +use tokio::{ + io::{AsyncReadExt, AsyncWriteExt}, + net::TcpStream, +}; + +#[derive(Serialize, Deserialize)] +struct QuoteRequest<'a> { + quote: &'a [u8], +} + +#[derive(Serialize, Deserialize, Debug)] +pub struct QuoteResponse { + pub encrypted_key: Vec, + pub provider_quote: Vec, +} + +pub async fn get_key(quote: Vec, address: IpAddr, port: u16) -> Result { + if quote.len() > 1024 * 1024 { + bail!("Quote is too long"); + } + let mut tcp_stream = TcpStream::connect((address, port)) + .await + .context("Failed to connect to key provider")?; + let payload = QuoteRequest { quote: "e }; + let serialized = serde_json::to_vec(&payload)?; + let length = serialized.len() as u32; + tcp_stream + .write_all(&length.to_be_bytes()) + .await + .context("Failed to write length")?; + tcp_stream + .write_all(&serialized) + .await + .context("Failed to write payload")?; + + let mut response_length = [0; 4]; + tcp_stream + .read_exact(&mut response_length) + .await + .context("Failed to read response length")?; + let response_length = u32::from_be_bytes(response_length); + let mut response = vec![0; response_length as usize]; + tcp_stream + .read_exact(&mut response) + .await + .context("Failed to read response")?; + let response: QuoteResponse = + serde_json::from_slice(&response).context("Failed to deserialize response")?; + Ok(response) +} diff --git a/key-provider-client/src/lib.rs b/key-provider-client/src/lib.rs new file mode 100644 index 0000000..6815c46 --- /dev/null +++ b/key-provider-client/src/lib.rs @@ -0,0 +1,2 @@ +pub mod guest; +pub mod host; diff --git a/ra-rpc/src/rocket_helper.rs b/ra-rpc/src/rocket_helper.rs index 223d604..6f0704d 100644 --- a/ra-rpc/src/rocket_helper.rs +++ b/ra-rpc/src/rocket_helper.rs @@ -138,7 +138,9 @@ impl<'r> FromRequest<'r> for &'r QuoteVerifier { type Error = (); async fn from_request(request: &'r Request<'_>) -> Outcome { - let state: &rocket::State = from_request!(request); + let Some(state) = rocket::State::::get(request.rocket()) else { + return Outcome::Error((Status::InternalServerError, ())); + }; Outcome::Success(state) } } diff --git a/ra-tls/src/kdf.rs b/ra-tls/src/kdf.rs index 311c571..7e333b8 100644 --- a/ra-tls/src/kdf.rs +++ b/ra-tls/src/kdf.rs @@ -35,8 +35,16 @@ pub fn derive_ecdsa_key_pair(from: &KeyPair, context_data: &[&[u8]]) -> Result Result { let derived_sk_bytes = - derive_ecdsa_key(&sk_bytes, context_data, 32).or(Err(anyhow!("failed to derive key")))?; + derive_ecdsa_key(sk_bytes, context_data, 32).or(Err(anyhow!("failed to derive key")))?; let derived_sk = p256::SecretKey::from_slice(&derived_sk_bytes) .context("failed to decode derived secret key")?; let derived_sk_der = derived_sk diff --git a/tdxctl/Cargo.toml b/tdxctl/Cargo.toml index b0ccdf4..d64a039 100644 --- a/tdxctl/Cargo.toml +++ b/tdxctl/Cargo.toml @@ -34,6 +34,8 @@ tdx-attest.workspace = true host-api = { workspace = true, features = ["client"] } cmd_lib.workspace = true toml.workspace = true +key-provider-client.workspace = true +dcap-qvl.workspace = true [dev-dependencies] rand.workspace = true diff --git a/tdxctl/src/fde_setup.rs b/tdxctl/src/fde_setup.rs index 8849f3f..0c13caf 100644 --- a/tdxctl/src/fde_setup.rs +++ b/tdxctl/src/fde_setup.rs @@ -3,10 +3,12 @@ use std::{ io::{Read, Write}, path::{Path, PathBuf}, process::{Command, Stdio}, + time::{Duration, SystemTime}, }; use anyhow::{anyhow, bail, Context, Result}; use fs_err as fs; +use key_provider_client::guest::PUBLICKEYBYTES; use kms_rpc::GetAppKeyRequest; use ra_rpc::client::RaClient; use serde::{Deserialize, Serialize}; @@ -15,10 +17,11 @@ use tracing::{info, warn}; use crate::{ cmd_gen_app_keys, cmd_gen_ra_cert, cmd_show, crypto::dh_decrypt, + gen_app_keys_from_seed, notify_client::NotifyClient, utils::{ deserialize_json_file, extend_rtmr3, sha256, sha256_file, AppCompose, AppKeys, HashingFile, - LocalConfig, + KeyProvider, LocalConfig, }, GenAppKeysArgs, GenRaCertArgs, }; @@ -181,44 +184,141 @@ impl SetupFdeArgs { HostShared::load(host_shared_copy_dir.as_path()) } - async fn request_app_keys(&self, host_shared: &HostShared) -> Result { - let kms_url = &host_shared.vm_config.kms_url; - let kms_enabled = host_shared.app_compose.kms_enabled(); - if kms_enabled { - let Some(kms_url) = kms_url else { - bail!("KMS URL is not set"); - }; - info!("KMS is enabled, generating RA-TLS cert"); - let gen_certs_dir = self.work_dir.join("certs"); - fs::create_dir_all(&gen_certs_dir).context("Failed to create certs dir")?; - cmd_gen_ra_cert(GenRaCertArgs { - ca_cert: host_shared.dir.tmp_ca_cert_file(), - ca_key: host_shared.dir.tmp_ca_key_file(), - cert_path: gen_certs_dir.join("cert.pem"), - key_path: gen_certs_dir.join("key.pem"), - })?; - info!("Requesting app keys from KMS: {kms_url}"); - let ra_client = RaClient::new_mtls( - format!("{kms_url}/prpc"), - fs::read_to_string(host_shared.dir.kms_ca_cert_file())?, - fs::read_to_string(gen_certs_dir.join("cert.pem"))?, - fs::read_to_string(gen_certs_dir.join("key.pem"))?, - )?; - let kms_client = kms_rpc::kms_client::KmsClient::new(ra_client); - let response = kms_client - .get_app_key(GetAppKeyRequest { upgradable: true }) + async fn request_app_keys_from_kms( + &self, + host_shared: &HostShared, + nc: &NotifyClient, + ) -> Result<()> { + let Some(kms_url) = &host_shared.vm_config.kms_url else { + bail!("KMS URL is not set"); + }; + info!("KMS is enabled, generating RA-TLS cert"); + let gen_certs_dir = self.work_dir.join("certs"); + fs::create_dir_all(&gen_certs_dir).context("Failed to create certs dir")?; + cmd_gen_ra_cert(GenRaCertArgs { + ca_cert: host_shared.dir.tmp_ca_cert_file(), + ca_key: host_shared.dir.tmp_ca_key_file(), + cert_path: gen_certs_dir.join("cert.pem"), + key_path: gen_certs_dir.join("key.pem"), + })?; + info!("Requesting app keys from KMS: {kms_url}"); + let ra_client = RaClient::new_mtls( + format!("{kms_url}/prpc"), + fs::read_to_string(host_shared.dir.kms_ca_cert_file())?, + fs::read_to_string(gen_certs_dir.join("cert.pem"))?, + fs::read_to_string(gen_certs_dir.join("key.pem"))?, + )?; + let kms_client = kms_rpc::kms_client::KmsClient::new(ra_client); + let response = kms_client + .get_app_key(GetAppKeyRequest { upgradable: true }) + .await + .context("Failed to get app key")?; + let keys_json = serde_json::to_string(&response).context("Failed to serialize app keys")?; + fs::write(self.app_keys_file(), keys_json).context("Failed to write app keys")?; + extend_rtmr3("key-provider", b"{\"name\": \"kms\"}")?; + Ok(()) + } + + async fn request_app_keys_from_local_key_provider( + &self, + host_shared: &HostShared, + nc: &NotifyClient, + ) -> Result<()> { + use key_provider_client::guest::{generate_keypair, open_sealed_box}; + + let Some(pccs_url) = &host_shared.vm_config.pccs_url else { + bail!("PCCS URL is not set"); + }; + + let (pk, sk) = generate_keypair(); + let provision = { + info!("Local key provider is enabled, requesting app keys from local key provider"); + + let mut report_data = [0u8; 64]; + report_data[..PUBLICKEYBYTES].copy_from_slice(&pk.0); + let (_, quote) = + tdx_attest::get_quote(&report_data, None).context("Failed to get quote")?; + nc.get_sealing_key("e) .await - .context("Failed to get app key")?; + .context("Failed to get sealing key")? + }; + + let mr = { + // verify the key provider quote + let now = SystemTime::now() + .duration_since(SystemTime::UNIX_EPOCH) + .context("Invalid system time")? + .as_secs(); + let quote_collateral = dcap_qvl::collateral::get_collateral( + pccs_url, + &provision.provider_quote, + Duration::from_secs(10), + ) + .await + .context("Failed to get quote collateral")?; + let verified_report = + dcap_qvl::verify::verify(&provision.provider_quote, "e_collateral, now) + .ok() + .context("Failed to verify key provider quote")?; + let mr = verified_report + .report + .as_sgx() + .map(|r| r.mr_enclave) + .context("Failed to get enclave MR")?; + hex::encode(mr) + }; + + { + // write to fs + let sealing_key = open_sealed_box(&provision.encrypted_key, &pk, &sk) + .ok() + .context("Failed to open sealing key")?; + if sealing_key.len() < 32 { + bail!("Invalid sealing key length"); + } + info!("Key length: {}", sealing_key.len()); + info!("Key hash: {}", hex::encode(sha256(&sealing_key))); + let app_keys = + gen_app_keys_from_seed(&sealing_key).context("Failed to generate app keys")?; let keys_json = - serde_json::to_string(&response).context("Failed to serialize app keys")?; + serde_json::to_string(&app_keys).context("Failed to serialize app keys")?; fs::write(self.app_keys_file(), keys_json).context("Failed to write app keys")?; - } else { - info!("KMS is not enabled, generating local app keys"); - cmd_gen_app_keys(GenAppKeysArgs { - ca_level: 1, - output: self.app_keys_file(), - })?; } + + { + // write to RTMR + let provider_info = serde_json::json!({ + "name": "gramine-sealing-key-provider", + "mr": mr, + }); + let provider_info_json = serde_json::to_vec(&provider_info)?; + extend_rtmr3("key-provider", &provider_info_json)?; + } + Ok(()) + } + + async fn request_app_keys( + &self, + host_shared: &HostShared, + nc: &NotifyClient, + ) -> Result { + let key_provider = host_shared.app_compose.key_provider(); + match key_provider { + KeyProvider::Kms => self.request_app_keys_from_kms(host_shared, nc).await?, + KeyProvider::Local => { + self.request_app_keys_from_local_key_provider(host_shared, nc) + .await? + } + KeyProvider::None => { + info!("No key provider is enabled, generating temporary app keys"); + cmd_gen_app_keys(GenAppKeysArgs { + ca_level: 1, + output: self.app_keys_file(), + })?; + extend_rtmr3("key-provider", b"{\"name\": \"none\"}")?; + } + } + deserialize_json_file(self.app_keys_file()).context("Failed to decode app keys") } @@ -400,12 +500,13 @@ impl SetupFdeArgs { Ok(()) } - async fn setup_rootfs(&self, nc: &NotifyClient, host_shared: &HostShared) -> Result<()> { + async fn setup_rootfs(&self, host_shared: &HostShared, nc: &NotifyClient) -> Result<()> { nc.notify_q("boot.progress", "loading host-shared").await; let rootfs_hash = &host_shared.vm_config.rootfs_hash; let compose_hash = sha256_file(host_shared.dir.app_compose_file())?; let truncated_compose_hash = truncate(&compose_hash, 20); let kms_enabled = host_shared.app_compose.kms_enabled(); + let key_provider = host_shared.app_compose.key_provider(); let ca_cert_hash = if kms_enabled { sha256_file(host_shared.dir.kms_ca_cert_file())? } else { @@ -419,7 +520,7 @@ impl SetupFdeArgs { instance_info.app_id = truncated_compose_hash.to_vec(); } - let disk_reusable = kms_enabled || !self.rootfs_encryption; + let disk_reusable = (!key_provider.is_none()) || !self.rootfs_encryption; if (!disk_reusable) || instance_info.instance_id.is_empty() { instance_info.instance_id = { let mut rand_id = vec![0u8; 20]; @@ -445,7 +546,7 @@ impl SetupFdeArgs { nc.notify_q("boot.progress", "requesting app keys").await; - let app_keys = self.request_app_keys(host_shared).await?; + let app_keys = self.request_app_keys(host_shared, nc).await?; if app_keys.disk_crypt_key.is_empty() { bail!("Failed to get valid key phrase from KMS"); } @@ -463,6 +564,7 @@ impl SetupFdeArgs { .await?; } self.write_decrypted_env(&decrypted_env)?; + extend_rtmr3("system-ready", &[])?; nc.notify_q("boot.progress", "rootfs ready").await; Ok(()) } @@ -471,7 +573,7 @@ impl SetupFdeArgs { pub async fn cmd_setup_fde(args: SetupFdeArgs) -> Result<()> { let host_shared = args.copy_host_shared()?; let nc = NotifyClient::new(host_shared.vm_config.host_api_url.clone()); - match args.setup_rootfs(&nc, &host_shared).await { + match args.setup_rootfs(&host_shared, &nc).await { Ok(_) => Ok(()), Err(err) => { nc.notify_q("boot.error", &format!("{err:?}")).await; diff --git a/tdxctl/src/main.rs b/tdxctl/src/main.rs index 7a2f960..2e7712b 100644 --- a/tdxctl/src/main.rs +++ b/tdxctl/src/main.rs @@ -5,7 +5,10 @@ use fde_setup::{cmd_setup_fde, SetupFdeArgs}; use fs_err as fs; use getrandom::getrandom; use notify_client::NotifyClient; -use ra_tls::{attestation::QuoteContentType, cert::CaCert}; +use ra_tls::{ + attestation::QuoteContentType, cert::CaCert, kdf::derive_ecdsa_key_pair_from_bytes, + rcgen::KeyPair, +}; use scale::Decode; use std::{ io::{self, Read, Write}, @@ -14,7 +17,7 @@ use std::{ use tboot::TbootArgs; use tdx_attest as att; use tracing::error; -use utils::extend_rtmr; +use utils::{extend_rtmr, AppKeys}; mod crypto; mod fde_setup; @@ -339,12 +342,25 @@ fn cmd_gen_ca_cert(args: GenCaCertArgs) -> Result<()> { } fn cmd_gen_app_keys(args: GenAppKeysArgs) -> Result<()> { - use ra_tls::cert::CertRequest; use ra_tls::rcgen::{KeyPair, PKCS_ECDSA_P256_SHA256}; let key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?; let disk_key = KeyPair::generate_for(&PKCS_ECDSA_P256_SHA256)?; - let pubkey = key.public_key_der(); + let app_keys = make_app_keys(key, disk_key, args.ca_level)?; + let app_keys = serde_json::to_string(&app_keys).context("Failed to serialize app keys")?; + fs::write(&args.output, app_keys).context("Failed to write app keys")?; + Ok(()) +} + +fn gen_app_keys_from_seed(seed: &[u8]) -> Result { + let key = derive_ecdsa_key_pair_from_bytes(seed, &["app-key".as_bytes()])?; + let disk_key = derive_ecdsa_key_pair_from_bytes(seed, &["app-disk-key".as_bytes()])?; + make_app_keys(key, disk_key, 1) +} + +fn make_app_keys(app_key: KeyPair, disk_key: KeyPair, ca_level: u8) -> Result { + use ra_tls::cert::CertRequest; + let pubkey = app_key.public_key_der(); let report_data = QuoteContentType::RaTlsCert.to_report_data(&pubkey); let (_, quote) = att::get_quote(&report_data, None).context("Failed to get quote")?; let event_logs = att::eventlog::read_event_logs().context("Failed to read event logs")?; @@ -353,21 +369,19 @@ fn cmd_gen_app_keys(args: GenAppKeysArgs) -> Result<()> { .subject("App Root Cert") .quote("e) .event_log(&event_log) - .key(&key) - .ca_level(args.ca_level) + .key(&app_key) + .ca_level(ca_level) .build(); let cert = req .self_signed() .context("Failed to self-sign certificate")?; - let app_keys = serde_json::json!({ - "app_key": key.serialize_pem(), - "disk_crypt_key": sha256(&disk_key.serialize_der()), - "certificate_chain": vec![cert.pem()], - }); - let app_keys = serde_json::to_string(&app_keys).context("Failed to serialize app keys")?; - fs::write(&args.output, app_keys).context("Failed to write app keys")?; - Ok(()) + Ok(AppKeys { + app_key: app_key.serialize_pem(), + disk_crypt_key: sha256(&disk_key.serialize_der()), + certificate_chain: vec![cert.pem()], + env_crypt_key: vec![], + }) } async fn cmd_notify_host(args: HostNotifyArgs) -> Result<()> { diff --git a/tdxctl/src/notify_client.rs b/tdxctl/src/notify_client.rs index ae2d227..7a8e633 100644 --- a/tdxctl/src/notify_client.rs +++ b/tdxctl/src/notify_client.rs @@ -1,5 +1,5 @@ use crate::utils::{deserialize_json_file, LocalConfig}; -use anyhow::Result; +use anyhow::{anyhow, Result}; use host_api::{ client::{new_client, DefaultClient}, Notification, @@ -49,4 +49,13 @@ impl NotifyClient { warn!("Failed to notify event {event} to host: {:?}", err); } } + + pub async fn get_sealing_key(&self, quote: &[u8]) -> Result { + self.client + .get_sealing_key(host_api::GetSealingKeyRequest { + quote: quote.to_vec(), + }) + .await + .map_err(|err| anyhow!("Failed to get sealing key: {err:?}")) + } } diff --git a/tdxctl/src/utils.rs b/tdxctl/src/utils.rs index 5d1f558..5db110e 100644 --- a/tdxctl/src/utils.rs +++ b/tdxctl/src/utils.rs @@ -5,7 +5,7 @@ use std::{ use anyhow::{Context, Result}; use fs_err as fs; -use serde::{de::DeserializeOwned, Deserialize}; +use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_human_bytes as hex_bytes; use sha2::{digest::Output, Digest}; use tdx_attest as att; @@ -92,6 +92,24 @@ pub struct AppCompose { pub kms_enabled: bool, #[serde(default)] pub tproxy_enabled: bool, + #[serde(default)] + pub local_key_provider_enabled: bool, + #[serde(default)] + key_provider: Option, +} + +#[derive(Deserialize, Debug, Clone, Copy)] +#[serde(rename_all = "snake_case")] +pub enum KeyProvider { + None, + Kms, + Local, +} + +impl KeyProvider { + pub fn is_none(&self) -> bool { + matches!(self, KeyProvider::None) + } } #[derive(Deserialize, Debug, Default)] @@ -116,6 +134,21 @@ impl AppCompose { pub fn kms_enabled(&self) -> bool { self.kms_enabled || self.feature_enabled("kms") } + + pub fn key_provider(&self) -> KeyProvider { + match self.key_provider { + Some(p) => p, + None => { + if self.local_key_provider_enabled { + KeyProvider::Local + } else if self.kms_enabled { + KeyProvider::Kms + } else { + KeyProvider::None + } + } + } + } } #[derive(Deserialize)] @@ -124,11 +157,12 @@ pub struct LocalConfig { pub rootfs_hash: Vec, pub kms_url: Option, pub tproxy_url: Option, + pub pccs_url: Option, pub docker_registry: Option, pub host_api_url: String, } -#[derive(Deserialize)] +#[derive(Serialize, Deserialize)] pub struct AppKeys { pub app_key: String, pub disk_crypt_key: String, diff --git a/teepod/Cargo.toml b/teepod/Cargo.toml index 23193e1..6854bf5 100644 --- a/teepod/Cargo.toml +++ b/teepod/Cargo.toml @@ -38,3 +38,4 @@ host-api.workspace = true safe-write.workspace = true guest-api = { workspace = true, features = ["client"] } load_config.workspace = true +key-provider-client.workspace = true diff --git a/teepod/src/app.rs b/teepod/src/app.rs index 9f315ff..d0fcf85 100644 --- a/teepod/src/app.rs +++ b/teepod/src/app.rs @@ -361,6 +361,7 @@ impl App { "rootfs_hash": rootfs_hash, "kms_url": cfg.cvm.kms_url, "tproxy_url": cfg.cvm.tproxy_url, + "pccs_url": cfg.cvm.pccs_url, "docker_registry": cfg.cvm.docker_registry, "host_api_url": format!("vsock://2:{}/api", cfg.host_api.port), }); diff --git a/teepod/src/config.rs b/teepod/src/config.rs index b522703..91aec38 100644 --- a/teepod/src/config.rs +++ b/teepod/src/config.rs @@ -79,6 +79,8 @@ pub struct CvmConfig { pub kms_url: String, /// The URL of the TProxy server pub tproxy_url: String, + /// The URL of the PCCS server + pub pccs_url: String, /// The URL of the Docker registry pub docker_registry: String, /// The maximum disk size in GB @@ -144,6 +146,9 @@ pub struct Config { /// Host API configuration pub host_api: HostApiConfig, + + /// Key provider configuration + pub key_provider: KeyProviderConfig, } impl Config { @@ -181,6 +186,13 @@ pub struct HostApiConfig { pub port: u32, } +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct KeyProviderConfig { + pub enabled: bool, + pub address: IpAddr, + pub port: u16, +} + impl Config { pub fn extract_or_default(figment: &Figment) -> Result { let mut me: Self = figment.extract()?; diff --git a/teepod/src/console.html b/teepod/src/console.html index 62491a0..1ae9d6b 100644 --- a/teepod/src/console.html +++ b/teepod/src/console.html @@ -549,6 +549,10 @@

Deploy a new instance

KMS +