From cc4826ab36f3c99797bdd11a97757135006e8f35 Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Thu, 18 Jan 2024 22:39:18 +0000 Subject: [PATCH 01/42] Increase timeout for oak functions tests (#4680) We should find a better solution, but this at least lets us get past #4677 --- oak_functions/examples/key_value_lookup/module/src/tests.rs | 3 ++- oak_functions_containers_launcher/src/lib.rs | 6 +++++- oak_functions_launcher/src/lib.rs | 6 +++++- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/oak_functions/examples/key_value_lookup/module/src/tests.rs b/oak_functions/examples/key_value_lookup/module/src/tests.rs index bde9a02881d..db24d9265fd 100644 --- a/oak_functions/examples/key_value_lookup/module/src/tests.rs +++ b/oak_functions/examples/key_value_lookup/module/src/tests.rs @@ -47,7 +47,8 @@ async fn test_server() { .await; // Wait for the server to start up. - std::thread::sleep(Duration::from_secs(10)); + // TODO(#4677): Reduce the wait time. + std::thread::sleep(Duration::from_secs(15)); { // Lookup match. diff --git a/oak_functions_containers_launcher/src/lib.rs b/oak_functions_containers_launcher/src/lib.rs index 217ffb07106..fa84f26e2d1 100644 --- a/oak_functions_containers_launcher/src/lib.rs +++ b/oak_functions_containers_launcher/src/lib.rs @@ -115,5 +115,9 @@ async fn update_lookup_data( config: &LookupDataConfig, ) -> anyhow::Result<()> { log::info!("updating lookup data"); - lookup::update_lookup_data(client, &config.lookup_data_path, config.max_chunk_size).await + let start = std::time::Instant::now(); + let result = + lookup::update_lookup_data(client, &config.lookup_data_path, config.max_chunk_size).await; + log::info!("updated lookup data in {}ms", start.elapsed().as_millis()); + result } diff --git a/oak_functions_launcher/src/lib.rs b/oak_functions_launcher/src/lib.rs index 61a5a9acc55..155c770a600 100644 --- a/oak_functions_launcher/src/lib.rs +++ b/oak_functions_launcher/src/lib.rs @@ -156,7 +156,11 @@ pub async fn update_lookup_data( config: &LookupDataConfig, ) -> anyhow::Result<()> { log::info!("updating lookup data"); - lookup::update_lookup_data(client, &config.lookup_data_path, config.max_chunk_size).await + let start = std::time::Instant::now(); + let result = + lookup::update_lookup_data(client, &config.lookup_data_path, config.max_chunk_size).await; + log::info!("updated lookup data in {}ms", start.elapsed().as_millis()); + result } // Loads application config (including Wasm bytes) into the enclave and returns a remote attestation From 0a97fc4a75fc305d0788953283b09653c4067765 Mon Sep 17 00:00:00 2001 From: Ivan Petrov Date: Thu, 18 Jan 2024 23:38:05 +0000 Subject: [PATCH 02/42] Update h2 crate to v0.3.24 (#4668) --- Cargo.lock | 38 ++++++++++++----------------- micro_rpc_workspace_test/Cargo.lock | 4 +-- 2 files changed, 17 insertions(+), 25 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 6f19fa8bed4..17c75c304e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,14 +64,13 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.7" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" dependencies = [ - "cfg-if", + "getrandom", "once_cell", "version_check", - "zerocopy", ] [[package]] @@ -83,12 +82,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "allocator-api2" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" - [[package]] name = "aml" version = "0.16.4" @@ -1207,9 +1200,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.23" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b553656127a00601c8ae5590fcfdc118e4083a7924b6cf4ffc1ea4b99dc429d7" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", @@ -1235,16 +1228,15 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -dependencies = [ - "ahash", - "allocator-api2", -] [[package]] name = "heck" @@ -1521,7 +1513,7 @@ dependencies = [ name = "key_value_lookup" version = "0.1.0" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.12.3", "http", "log", "maplit", @@ -2156,7 +2148,7 @@ name = "oak_echo_service" version = "0.1.0" dependencies = [ "async-trait", - "hashbrown 0.14.3", + "hashbrown 0.12.3", "log", "micro_rpc", "micro_rpc_build", @@ -2269,7 +2261,7 @@ dependencies = [ "command-fds", "env_logger", "futures", - "hashbrown 0.14.3", + "hashbrown 0.12.3", "log", "micro_rpc", "micro_rpc_build", @@ -2307,7 +2299,7 @@ dependencies = [ name = "oak_functions_sdk" version = "0.1.0" dependencies = [ - "hashbrown 0.14.3", + "hashbrown 0.12.3", "lazy_static", "micro_rpc", "micro_rpc_build", @@ -2346,7 +2338,7 @@ dependencies = [ "byteorder", "bytes", "env_logger", - "hashbrown 0.14.3", + "hashbrown 0.12.3", "log", "micro_rpc", "micro_rpc_build", @@ -2413,7 +2405,7 @@ dependencies = [ "bmrng", "clap", "command-fds", - "hashbrown 0.14.3", + "hashbrown 0.12.3", "log", "micro_rpc", "micro_rpc_build", @@ -3199,7 +3191,7 @@ name = "rust-hypervisor-firmware-virtio" version = "0.1.0" dependencies = [ "atomic_refcell", - "bitflags 1.3.2", + "bitflags 2.4.1", "log", "x86_64", ] diff --git a/micro_rpc_workspace_test/Cargo.lock b/micro_rpc_workspace_test/Cargo.lock index 0e47a289952..6b0e6694cbd 100644 --- a/micro_rpc_workspace_test/Cargo.lock +++ b/micro_rpc_workspace_test/Cargo.lock @@ -578,9 +578,9 @@ dependencies = [ [[package]] name = "h2" -version = "0.3.22" +version = "0.3.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" +checksum = "bb2c4422095b67ee78da96fbb51a4cc413b3b25883c7717ff7ca1ab31022c9c9" dependencies = [ "bytes", "fnv", From 547103ba830f795744733128e07a39fcff75f3c7 Mon Sep 17 00:00:00 2001 From: Brett McLarnon Date: Thu, 18 Jan 2024 17:41:40 -0800 Subject: [PATCH 03/42] Add micro_rpc_build support for external paths. (#4681) This mirrors the similar feature in the `oak_grpc_utils` crate. Fixes #4679 --- micro_rpc_build/src/lib.rs | 21 +++++++++++++++++++++ oak_functions_service/build.rs | 1 + 2 files changed, 22 insertions(+) diff --git a/micro_rpc_build/src/lib.rs b/micro_rpc_build/src/lib.rs index 7588aae67c5..872e5d54164 100644 --- a/micro_rpc_build/src/lib.rs +++ b/micro_rpc_build/src/lib.rs @@ -49,6 +49,24 @@ pub struct CompileOptions { /// List of `bytes` fields that will use `bytes::Bytes` instead of `Vec` pub bytes: Vec, + + /// Specifies externally provided Protobuf packages or types. + pub extern_paths: Vec, +} + +#[derive(Default, Clone)] +pub struct ExternPath { + proto_path: String, + rust_path: String, +} + +impl ExternPath { + pub fn new(proto_path: &str, rust_path: &str) -> Self { + ExternPath { + proto_path: proto_path.to_string(), + rust_path: rust_path.to_string(), + } + } } /// Compile Rust server code from the services in the provided protobuf file. @@ -84,6 +102,9 @@ pub fn compile( config.service_generator(Box::new(ServiceGenerator { options: options.clone(), })); + for extern_path in options.extern_paths { + config.extern_path(extern_path.proto_path, extern_path.rust_path); + } config // Use BTreeMap to allow using this function in no-std crates. .btree_map(["."]) diff --git a/oak_functions_service/build.rs b/oak_functions_service/build.rs index 5e18808037f..d6d9df44aaf 100644 --- a/oak_functions_service/build.rs +++ b/oak_functions_service/build.rs @@ -23,6 +23,7 @@ fn main() { micro_rpc_build::CompileOptions { receiver_type: ReceiverType::RefSelf, bytes: vec![".oak.functions.LookupDataEntry".to_string()], + ..Default::default() }, ); } From 9bcda0bf639f7d8b4fb002998bb3eb5cc030b509 Mon Sep 17 00:00:00 2001 From: Brett McLarnon Date: Thu, 18 Jan 2024 20:30:32 -0800 Subject: [PATCH 04/42] Add bazel rule for converting OCI images to OCI runtime bundles (#4619) With this rule (+ rules_oci), it's possible to build deterministic/reproducible runtime bundles entirely using bazel. The new rule only supports x86_64 Linux because it uses a pre-compiled umoci binary; if other build environments are required later, additional toolchains can be added. To demonstrate the new rule, a C++ implementation of the Oak Containers Hello World trusted app has been added. This implementation matches the existing Rust implementation and is verified by the existing Rust Hello World integration test. Note that `oak_containers_launcher::Args` member visibility needed to be changed so that the unit test could specify a container bundle path. --- Cargo.lock | 1 + WORKSPACE | 57 ++++++++++++ bazel/BUILD | 17 ++++ bazel/defs.bzl | 20 +++++ bazel/private/BUILD | 19 ++++ bazel/private/oci_runtime_bundle.bzl | 79 ++++++++++++++++ bazel/private/oci_runtime_bundle.sh.tpl | 39 ++++++++ bazel/repositories.bzl | 23 +++++ bazel/tools/umoci/BUILD | 22 +++++ bazel/tools/umoci/BUILD.tpl | 34 +++++++ bazel/tools/umoci/umoci_toolchain.bzl | 81 +++++++++++++++++ cc/containers/hello_world_trusted_app/BUILD | 89 +++++++++++++++++++ .../hello_world_trusted_app/README.md | 12 +++ .../hello_world_trusted_app/app_service.cc | 64 +++++++++++++ .../hello_world_trusted_app/app_service.h | 48 ++++++++++ cc/containers/hello_world_trusted_app/main.cc | 47 ++++++++++ .../orchestrator_client.cc | 82 +++++++++++++++++ .../orchestrator_client.h | 54 +++++++++++ cc/transport/grpc_streaming_transport_test.cc | 5 +- flake.nix | 1 + justfile | 5 +- oak_containers/proto/BUILD | 48 ++++++++++ .../proto/BUILD | 44 +++++++++ .../Cargo.toml | 1 + .../tests/integration_test.rs | 53 ++++++++--- oak_containers_launcher/src/lib.rs | 8 +- proto/containers/BUILD | 14 +++ 27 files changed, 945 insertions(+), 22 deletions(-) create mode 100644 bazel/BUILD create mode 100644 bazel/defs.bzl create mode 100644 bazel/private/BUILD create mode 100644 bazel/private/oci_runtime_bundle.bzl create mode 100644 bazel/private/oci_runtime_bundle.sh.tpl create mode 100644 bazel/repositories.bzl create mode 100644 bazel/tools/umoci/BUILD create mode 100644 bazel/tools/umoci/BUILD.tpl create mode 100644 bazel/tools/umoci/umoci_toolchain.bzl create mode 100644 cc/containers/hello_world_trusted_app/BUILD create mode 100644 cc/containers/hello_world_trusted_app/README.md create mode 100644 cc/containers/hello_world_trusted_app/app_service.cc create mode 100644 cc/containers/hello_world_trusted_app/app_service.h create mode 100644 cc/containers/hello_world_trusted_app/main.cc create mode 100644 cc/containers/hello_world_trusted_app/orchestrator_client.cc create mode 100644 cc/containers/hello_world_trusted_app/orchestrator_client.h create mode 100644 oak_containers/proto/BUILD create mode 100644 oak_containers_hello_world_trusted_app/proto/BUILD diff --git a/Cargo.lock b/Cargo.lock index 17c75c304e4..7f8156af036 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1943,6 +1943,7 @@ dependencies = [ "oak_containers_launcher", "oak_crypto", "oak_grpc_utils", + "once_cell", "prost", "tokio", "tonic", diff --git a/WORKSPACE b/WORKSPACE index 60b356ef896..ec55c450875 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -194,3 +194,60 @@ git_repository( commit = "f43f9d61c201b314c62a3ebcf2d4a37f1a3b06f7", remote = "https://github.com/erenon/bazel_clang_tidy.git", ) + +# Bazel rules for building OCI images and runtime bundles. +http_archive( + name = "rules_oci", + sha256 = "686f871f9697e08877b85ea6c16c8d48f911bf466c3aeaf108ca0ab2603c7306", + strip_prefix = "rules_oci-1.5.1", + url = "https://github.com/bazel-contrib/rules_oci/releases/download/v1.5.1/rules_oci-v1.5.1.tar.gz", +) + +load("@rules_oci//oci:dependencies.bzl", "rules_oci_dependencies") + +rules_oci_dependencies() + +load("@rules_oci//oci:repositories.bzl", "LATEST_CRANE_VERSION", "LATEST_ZOT_VERSION", "oci_register_toolchains") + +oci_register_toolchains( + name = "oci", + crane_version = LATEST_CRANE_VERSION, + zot_version = LATEST_ZOT_VERSION, +) + +load("@rules_oci//oci:pull.bzl", "oci_pull") + +oci_pull( + name = "distroless_cc_debian12", + digest = "sha256:6714977f9f02632c31377650c15d89a7efaebf43bab0f37c712c30fc01edb973", + image = "gcr.io/distroless/cc-debian12", + platforms = ["linux/amd64"], +) + +load("@//bazel:repositories.bzl", "oak_toolchain_repositories") + +oak_toolchain_repositories() + +# Register a hermetic C++ toolchain to ensure that binaries use a glibc version supported by +# distroless images. The glibc version provided by nix may be too new. +# This (currently) needs to be loaded after rules_oci because it defaults to using an older version +# of aspect-build/bazel-lib. +http_archive( + name = "aspect_gcc_toolchain", + sha256 = "3341394b1376fb96a87ac3ca01c582f7f18e7dc5e16e8cf40880a31dd7ac0e1e", + strip_prefix = "gcc-toolchain-0.4.2", + urls = [ + "https://github.com/aspect-build/gcc-toolchain/archive/refs/tags/0.4.2.tar.gz", + ], +) + +load("@aspect_gcc_toolchain//toolchain:repositories.bzl", "gcc_toolchain_dependencies") + +gcc_toolchain_dependencies() + +load("@aspect_gcc_toolchain//toolchain:defs.bzl", "ARCHS", "gcc_register_toolchain") + +gcc_register_toolchain( + name = "gcc_toolchain_x86_64", + target_arch = ARCHS.x86_64, +) diff --git a/bazel/BUILD b/bazel/BUILD new file mode 100644 index 00000000000..2e6e6e45778 --- /dev/null +++ b/bazel/BUILD @@ -0,0 +1,17 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +package(licenses = ["notice"]) diff --git a/bazel/defs.bzl b/bazel/defs.bzl new file mode 100644 index 00000000000..634f1aac4d3 --- /dev/null +++ b/bazel/defs.bzl @@ -0,0 +1,20 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# +"""Forwarder for bazel rules and macros.""" + +load("//bazel/private:oci_runtime_bundle.bzl", _oci_runtime_bundle = "oci_runtime_bundle") + +oci_runtime_bundle = _oci_runtime_bundle diff --git a/bazel/private/BUILD b/bazel/private/BUILD new file mode 100644 index 00000000000..b6ea2bdac36 --- /dev/null +++ b/bazel/private/BUILD @@ -0,0 +1,19 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +package(licenses = ["notice"]) + +exports_files(glob(["*.tpl"])) diff --git a/bazel/private/oci_runtime_bundle.bzl b/bazel/private/oci_runtime_bundle.bzl new file mode 100644 index 00000000000..fb7e50a434a --- /dev/null +++ b/bazel/private/oci_runtime_bundle.bzl @@ -0,0 +1,79 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# +"""Implementation details for the oci_runtime_bundle macro.""" + +def _oci_runtime_bundle_impl(ctx): + image = ctx.file.image + bundle = ctx.outputs.bundle + umoci = ctx.toolchains["//bazel/tools/umoci:toolchain_type"].umociinfo.bin + yq = ctx.toolchains["@aspect_bazel_lib//lib:yq_toolchain_type"].yqinfo.bin + + # Since bazel doesn't support actions that create directory artifacts + # containing symlinks, we use a shell script so that a single action can + # unpack the container image and then re-pack it into a tar file. + executable = ctx.actions.declare_file("{}.tar.sh".format(ctx.label.name)) + ctx.actions.expand_template( + template = ctx.file._tpl, + output = executable, + is_executable = True, + substitutions = { + "{{umoci}}": umoci.path, + "{{yq}}": yq.path, + }, + ) + + ctx.actions.run( + executable = executable, + inputs = [image], + outputs = [bundle], + tools = [umoci, yq], + arguments = [image.path, bundle.path], + mnemonic = "OciRuntimeBundle", + progress_message = "Converting %{input} to an OCI runtime bundle", + ) + +_oci_runtime_bundle = rule( + implementation = _oci_runtime_bundle_impl, + attrs = { + "image": attr.label(allow_single_file = True), + "bundle": attr.output(), + "_tpl": attr.label( + allow_single_file = True, + default = ":oci_runtime_bundle.sh.tpl", + ), + }, + toolchains = [ + "//bazel/tools/umoci:toolchain_type", + "@aspect_bazel_lib//lib:yq_toolchain_type", + "@bazel_tools//tools/sh:toolchain_type", + ], +) + +def oci_runtime_bundle(name, image, **kwargs): + """Converts an oci_image to a OCI runtime bundle tar. + + Args: + name: the target name to produce. Building this target will generate a + "{name}.tar" output file. + image: the oci_image target to convert. + **kwargs: additional arguments passed to the rule (e.g., visibility). + """ + _oci_runtime_bundle( + name = name, + bundle = "{}.tar".format(name), + image = image, + **kwargs + ) diff --git a/bazel/private/oci_runtime_bundle.sh.tpl b/bazel/private/oci_runtime_bundle.sh.tpl new file mode 100644 index 00000000000..8eaf749f9c8 --- /dev/null +++ b/bazel/private/oci_runtime_bundle.sh.tpl @@ -0,0 +1,39 @@ +#!/usr/bin/env bash +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +set -eu + +readonly UMOCI="{{umoci}}" +readonly YQ="{{yq}}" + +# Add tags to the image index generated by oci_image so that it's compatible +# with umoci. +readonly IMAGE_DIR=$(mktemp -d) +cp -RL "$1/." "${IMAGE_DIR}" +"${YQ}" -i -o=json '.manifests[0].annotations["org.opencontainers.image.ref.name"] = "latest"' "${IMAGE_DIR}/index.json" + +# Use umoci to unpack the image into a runtime bundle. +readonly BUNDLE_DIR=$(mktemp -d) +"${UMOCI}" unpack --rootless --image "${IMAGE_DIR}" "${BUNDLE_DIR}" + +# Sort mount options, which umoci emits in a non-deterministic order. +"${YQ}" -i -o=json 'with(.mounts ; .[] | select(.options != null) | .options |= sort)' "${BUNDLE_DIR}/config.json" + +# Pack the runtime bundle into a (reproducible) tar, excluding unnecessary files +# added by umoci. +tar --create --sort=name --mtime="2000-01-01" --owner=0 --group=0 --numeric-owner \ + --file="$2" --directory="${BUNDLE_DIR}" --exclude=./umoci.json --exclude='./sha256_*.mtree' . diff --git a/bazel/repositories.bzl b/bazel/repositories.bzl new file mode 100644 index 00000000000..13957babf46 --- /dev/null +++ b/bazel/repositories.bzl @@ -0,0 +1,23 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# +"""Functions to load Oak dependencies.""" + +load("//bazel/tools/umoci:umoci_toolchain.bzl", "register_umoci_toolchain") + +# buildifier: disable=unnamed-macro +def oak_toolchain_repositories(oak_workspace_name = None): + """Downloads dependencies and registers toolchains used by Oak rules.""" + register_umoci_toolchain(name = "umoci_toolchain", oak_workspace_name = oak_workspace_name) diff --git a/bazel/tools/umoci/BUILD b/bazel/tools/umoci/BUILD new file mode 100644 index 00000000000..cd197046ad9 --- /dev/null +++ b/bazel/tools/umoci/BUILD @@ -0,0 +1,22 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +package(licenses = ["notice"]) + +toolchain_type( + name = "toolchain_type", + visibility = ["//visibility:public"], +) diff --git a/bazel/tools/umoci/BUILD.tpl b/bazel/tools/umoci/BUILD.tpl new file mode 100644 index 00000000000..cb97ef526c2 --- /dev/null +++ b/bazel/tools/umoci/BUILD.tpl @@ -0,0 +1,34 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +load("@{{oak_workspace_name}}//bazel/tools/umoci:umoci_toolchain.bzl", "umoci_toolchain") + +package(licenses = ["notice"]) + +umoci_toolchain( + name = "umoci", + bin = "umoci.amd64", +) + +toolchain( + name = "umoci_toolchain", + exec_compatible_with = [ + "@platforms//os:linux", + "@platforms//cpu:x86_64", + ], + toolchain = ":umoci", + toolchain_type = "@{{oak_workspace_name}}//bazel/tools/umoci:toolchain_type", +) diff --git a/bazel/tools/umoci/umoci_toolchain.bzl b/bazel/tools/umoci/umoci_toolchain.bzl new file mode 100644 index 00000000000..7c931dc7e8c --- /dev/null +++ b/bazel/tools/umoci/umoci_toolchain.bzl @@ -0,0 +1,81 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# +"""Defines a toolchain rule for umoci.""" + +UmociInfo = provider( + doc = "Information about how to invoke the umoci tool.", + fields = { + "bin": "Executable umoci binary", + }, +) + +def _umoci_toolchain_impl(ctx): + binary = ctx.file.bin + default_info = DefaultInfo( + files = depset([binary]), + runfiles = ctx.runfiles(files = [binary]), + ) + toolchain_info = platform_common.ToolchainInfo( + umociinfo = UmociInfo( + bin = binary, + ), + ) + return [default_info, toolchain_info] + +umoci_toolchain = rule( + implementation = _umoci_toolchain_impl, + attrs = { + "bin": attr.label( + allow_single_file = True, + executable = True, + mandatory = True, + cfg = "exec", + ), + }, +) + +def _umoci_toolchain_repo_impl(repository_ctx): + repository_ctx.download( + url = "https://github.com/opencontainers/umoci/releases/download/v0.4.7/umoci.amd64", + output = "umoci.amd64", + executable = True, + sha256 = "6abecdbe7ac96a8e48fdb73fb53f08d21d4dc5e040f7590d2ca5547b7f2b2e85", + ) + + repository_ctx.template( + "BUILD", + repository_ctx.attr._build_tpl, + substitutions = { + "{{oak_workspace_name}}": repository_ctx.attr.oak_workspace_name, + }, + executable = False, + ) + +_umoci_toolchain_repo = repository_rule( + implementation = _umoci_toolchain_repo_impl, + attrs = { + "oak_workspace_name": attr.string( + doc = "The name given to the oak repository, if the default was not used.", + default = "oak", + ), + "_build_tpl": attr.label(default = "//bazel/tools/umoci:BUILD.tpl"), + }, +) + +def register_umoci_toolchain(name, oak_workspace_name = None): + """Downloads dependencies and registers the umoci toolchain.""" + _umoci_toolchain_repo(name = name, oak_workspace_name = oak_workspace_name) + native.register_toolchains("@{}//:umoci_toolchain".format(name)) diff --git a/cc/containers/hello_world_trusted_app/BUILD b/cc/containers/hello_world_trusted_app/BUILD new file mode 100644 index 00000000000..3f3489c12d5 --- /dev/null +++ b/cc/containers/hello_world_trusted_app/BUILD @@ -0,0 +1,89 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +load("@rules_oci//oci:defs.bzl", "oci_image") +load("@rules_pkg//pkg:tar.bzl", "pkg_tar") +load("//bazel:defs.bzl", "oci_runtime_bundle") + +package(licenses = ["notice"]) + +cc_library( + name = "app_service", + srcs = ["app_service.cc"], + hdrs = ["app_service.h"], + deps = [ + ":orchestrator_client", + "//cc/crypto:common", + "//cc/crypto:server_encryptor", + "//oak_containers_hello_world_trusted_app/proto:interface_cc_grpc", + "//oak_containers_hello_world_trusted_app/proto:interface_cc_proto", + "//oak_crypto/proto/v1:crypto_cc_proto", + "@com_github_grpc_grpc//:grpc++", + "@com_google_absl//absl/strings", + ], +) + +cc_library( + name = "orchestrator_client", + srcs = ["orchestrator_client.cc"], + hdrs = ["orchestrator_client.h"], + deps = [ + "//cc/crypto:encryption_key_provider", + "//cc/crypto/hpke:recipient_context", + "//oak_containers/proto:interfaces_cc_grpc", + "//oak_containers/proto:interfaces_cc_proto", + "//oak_crypto/proto/v1:crypto_cc_proto", + "//proto/containers:orchestrator_crypto_cc_grpc", + "//proto/containers:orchestrator_crypto_cc_proto", + "@com_github_grpc_grpc//:grpc++", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_protobuf//:protobuf", + ], +) + +cc_binary( + name = "main", + srcs = ["main.cc"], + deps = [ + ":app_service", + ":orchestrator_client", + "@com_github_grpc_grpc//:grpc++", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/log:initialize", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + ], +) + +pkg_tar( + name = "tar", + srcs = [":main"], + package_dir = "/usr/local/bin", +) + +oci_image( + name = "image", + base = "@distroless_cc_debian12", + entrypoint = ["/usr/local/bin/main"], + tars = [":tar"], +) + +oci_runtime_bundle( + name = "bundle", + image = ":image", +) diff --git a/cc/containers/hello_world_trusted_app/README.md b/cc/containers/hello_world_trusted_app/README.md new file mode 100644 index 00000000000..061cf7c0eb9 --- /dev/null +++ b/cc/containers/hello_world_trusted_app/README.md @@ -0,0 +1,12 @@ + + + +

Project Oak Containers Logo

+ + +# C++ Hello World Trusted App + +C++ implementation of the trusted part (inside the TEE) of the Oak Containers +Hello World example application. This implementation parallels the Rust +implementation in +[`/oak_containers_hello_world_trusted_app`](../../../oak_containers_hello_world_trusted_app). diff --git a/cc/containers/hello_world_trusted_app/app_service.cc b/cc/containers/hello_world_trusted_app/app_service.cc new file mode 100644 index 00000000000..c594dcc5e8b --- /dev/null +++ b/cc/containers/hello_world_trusted_app/app_service.cc @@ -0,0 +1,64 @@ +/* + * Copyright 2024 The Project Oak Authors + * + * Licensed 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. + */ +#include "cc/containers/hello_world_trusted_app/app_service.h" + +#include + +#include "absl/strings/str_cat.h" +#include "absl/strings/string_view.h" +#include "cc/crypto/common.h" +#include "cc/crypto/server_encryptor.h" +#include "grpcpp/server_context.h" +#include "grpcpp/support/status.h" +#include "oak_containers_hello_world_trusted_app/proto/interface.grpc.pb.h" +#include "oak_containers_hello_world_trusted_app/proto/interface.pb.h" +#include "oak_crypto/proto/v1/crypto.pb.h" + +namespace oak::oak_containers_hello_world_trusted_app { + +using ::oak::containers::example::HelloRequest; +using ::oak::containers::example::HelloResponse; +using ::oak::crypto::DecryptionResult; +using ::oak::crypto::ServerEncryptor; +using ::oak::crypto::v1::EncryptedResponse; + +constexpr absl::string_view kEmptyAssociatedData = ""; + +grpc::Status TrustedApplicationImpl::Hello(grpc::ServerContext* context, + const HelloRequest* request, HelloResponse* response) { + ServerEncryptor server_encryptor(orchestrator_client_); + absl::StatusOr decrypted_request = + server_encryptor.Decrypt(request->encrypted_request()); + if (!decrypted_request.ok()) { + return grpc::Status(static_cast(decrypted_request.status().code()), + std::string(decrypted_request.status().message())); + } + + std::string greeting = absl::StrCat("Hello from the trusted side, ", decrypted_request->plaintext, + "! Btw, the Trusted App has a config with a length of ", + application_config_.size(), " bytes."); + absl::StatusOr encrypted_response = + server_encryptor.Encrypt(greeting, kEmptyAssociatedData); + if (!encrypted_response.ok()) { + return grpc::Status(static_cast(encrypted_response.status().code()), + std::string(encrypted_response.status().message())); + } + + *response->mutable_encrypted_response() = *std::move(encrypted_response); + return grpc::Status::OK; +} + +} // namespace oak::oak_containers_hello_world_trusted_app diff --git a/cc/containers/hello_world_trusted_app/app_service.h b/cc/containers/hello_world_trusted_app/app_service.h new file mode 100644 index 00000000000..59a90006b9a --- /dev/null +++ b/cc/containers/hello_world_trusted_app/app_service.h @@ -0,0 +1,48 @@ +/* + * Copyright 2024 The Project Oak Authors + * + * Licensed 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. + */ +#ifndef CC_OAK_CONTAINERS_HELLO_WORLD_TRUSTED_APP_APP_SERVICE_H_ +#define CC_OAK_CONTAINERS_HELLO_WORLD_TRUSTED_APP_APP_SERVICE_H_ + +#include + +#include "absl/log/die_if_null.h" +#include "absl/strings/string_view.h" +#include "cc/containers/hello_world_trusted_app/orchestrator_client.h" +#include "grpcpp/server_context.h" +#include "grpcpp/support/status.h" +#include "oak_containers_hello_world_trusted_app/proto/interface.grpc.pb.h" +#include "oak_containers_hello_world_trusted_app/proto/interface.pb.h" + +namespace oak::oak_containers_hello_world_trusted_app { + +class TrustedApplicationImpl : public containers::example::TrustedApplication::Service { + public: + TrustedApplicationImpl(OrchestratorClient* orchestrator_client, + absl::string_view application_config) + : orchestrator_client_(*ABSL_DIE_IF_NULL(orchestrator_client)), + application_config_(application_config) {} + + grpc::Status Hello(grpc::ServerContext* context, const containers::example::HelloRequest* request, + containers::example::HelloResponse* response) override; + + private: + OrchestratorClient& orchestrator_client_; + const std::string application_config_; +}; + +} // namespace oak::oak_containers_hello_world_trusted_app + +#endif // CC_OAK_CONTAINERS_HELLO_WORLD_TRUSTED_APP_APP_SERVICE_H_ diff --git a/cc/containers/hello_world_trusted_app/main.cc b/cc/containers/hello_world_trusted_app/main.cc new file mode 100644 index 00000000000..8af80a33239 --- /dev/null +++ b/cc/containers/hello_world_trusted_app/main.cc @@ -0,0 +1,47 @@ +/* + * Copyright 2024 The Project Oak Authors + * + * Licensed 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. + */ +#include + +#include "absl/log/check.h" +#include "absl/log/initialize.h" +#include "absl/status/statusor.h" +#include "app_service.h" +#include "cc/containers/hello_world_trusted_app/app_service.h" +#include "cc/containers/hello_world_trusted_app/orchestrator_client.h" +#include "grpcpp/security/server_credentials.h" +#include "grpcpp/server.h" +#include "grpcpp/server_builder.h" +#include "orchestrator_client.h" + +using ::oak::oak_containers_hello_world_trusted_app::OrchestratorClient; +using ::oak::oak_containers_hello_world_trusted_app::TrustedApplicationImpl; + +int main(int argc, char* argv[]) { + absl::InitializeLog(); + + OrchestratorClient client; + absl::StatusOr application_config = client.GetApplicationConfig(); + QCHECK_OK(application_config); + TrustedApplicationImpl service(&client, *application_config); + + grpc::ServerBuilder builder; + builder.AddListeningPort("[::]:8080", grpc::InsecureServerCredentials()); + builder.RegisterService(&service); + std::unique_ptr server(builder.BuildAndStart()); + QCHECK_OK(client.NotifyAppReady()); + server->Wait(); + return 0; +} diff --git a/cc/containers/hello_world_trusted_app/orchestrator_client.cc b/cc/containers/hello_world_trusted_app/orchestrator_client.cc new file mode 100644 index 00000000000..31752650547 --- /dev/null +++ b/cc/containers/hello_world_trusted_app/orchestrator_client.cc @@ -0,0 +1,82 @@ +/* + * Copyright 2024 The Project Oak Authors + * + * Licensed 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. + */ +#include "cc/containers/hello_world_trusted_app/orchestrator_client.h" + +#include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "cc/crypto/hpke/recipient_context.h" +#include "google/protobuf/empty.pb.h" +#include "grpcpp/channel.h" +#include "grpcpp/create_channel.h" +#include "grpcpp/security/credentials.h" +#include "grpcpp/support/status.h" +#include "oak_containers/proto/interfaces.grpc.pb.h" +#include "oak_containers/proto/interfaces.pb.h" +#include "oak_crypto/proto/v1/crypto.pb.h" +#include "proto/containers/orchestrator_crypto.grpc.pb.h" +#include "proto/containers/orchestrator_crypto.pb.h" + +namespace oak::oak_containers_hello_world_trusted_app { + +using ::oak::containers::GetApplicationConfigResponse; +using ::oak::containers::v1::DeriveSessionKeysRequest; +using ::oak::containers::v1::DeriveSessionKeysResponse; +using ::oak::crypto::RecipientContext; + +OrchestratorClient::OrchestratorClient() + : OrchestratorClient(grpc::CreateChannel("unix:/oak_utils/orchestrator_ipc", + grpc::InsecureChannelCredentials())) {} + +OrchestratorClient::OrchestratorClient(const std::shared_ptr& channel) + : stub_(containers::Orchestrator::NewStub(channel)), + crypto_stub_(containers::v1::OrchestratorCrypto::NewStub(channel)) {} + +absl::StatusOr OrchestratorClient::GetApplicationConfig() const { + grpc::ClientContext context; + GetApplicationConfigResponse response; + if (auto status = stub_->GetApplicationConfig(&context, {}, &response); !status.ok()) { + return absl::Status(static_cast(status.error_code()), status.error_message()); + } + return std::move(*response.mutable_config()); +} + +absl::Status OrchestratorClient::NotifyAppReady() const { + grpc::ClientContext context; + google::protobuf::Empty response; + if (auto status = stub_->NotifyAppReady(&context, {}, &response); !status.ok()) { + return absl::Status(static_cast(status.error_code()), status.error_message()); + } + return absl::OkStatus(); +} + +absl::StatusOr> OrchestratorClient::GenerateRecipientContext( + absl::string_view serialized_encapsulated_public_key) { + grpc::ClientContext context; + DeriveSessionKeysRequest request; + request.set_key_origin(containers::v1::KeyOrigin::INSTANCE); + request.set_serialized_encapsulated_public_key(serialized_encapsulated_public_key); + DeriveSessionKeysResponse response; + if (auto status = crypto_stub_->DeriveSessionKeys(&context, request, &response); !status.ok()) { + return absl::Status(static_cast(status.error_code()), status.error_message()); + } + return RecipientContext::Deserialize(response.session_keys()); +} + +} // namespace oak::oak_containers_hello_world_trusted_app diff --git a/cc/containers/hello_world_trusted_app/orchestrator_client.h b/cc/containers/hello_world_trusted_app/orchestrator_client.h new file mode 100644 index 00000000000..fcb748d820b --- /dev/null +++ b/cc/containers/hello_world_trusted_app/orchestrator_client.h @@ -0,0 +1,54 @@ +/* + * Copyright 2024 The Project Oak Authors + * + * Licensed 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. + */ +#ifndef CC_OAK_CONTAINERS_HELLO_WORLD_TRUSTED_APP_ORCHESTRATOR_CLIENT_H_ +#define CC_OAK_CONTAINERS_HELLO_WORLD_TRUSTED_APP_ORCHESTRATOR_CLIENT_H_ + +#include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "cc/crypto/encryption_key_provider.h" +#include "cc/crypto/hpke/recipient_context.h" +#include "grpcpp/channel.h" +#include "oak_containers/proto/interfaces.grpc.pb.h" +#include "oak_crypto/proto/v1/crypto.pb.h" +#include "proto/containers/orchestrator_crypto.grpc.pb.h" + +namespace oak::oak_containers_hello_world_trusted_app { + +class OrchestratorClient : public crypto::RecipientContextGenerator { + public: + OrchestratorClient(); + + absl::StatusOr GetApplicationConfig() const; + + absl::Status NotifyAppReady() const; + + absl::StatusOr> GenerateRecipientContext( + absl::string_view serialized_encapsulated_public_key) override; + + private: + explicit OrchestratorClient(const std::shared_ptr& channel); + + std::unique_ptr stub_; + std::unique_ptr crypto_stub_; +}; + +} // namespace oak::oak_containers_hello_world_trusted_app + +#endif // CC_OAK_CONTAINERS_HELLO_WORLD_TRUSTED_APP_ORCHESTRATOR_CLIENT_H_ diff --git a/cc/transport/grpc_streaming_transport_test.cc b/cc/transport/grpc_streaming_transport_test.cc index 95a671bd04e..ee0826e75a1 100644 --- a/cc/transport/grpc_streaming_transport_test.cc +++ b/cc/transport/grpc_streaming_transport_test.cc @@ -60,10 +60,7 @@ class GrpcStreamingTransportTest : public ::testing::Test { channel_ = server_->InProcessChannel({}); stub_ = oak::session::v1::StreamingSession::NewStub(channel_); - absl::Time absl_deadline = absl::Now() + absl::Seconds(10); - gpr_timespec deadline; - deadline.tv_sec = absl::ToInt64Seconds(absl::time_internal::ToUnixDuration(absl_deadline)); - context_.set_deadline(deadline); + context_.set_deadline(gpr_time_from_seconds(10, GPR_TIMESPAN)); } void TearDown() override { diff --git a/flake.nix b/flake.nix index 211a247bdd6..08420ac6842 100644 --- a/flake.nix +++ b/flake.nix @@ -152,6 +152,7 @@ containers = with pkgs; mkShell { inputsFrom = [ base + bazelShell rust ]; packages = [ diff --git a/justfile b/justfile index 8bba293df5d..144b07ad1d7 100644 --- a/justfile +++ b/justfile @@ -45,10 +45,13 @@ oak_containers_system_image: oak_containers_hello_world_container_bundle_tar: env --chdir=oak_containers_hello_world_container DOCKER_BUILDKIT=0 bash build_container_bundle +cc_oak_containers_hello_world_container_bundle_tar: + env bazel build -c opt //cc/containers/hello_world_trusted_app:bundle.tar + oak_containers_hello_world_untrusted_app: env cargo build --release --package='oak_containers_hello_world_untrusted_app' -all_oak_containers_binaries: stage0_bin stage1_cpio oak_containers_kernel oak_containers_system_image oak_containers_hello_world_container_bundle_tar oak_containers_hello_world_untrusted_app +all_oak_containers_binaries: stage0_bin stage1_cpio oak_containers_kernel oak_containers_system_image oak_containers_hello_world_container_bundle_tar cc_oak_containers_hello_world_container_bundle_tar oak_containers_hello_world_untrusted_app # Oak Functions Containers entry point. diff --git a/oak_containers/proto/BUILD b/oak_containers/proto/BUILD new file mode 100644 index 00000000000..46d5f78cf43 --- /dev/null +++ b/oak_containers/proto/BUILD @@ -0,0 +1,48 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") +load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], +) + +proto_library( + name = "interfaces_proto", + srcs = ["interfaces.proto"], + deps = [ + "//oak_crypto/proto/v1:crypto_proto", + "//proto/attestation:endorsement_proto", + "//proto/attestation:evidence_proto", + "//proto/session:messages_proto", + "@com_google_protobuf//:empty_proto", + ], +) + +cc_proto_library( + name = "interfaces_cc_proto", + deps = [":interfaces_proto"], +) + +cc_grpc_library( + name = "interfaces_cc_grpc", + srcs = [":interfaces_proto"], + grpc_only = True, + deps = [":interfaces_cc_proto"], +) diff --git a/oak_containers_hello_world_trusted_app/proto/BUILD b/oak_containers_hello_world_trusted_app/proto/BUILD new file mode 100644 index 00000000000..2ffd684d638 --- /dev/null +++ b/oak_containers_hello_world_trusted_app/proto/BUILD @@ -0,0 +1,44 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") +load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") + +package( + default_visibility = ["//cc/containers/hello_world_trusted_app:__subpackages__"], + licenses = ["notice"], +) + +proto_library( + name = "interface_proto", + srcs = ["interface.proto"], + deps = [ + "//oak_crypto/proto/v1:crypto_proto", + ], +) + +cc_proto_library( + name = "interface_cc_proto", + deps = [":interface_proto"], +) + +cc_grpc_library( + name = "interface_cc_grpc", + srcs = [":interface_proto"], + grpc_only = True, + deps = [":interface_cc_proto"], +) diff --git a/oak_containers_hello_world_untrusted_app/Cargo.toml b/oak_containers_hello_world_untrusted_app/Cargo.toml index 7939beccf7a..da510b99013 100644 --- a/oak_containers_hello_world_untrusted_app/Cargo.toml +++ b/oak_containers_hello_world_untrusted_app/Cargo.toml @@ -27,4 +27,5 @@ tower = "*" [dev-dependencies] duct = "*" +once_cell = "*" xtask = { workspace = true } diff --git a/oak_containers_hello_world_untrusted_app/tests/integration_test.rs b/oak_containers_hello_world_untrusted_app/tests/integration_test.rs index a87ac3fd7a4..146bd6c0ea9 100644 --- a/oak_containers_hello_world_untrusted_app/tests/integration_test.rs +++ b/oak_containers_hello_world_untrusted_app/tests/integration_test.rs @@ -19,19 +19,34 @@ use std::sync::Once; use oak_containers_launcher::Args; use oak_crypto::encryptor::ClientEncryptor; +use once_cell::sync::Lazy; const EMPTY_ASSOCIATED_DATA: &[u8] = b""; -static INIT: Once = Once::new(); +static INIT_LOGGING: Once = Once::new(); fn init_logging() { - INIT.call_once(|| { + INIT_LOGGING.call_once(|| { env_logger::init(); }); } -#[tokio::test(flavor = "multi_thread", worker_threads = 4)] -async fn hello_world() { +static INIT_DEPENDENCIES: Lazy> = Lazy::new(|| { + // This takes a long time, but we want to make sure everything it up to date. + // TODO(#4195): Stop dependencies from always being rebuilt. + duct::cmd!("just", "all_oak_containers_binaries",) + .dir(env!("WORKSPACE_ROOT")) + .run()?; + Ok(()) +}); + +fn init_dependencies() { + INIT_DEPENDENCIES + .as_ref() + .expect("couldn't build dependencies"); +} + +async fn run_hello_world_test(container_bundle: std::path::PathBuf) { init_logging(); if xtask::testing::skip_test() { @@ -39,12 +54,14 @@ async fn hello_world() { return; } - // This takes a long time, but we want to make sure everything it up to date. - // TODO(#4195): Stop dependencies from always being rebuilt. - build_dependencies().expect("couldn't build dependencies"); + init_dependencies(); + let app_args = Args { + container_bundle, + ..Args::default_for_test() + }; let mut untrusted_app = - oak_containers_hello_world_untrusted_app::UntrustedApp::create(Args::default_for_test()) + oak_containers_hello_world_untrusted_app::UntrustedApp::create(app_args) .await .expect("could not create untrusted app"); @@ -79,9 +96,19 @@ async fn hello_world() { assert_eq!(greeting, "Hello from the trusted side, fancy test! Btw, the Trusted App has a config with a length of 0 bytes."); } -fn build_dependencies() -> anyhow::Result<()> { - duct::cmd!("just", "all_oak_containers_binaries",) - .dir(env!("WORKSPACE_ROOT")) - .run()?; - Ok(()) +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn hello_world() { + run_hello_world_test(Args::default_for_test().container_bundle).await; +} + +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn cc_hello_world() { + run_hello_world_test( + format!( + "{}bazel-bin/cc/containers/hello_world_trusted_app/bundle.tar", + env!("WORKSPACE_ROOT") + ) + .into(), + ) + .await; } diff --git a/oak_containers_launcher/src/lib.rs b/oak_containers_launcher/src/lib.rs index e1d2ee95c91..c2d0ab7cc3c 100644 --- a/oak_containers_launcher/src/lib.rs +++ b/oak_containers_launcher/src/lib.rs @@ -74,13 +74,13 @@ const VM_START_TIMEOUT: u64 = 300; #[group(skip)] pub struct Args { #[arg(long, required = true, value_parser = path_exists,)] - system_image: std::path::PathBuf, + pub system_image: std::path::PathBuf, #[arg(long, required = true, value_parser = path_exists,)] - container_bundle: std::path::PathBuf, + pub container_bundle: std::path::PathBuf, #[arg(long, value_parser = path_exists,)] - application_config: Option, + pub application_config: Option, #[command(flatten)] - qemu_params: qemu::Params, + pub qemu_params: qemu::Params, } impl Args { diff --git a/proto/containers/BUILD b/proto/containers/BUILD index 3a61fa305e6..10b7f99bd3c 100644 --- a/proto/containers/BUILD +++ b/proto/containers/BUILD @@ -15,6 +15,8 @@ # load("@bazel_skylib//rules:build_test.bzl", "build_test") +load("@com_github_grpc_grpc//bazel:cc_grpc_library.bzl", "cc_grpc_library") +load("@rules_cc//cc:defs.bzl", "cc_proto_library") load("@rules_proto//proto:defs.bzl", "proto_library") package( @@ -30,6 +32,18 @@ proto_library( ], ) +cc_proto_library( + name = "orchestrator_crypto_cc_proto", + deps = [":orchestrator_crypto_proto"], +) + +cc_grpc_library( + name = "orchestrator_crypto_cc_grpc", + srcs = [":orchestrator_crypto_proto"], + grpc_only = True, + deps = [":orchestrator_crypto_cc_proto"], +) + proto_library( name = "hostlib_key_provisioning_proto", srcs = ["hostlib_key_provisioning.proto"], From ebb0aab17c23b3327d5d97e2ff38639d48f1f7ed Mon Sep 17 00:00:00 2001 From: conradgrobler <58467069+conradgrobler@users.noreply.github.com> Date: Fri, 19 Jan 2024 10:48:59 +0000 Subject: [PATCH 05/42] Log Stage 0 measurement digests in hex (#4676) --- stage0/src/lib.rs | 54 ++++++++++++++++++++++++++++-------------- stage0_dice/src/lib.rs | 36 ++++++++++++++-------------- 2 files changed, 54 insertions(+), 36 deletions(-) diff --git a/stage0/src/lib.rs b/stage0/src/lib.rs index 23fc1a9b258..c0b4511521e 100644 --- a/stage0/src/lib.rs +++ b/stage0/src/lib.rs @@ -206,7 +206,7 @@ pub fn rust64_start(encrypted: u64) -> ! { // point will fail. allocator::init_global_allocator(zero_page.e820_table()); - let setup_data_measurement = zero_page + let setup_data_sha2_256_digest = zero_page .try_fill_hdr_from_setup_data(&mut fwcfg) .unwrap_or_default(); @@ -231,7 +231,7 @@ pub fn rust64_start(encrypted: u64) -> ! { } let cmdline = kernel::try_load_cmdline(&mut fwcfg).unwrap_or_default(); - let cmdline_measurement = measure_byte_slice(cmdline.as_bytes()); + let cmdline_sha2_256_digest = measure_byte_slice(cmdline.as_bytes()); let kernel_info = kernel::try_load_kernel_image(&mut fwcfg, zero_page.e820_table()).unwrap_or_default(); @@ -259,8 +259,8 @@ pub fn rust64_start(encrypted: u64) -> ! { let rsdp = acpi::build_acpi_tables(&mut fwcfg, &mut acpi_digest).unwrap(); zero_page.set_acpi_rsdp_addr(PhysAddr::new(rsdp as *const _ as u64)); let acpi_digest = acpi_digest.finalize(); - let mut acpi_measurement = Measurement::default(); - acpi_measurement[..].copy_from_slice(&acpi_digest[..]); + let mut acpi_sha2_256_digest = Measurement::default(); + acpi_sha2_256_digest[..].copy_from_slice(&acpi_digest[..]); if let Err(err) = smp::bootstrap_aps(rsdp) { log::warn!( @@ -289,7 +289,7 @@ pub fn rust64_start(encrypted: u64) -> ! { } } - let ram_disk_measurement = + let ram_disk_sha2_256_digest = initramfs::try_load_initial_ram_disk(&mut fwcfg, zero_page.e820_table(), &kernel_info) .map(|ram_disk| { zero_page.set_initial_ram_disk(ram_disk); @@ -297,22 +297,40 @@ pub fn rust64_start(encrypted: u64) -> ! { }) .unwrap_or_default(); - let memory_map_measurement = measure_byte_slice(zero_page.e820_table().as_bytes()); + let memory_map_sha2_256_digest = measure_byte_slice(zero_page.e820_table().as_bytes()); - log::debug!("Kernel image digest: {:?}", kernel_info.measurement); - log::debug!("Kernel setup data digest: {:?}", setup_data_measurement); - log::debug!("Kernel image digest: {:?}", cmdline_measurement); - log::debug!("Initial RAM disk digest: {:?}", ram_disk_measurement); - log::debug!("ACPI table generation digest: {:?}", acpi_measurement); - log::debug!("E820 table digest: {:?}", memory_map_measurement); + log::debug!( + "Kernel image digest: sha2-256:{}", + hex::encode(kernel_info.measurement) + ); + log::debug!( + "Kernel setup data digest: sha2-256:{}", + hex::encode(setup_data_sha2_256_digest) + ); + log::debug!( + "Kernel image digest: sha2-256:{}", + hex::encode(cmdline_sha2_256_digest) + ); + log::debug!( + "Initial RAM disk digest: sha2-256:{}", + hex::encode(ram_disk_sha2_256_digest) + ); + log::debug!( + "ACPI table generation digest: sha2-256:{}", + hex::encode(acpi_sha2_256_digest) + ); + log::debug!( + "E820 table digest: sha2-256:{}", + hex::encode(memory_map_sha2_256_digest) + ); let measurements = oak_stage0_dice::Measurements { - acpi_measurement, - kernel_measurement: kernel_info.measurement, - cmdline_measurement, - ram_disk_measurement, - setup_data_measurement, - memory_map_measurement, + acpi_sha2_256_digest, + kernel_sha2_256_digest: kernel_info.measurement, + cmdline_sha2_256_digest, + ram_disk_sha2_256_digest, + setup_data_sha2_256_digest, + memory_map_sha2_256_digest, }; let dice_data = Box::leak(Box::new_in( diff --git a/stage0_dice/src/lib.rs b/stage0_dice/src/lib.rs index 004d5d0107b..9347a9c8239 100644 --- a/stage0_dice/src/lib.rs +++ b/stage0_dice/src/lib.rs @@ -50,17 +50,17 @@ pub const REPORT_DATA_SIZE: usize = 64; #[derive(Default)] pub struct Measurements { /// The measurement of the kernel image. - pub kernel_measurement: [u8; 32], + pub kernel_sha2_256_digest: [u8; 32], /// The measurement of the kernel command-line. - pub cmdline_measurement: [u8; 32], + pub cmdline_sha2_256_digest: [u8; 32], /// The measurement of the kernel setup data. - pub setup_data_measurement: [u8; 32], + pub setup_data_sha2_256_digest: [u8; 32], /// The measurement of the initial RAM disk. - pub ram_disk_measurement: [u8; 32], + pub ram_disk_sha2_256_digest: [u8; 32], /// The measurement of the physical memory map. - pub memory_map_measurement: [u8; 32], + pub memory_map_sha2_256_digest: [u8; 32], /// The concatenated measurement of the command used for building the ACPI tables. - pub acpi_measurement: [u8; 32], + pub acpi_sha2_256_digest: [u8; 32], } /// Generates an ECA certificate for use by the next boot stage (Stage 1). @@ -78,42 +78,42 @@ fn generate_stage1_certificate( Value::Integer(KERNEL_MEASUREMENT_ID.into()), Value::Map(alloc::vec![( Value::Integer(SHA2_256_ID.into()), - Value::Bytes(measurements.kernel_measurement.into()), + Value::Bytes(measurements.kernel_sha2_256_digest.into()), )]), ), ( Value::Integer(KERNEL_COMMANDLINE_MEASUREMENT_ID.into()), Value::Map(alloc::vec![( Value::Integer(SHA2_256_ID.into()), - Value::Bytes(measurements.cmdline_measurement.into()), + Value::Bytes(measurements.cmdline_sha2_256_digest.into()), )]), ), ( Value::Integer(SETUP_DATA_MEASUREMENT_ID.into()), Value::Map(alloc::vec![( Value::Integer(SHA2_256_ID.into()), - Value::Bytes(measurements.setup_data_measurement.into()), + Value::Bytes(measurements.setup_data_sha2_256_digest.into()), )]), ), ( Value::Integer(INITRD_MEASUREMENT_ID.into()), Value::Map(alloc::vec![( Value::Integer(SHA2_256_ID.into()), - Value::Bytes(measurements.ram_disk_measurement.into()), + Value::Bytes(measurements.ram_disk_sha2_256_digest.into()), )]), ), ( Value::Integer(MEMORY_MAP_MEASUREMENT_ID.into()), Value::Map(alloc::vec![( Value::Integer(SHA2_256_ID.into()), - Value::Bytes(measurements.memory_map_measurement.into()), + Value::Bytes(measurements.memory_map_sha2_256_digest.into()), )]), ), ( Value::Integer(ACPI_MEASUREMENT_ID.into()), Value::Map(alloc::vec![( Value::Integer(SHA2_256_ID.into()), - Value::Bytes(measurements.acpi_measurement.into()), + Value::Bytes(measurements.acpi_sha2_256_digest.into()), )]), ), ]), @@ -160,14 +160,14 @@ pub fn generate_dice_data< .expect("couldn't serialize stage 1 ECA certificate"); // Use the SHA2-256 digest of the serialized ECA verifying key as the report data. - let eca_measurement = { + let eca_sha2_256_digest = { let mut digest = Sha256::default(); digest.update(&stage0_eca_verifying_key); digest.finalize() }; let mut report_data = [0u8; REPORT_DATA_SIZE]; - report_data[..eca_measurement.len()].copy_from_slice(&eca_measurement[..]); + report_data[..eca_sha2_256_digest.len()].copy_from_slice(&eca_sha2_256_digest[..]); let report = get_attestation(report_data).expect("couldn't get attestation report."); let report_bytes = report.as_bytes(); @@ -178,10 +178,10 @@ pub fn generate_dice_data< // Mix in the measurements of the kernel, the kernel command-line, the kernel setup data and the // initial RAM disk when deriving the CDI for Layer 1. let mut salt: Vec = Vec::with_capacity(128); - salt.extend_from_slice(&measurements.kernel_measurement[..]); - salt.extend_from_slice(&measurements.cmdline_measurement[..]); - salt.extend_from_slice(&measurements.setup_data_measurement[..]); - salt.extend_from_slice(&measurements.ram_disk_measurement[..]); + salt.extend_from_slice(&measurements.kernel_sha2_256_digest[..]); + salt.extend_from_slice(&measurements.cmdline_sha2_256_digest[..]); + salt.extend_from_slice(&measurements.setup_data_sha2_256_digest[..]); + salt.extend_from_slice(&measurements.ram_disk_sha2_256_digest[..]); let hkdf = Hkdf::::new(Some(&salt), &ikm[..]); result.magic = STAGE0_MAGIC; From 46651fa0134467568efb93aab68a6fef02073619 Mon Sep 17 00:00:00 2001 From: Ivan Petrov Date: Fri, 19 Jan 2024 12:39:07 +0000 Subject: [PATCH 06/42] Use absl::strcat in gRPC transport (#4669) --- cc/transport/grpc_streaming_transport.cc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cc/transport/grpc_streaming_transport.cc b/cc/transport/grpc_streaming_transport.cc index 511452f4653..ec61e25f43e 100644 --- a/cc/transport/grpc_streaming_transport.cc +++ b/cc/transport/grpc_streaming_transport.cc @@ -21,6 +21,7 @@ #include "absl/log/log.h" #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "absl/strings/string_view.h" #include "grpcpp/channel.h" #include "grpcpp/client_context.h" @@ -65,7 +66,7 @@ absl::StatusOr GrpcStreamingTransport::GetEvidence() { return absl::InternalError("received InvokeResponse instead of GetPublicKeyResponse"); case ResponseWrapper::RESPONSE_NOT_SET: default: - return absl::InternalError("received unsupported response: " + response->DebugString()); + return absl::InternalError("received unsupported response: " + absl::StrCat(*response)); } } @@ -89,7 +90,7 @@ absl::StatusOr GrpcStreamingTransport::Invoke( return response->invoke_response().encrypted_response(); case ResponseWrapper::RESPONSE_NOT_SET: default: - return absl::InternalError("received unsupported response: " + response->DebugString()); + return absl::InternalError("received unsupported response: " + absl::StrCat(*response)); } } From b1e4abd31a87dea4f2f0b62b588030256892fcb8 Mon Sep 17 00:00:00 2001 From: Ivan Petrov Date: Fri, 19 Jan 2024 12:39:33 +0000 Subject: [PATCH 07/42] Use AttestationVerifier in the Rust Client (#4655) This PR: - Adds an `AttestationVerifier` trait to the `oak_attestation_verification` - Adds an `InsecureAttestationVerifier` implementation that doesn't use reference values and is needed for testing Ref https://github.com/project-oak/oak/issues/3641 Ref https://github.com/project-oak/oak/issues/4074 Ref https://github.com/project-oak/oak/issues/4627 --- Cargo.lock | 6 +- oak_attestation_verification/src/verifier.rs | 8 +- oak_client/Cargo.toml | 2 +- oak_client/src/client.rs | 86 +++++++++++++++++++ oak_client/src/lib.rs | 58 +------------ oak_client/src/transport.rs | 20 ++--- oak_client/src/verifier.rs | 48 ++++------- oak_functions/load_test/Cargo.toml | 1 + oak_functions/load_test/src/main.rs | 3 +- oak_functions_client/src/lib.rs | 10 +-- oak_functions_client/src/main.rs | 3 +- oak_functions_containers_launcher/Cargo.toml | 1 + .../tests/integration_test.rs | 28 ++++-- oak_functions_launcher/Cargo.toml | 1 + .../tests/integration_test.rs | 28 ++++-- oak_functions_test_utils/Cargo.toml | 1 + oak_functions_test_utils/src/lib.rs | 3 +- 17 files changed, 178 insertions(+), 129 deletions(-) create mode 100644 oak_client/src/client.rs diff --git a/Cargo.lock b/Cargo.lock index 7f8156af036..fdde07caed2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1906,7 +1906,7 @@ dependencies = [ "async-trait", "futures-util", "log", - "oak_attestation", + "oak_attestation_verification", "oak_crypto", "oak_grpc_utils", "prost", @@ -2236,6 +2236,7 @@ dependencies = [ "futures", "log", "oak_attestation", + "oak_client", "oak_containers_launcher", "oak_crypto", "oak_functions_abi", @@ -2268,6 +2269,7 @@ dependencies = [ "micro_rpc_build", "oak_attestation", "oak_channel", + "oak_client", "oak_crypto", "oak_functions_abi", "oak_functions_client", @@ -2291,6 +2293,7 @@ version = "0.1.0" dependencies = [ "anyhow", "bencher", + "oak_client", "oak_functions_client", "rand", "tokio", @@ -2368,6 +2371,7 @@ dependencies = [ "hyper", "log", "nix", + "oak_client", "oak_functions_abi", "oak_functions_client", "port_check", diff --git a/oak_attestation_verification/src/verifier.rs b/oak_attestation_verification/src/verifier.rs index 63fbdd0ae44..8d69dd05b8f 100644 --- a/oak_attestation_verification/src/verifier.rs +++ b/oak_attestation_verification/src/verifier.rs @@ -59,8 +59,8 @@ use crate::{ const ADDITIONAL_DATA: &[u8] = b""; pub struct DiceChainResult { - encryption_public_key: Vec, - signing_public_key: Vec, + pub encryption_public_key: Vec, + pub signing_public_key: Vec, } impl From<&anyhow::Result> for AttestationResults { @@ -120,7 +120,7 @@ pub fn verify( } /// Verifies signatures along the certificate chain induced by DICE layers. -fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result { +pub fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result { let root_layer = evidence .root_layer .as_ref() @@ -152,7 +152,7 @@ fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result { let appl_keys = evidence .application_keys .as_ref() - .ok_or_else(|| anyhow::anyhow!("no application fields in evidence"))?; + .ok_or_else(|| anyhow::anyhow!("no application keys in evidence"))?; // Process encryption certificate. let encryption_cert = diff --git a/oak_client/Cargo.toml b/oak_client/Cargo.toml index 7e4a93dc688..77bbcc5e3fd 100644 --- a/oak_client/Cargo.toml +++ b/oak_client/Cargo.toml @@ -10,7 +10,7 @@ anyhow = "*" async-trait = "*" futures-util = "*" log = "*" -oak_attestation = { workspace = true } +oak_attestation_verification = { workspace = true } oak_crypto = { workspace = true } prost = { workspace = true } tonic = { workspace = true } diff --git a/oak_client/src/client.rs b/oak_client/src/client.rs new file mode 100644 index 00000000000..e49af04dade --- /dev/null +++ b/oak_client/src/client.rs @@ -0,0 +1,86 @@ +// +// Copyright 2024 The Project Oak Authors +// +// Licensed 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. +// + +use std::vec::Vec; + +use anyhow::{anyhow, Context}; +use oak_crypto::encryptor::ClientEncryptor; + +use crate::{ + transport::{EvidenceProvider, Transport}, + verifier::AttestationVerifier, +}; + +const EMPTY_ASSOCIATED_DATA: &[u8] = b""; + +/// Client for connecting to Oak. +/// Represents a Relying Party from the RATS Architecture: +/// +pub struct OakClient { + transport: T, + server_encryption_public_key: Vec, +} + +impl OakClient { + pub async fn create( + mut transport: T, + verifier: &dyn AttestationVerifier, + ) -> anyhow::Result { + let endorsed_evidence = transport + .get_endorsed_evidence() + .await + .context("couldn't get endorsed evidence")?; + + let evidence = endorsed_evidence + .evidence + .context("endorsed evidence message doesn't contain evidence")?; + let endorsements = endorsed_evidence + .endorsements + .context("endorsed evidence message doesn't contain endorsements")?; + let attestation_results = verifier + .verify(&evidence, &endorsements) + .context("couldn't verify endorsed evidence")?; + + Ok(Self { + transport, + server_encryption_public_key: attestation_results.encryption_public_key.to_vec(), + }) + } + + pub async fn invoke(&mut self, request_body: &[u8]) -> anyhow::Result> { + // Encrypt request. + let mut client_encryptor = ClientEncryptor::create(&self.server_encryption_public_key) + .context("couldn't create encryptor")?; + let encrypted_request = client_encryptor + .encrypt(request_body, EMPTY_ASSOCIATED_DATA) + .context("couldn't encrypt request")?; + + // Send request. + let encrypted_response = self + .transport + .invoke(&encrypted_request) + .await + .map_err(|error| anyhow!("couldn't send request: {:?}", error))?; + + // Decrypt response. + // Currently we ignore the associated data. + let (response, _) = client_encryptor + .decrypt(&encrypted_response) + .context("client couldn't decrypt response")?; + + Ok(response) + } +} diff --git a/oak_client/src/lib.rs b/oak_client/src/lib.rs index 0f8276916a1..bb462916384 100644 --- a/oak_client/src/lib.rs +++ b/oak_client/src/lib.rs @@ -16,7 +16,7 @@ pub mod proto { pub mod oak { - pub use oak_attestation::proto::oak::attestation; + pub use oak_attestation_verification::proto::oak::attestation; pub use oak_crypto::proto::oak::crypto; pub mod session { pub mod v1 { @@ -28,60 +28,6 @@ pub mod proto { } } +pub mod client; pub mod transport; pub mod verifier; - -use std::vec::Vec; - -use anyhow::{anyhow, Context}; -use oak_crypto::encryptor::ClientEncryptor; - -use crate::transport::{EvidenceProvider, Transport}; - -const EMPTY_ASSOCIATED_DATA: &[u8] = b""; - -/// Client for connecting to Oak. -/// Represents a Relying Party from the RATS Architecture: -/// -pub struct OakClient { - transport: T, - server_encryption_public_key: Vec, -} - -impl OakClient { - pub async fn create(mut transport: T) -> anyhow::Result { - // TODO(#3641): Implement client-side attestation verification. - let evidence = transport - .get_evidence() - .await - .context("couldn't get attestation evidence")?; - Ok(Self { - transport, - server_encryption_public_key: evidence.encryption_public_key.to_vec(), - }) - } - - pub async fn invoke(&mut self, request_body: &[u8]) -> anyhow::Result> { - // Encrypt request. - let mut client_encryptor = ClientEncryptor::create(&self.server_encryption_public_key) - .context("couldn't create encryptor")?; - let encrypted_request = client_encryptor - .encrypt(request_body, EMPTY_ASSOCIATED_DATA) - .context("couldn't encrypt request")?; - - // Send request. - let encrypted_response = self - .transport - .invoke(&encrypted_request) - .await - .map_err(|error| anyhow!("couldn't send request: {:?}", error))?; - - // Decrypt response. - // Currently we ignore the associated data. - let (response, _) = client_encryptor - .decrypt(&encrypted_response) - .context("client couldn't decrypt response")?; - - Ok(response) - } -} diff --git a/oak_client/src/transport.rs b/oak_client/src/transport.rs index c59b6f781d8..85237793445 100644 --- a/oak_client/src/transport.rs +++ b/oak_client/src/transport.rs @@ -20,7 +20,7 @@ use tonic::transport::Channel; use crate::proto::oak::session::v1::{ request_wrapper, response_wrapper, streaming_session_client::StreamingSessionClient, - AttestationEvidence, GetPublicKeyRequest, InvokeRequest, RequestWrapper, + AttestationBundle, GetPublicKeyRequest, InvokeRequest, RequestWrapper, }; pub struct GrpcStreamingTransport { @@ -83,12 +83,12 @@ impl Transport for GrpcStreamingTransport { #[async_trait::async_trait] pub trait EvidenceProvider { - async fn get_evidence(&mut self) -> anyhow::Result; + async fn get_endorsed_evidence(&mut self) -> anyhow::Result; } #[async_trait::async_trait] impl EvidenceProvider for GrpcStreamingTransport { - async fn get_evidence(&mut self) -> anyhow::Result { + async fn get_endorsed_evidence(&mut self) -> anyhow::Result { let mut response_stream = self .rpc_client .stream(futures_util::stream::iter(vec![RequestWrapper { @@ -98,29 +98,27 @@ impl EvidenceProvider for GrpcStreamingTransport { )), }])) .await - .context("couldn't get attestation evidence")? + .context("couldn't get endorsed evidence")? .into_inner(); // Read the next (and only) message from the response stream. let response_wrapper = response_stream .message() .await - .context("gRPC server error when requesting attestation evidence")? + .context("gRPC server error when requesting endorsed evidence")? .context("received empty response stream")?; - let Some(response_wrapper::Response::GetPublicKeyResponse(get_evidence_response)) = + let Some(response_wrapper::Response::GetPublicKeyResponse(get_endorsed_evidence_response)) = response_wrapper.response else { return Err(anyhow::anyhow!( - "response_wrapper doesn't contain a valid get_evidence_response message" + "response_wrapper doesn't contain a valid get_endorsed_evidence_response message" )); }; #[allow(deprecated)] - get_evidence_response + get_endorsed_evidence_response .attestation_bundle - .context("get_evidence_response message doesn't contain an attestation bundle")? - .attestation_evidence - .context("get_evidence_response message doesn't contain an attestation evidence") + .context("get_endorsed_evidence_response message doesn't contain endorsed evidence") } } diff --git a/oak_client/src/verifier.rs b/oak_client/src/verifier.rs index 38a994d722e..331ef5a2772 100644 --- a/oak_client/src/verifier.rs +++ b/oak_client/src/verifier.rs @@ -14,39 +14,27 @@ // limitations under the License. // -pub trait EvidenceProvider { - fn get_evidence(&mut self) -> anyhow::Result; -} - -/// Attestation evidence used to verify the validity of the Trusted Execution -/// Environment and the binary running on it. -/// -pub struct Evidence { - pub enclave_public_key: Vec, -} - -/// Reference values used by the verifier to appraise the attestation evidence. -/// -pub struct ReferenceValue { - pub binary_hash: String, -} +use anyhow::Context; +use oak_attestation_verification::{ + proto::oak::attestation::v1::{Endorsements, Evidence}, + verifier::{verify_dice_chain, DiceChainResult}, +}; -/// Verifier that appraises the attestation evidence and produces an attestation -/// result. -/// -pub trait Verifier { - fn verify(&self, evidence: &Evidence, reference_value: &ReferenceValue) -> anyhow::Result<()>; +pub trait AttestationVerifier { + fn verify( + &self, + evidence: &Evidence, + endorsements: &Endorsements, + ) -> anyhow::Result; } -pub struct AmdSevSnpVerifier {} +/// Verifier that doesn't check the Evidence against Reference Values and only checks the DICE chain +/// correctness. +/// Should be only used for testing. +pub struct InsecureAttestationVerifier; -// TODO(#3641): Implement client-side attestation verification. -impl Verifier for AmdSevSnpVerifier { - fn verify( - &self, - _evidence: &Evidence, - _reference_value: &ReferenceValue, - ) -> anyhow::Result<()> { - Ok(()) +impl AttestationVerifier for InsecureAttestationVerifier { + fn verify(&self, evidence: &Evidence, _: &Endorsements) -> anyhow::Result { + verify_dice_chain(evidence).context("couldn't verify the DICE chain") } } diff --git a/oak_functions/load_test/Cargo.toml b/oak_functions/load_test/Cargo.toml index ed4d0397ee4..2a902422198 100644 --- a/oak_functions/load_test/Cargo.toml +++ b/oak_functions/load_test/Cargo.toml @@ -8,6 +8,7 @@ license = "Apache-2.0" [dependencies] anyhow = "*" bencher = "*" +oak_client = { workspace = true } oak_functions_client = { workspace = true } rand = "*" tokio = { workspace = true, features = [ diff --git a/oak_functions/load_test/src/main.rs b/oak_functions/load_test/src/main.rs index 995e327e577..dceb23af8fe 100644 --- a/oak_functions/load_test/src/main.rs +++ b/oak_functions/load_test/src/main.rs @@ -18,6 +18,7 @@ use std::time::Instant; use anyhow::Context; use bencher::stats::Stats; +use oak_client::verifier::InsecureAttestationVerifier; use oak_functions_client::OakFunctionsClient; // From https://pantheon.corp.google.com/api-gateway/gateway/weather-lookup-grpc/location/europe-west2?project=oak-ci. @@ -28,7 +29,7 @@ const TOTAL_REQUESTS: usize = 50; #[tokio::main] async fn main() -> anyhow::Result<()> { - let mut client = OakFunctionsClient::new(URL) + let mut client = OakFunctionsClient::new(URL, &InsecureAttestationVerifier {}) .await .context("couldn't create client")?; diff --git a/oak_functions_client/src/lib.rs b/oak_functions_client/src/lib.rs index 59a2f5a8618..d4cd6d0729c 100644 --- a/oak_functions_client/src/lib.rs +++ b/oak_functions_client/src/lib.rs @@ -15,25 +15,25 @@ use anyhow::Context; use oak_client::{ - proto::oak::session::v1::streaming_session_client::StreamingSessionClient, - transport::GrpcStreamingTransport, OakClient, + client::OakClient, proto::oak::session::v1::streaming_session_client::StreamingSessionClient, + transport::GrpcStreamingTransport, verifier::AttestationVerifier, }; use prost::Message; use tonic::transport::Channel; pub struct OakFunctionsClient { - oak_client: oak_client::OakClient, + oak_client: OakClient, } impl OakFunctionsClient { - pub async fn new(uri: &str) -> anyhow::Result { + pub async fn new(uri: &str, verifier: &dyn AttestationVerifier) -> anyhow::Result { let channel = Channel::from_shared(uri.to_string()) .context("couldn't create gRPC channel")? .connect() .await .context("couldn't connect via gRPC channel")?; let transport = GrpcStreamingTransport::new(StreamingSessionClient::new(channel)); - let oak_client = OakClient::create(transport) + let oak_client = OakClient::create(transport, verifier) .await .context("couldn't create Oak client")?; Ok(Self { oak_client }) diff --git a/oak_functions_client/src/main.rs b/oak_functions_client/src/main.rs index 2960dbaeb97..5435e994f62 100644 --- a/oak_functions_client/src/main.rs +++ b/oak_functions_client/src/main.rs @@ -19,6 +19,7 @@ use anyhow::Context; use clap::Parser; +use oak_client::verifier::InsecureAttestationVerifier; use oak_functions_abi::Request; use oak_functions_client::OakFunctionsClient; use regex::Regex; @@ -61,7 +62,7 @@ async fn main() -> anyhow::Result<()> { env_logger::init(); let opt = Opt::parse(); - let mut client = OakFunctionsClient::new(&opt.uri) + let mut client = OakFunctionsClient::new(&opt.uri, &InsecureAttestationVerifier {}) .await .context("couldn't create Oak Functions client")?; diff --git a/oak_functions_containers_launcher/Cargo.toml b/oak_functions_containers_launcher/Cargo.toml index 3b9a313a109..57ed1d31335 100644 --- a/oak_functions_containers_launcher/Cargo.toml +++ b/oak_functions_containers_launcher/Cargo.toml @@ -25,6 +25,7 @@ tonic = { workspace = true } ubyte = "*" [dev-dependencies] +oak_client = { workspace = true } oak_functions_client = { workspace = true } oak_functions_test_utils = { workspace = true } xtask = { workspace = true } diff --git a/oak_functions_containers_launcher/tests/integration_test.rs b/oak_functions_containers_launcher/tests/integration_test.rs index e7e9fb5f2fd..6c55f7d4657 100644 --- a/oak_functions_containers_launcher/tests/integration_test.rs +++ b/oak_functions_containers_launcher/tests/integration_test.rs @@ -17,6 +17,7 @@ use std::time::Duration; +use oak_client::verifier::InsecureAttestationVerifier; use oak_functions_client::OakFunctionsClient; use xtask::{launcher::MOCK_LOOKUP_DATA_PATH, workspace_path}; @@ -40,9 +41,12 @@ async fn test_launcher_key_value_lookup() { // Wait for the server to start up. tokio::time::sleep(Duration::from_secs(60)).await; - let mut client = OakFunctionsClient::new(&format!("http://localhost:{port}")) - .await - .expect("failed to create client"); + let mut client = OakFunctionsClient::new( + &format!("http://localhost:{port}"), + &InsecureAttestationVerifier {}, + ) + .await + .expect("failed to create client"); let response = client.invoke(b"test_key").await.expect("failed to invoke"); assert_eq!(response, b"test_value"); @@ -68,9 +72,12 @@ async fn test_launcher_echo() { // Wait for the server to start up. tokio::time::sleep(Duration::from_secs(60)).await; - let mut client = OakFunctionsClient::new(&format!("http://localhost:{port}")) - .await - .expect("failed to create client"); + let mut client = OakFunctionsClient::new( + &format!("http://localhost:{port}"), + &InsecureAttestationVerifier {}, + ) + .await + .expect("failed to create client"); let response = client.invoke(b"xxxyyyzzz").await.expect("failed to invoke"); assert_eq!(std::str::from_utf8(&response).unwrap(), "xxxyyyzzz"); @@ -104,9 +111,12 @@ async fn test_launcher_weather_lookup() { // Wait for the server to start up. tokio::time::sleep(Duration::from_secs(120)).await; - let mut client = OakFunctionsClient::new(&format!("http://localhost:{port}")) - .await - .expect("failed to create client"); + let mut client = OakFunctionsClient::new( + &format!("http://localhost:{port}"), + &InsecureAttestationVerifier {}, + ) + .await + .expect("failed to create client"); let response = client .invoke(br#"{"lat":0,"lng":0}"#) diff --git a/oak_functions_launcher/Cargo.toml b/oak_functions_launcher/Cargo.toml index 0b88401aee7..ccbf39d7c21 100644 --- a/oak_functions_launcher/Cargo.toml +++ b/oak_functions_launcher/Cargo.toml @@ -41,6 +41,7 @@ micro_rpc_build = { workspace = true } oak_grpc_utils = { workspace = true } [dev-dependencies] +oak_client = { workspace = true } oak_crypto = { workspace = true } oak_functions_client = { workspace = true } oak_functions_test_utils = { workspace = true } diff --git a/oak_functions_launcher/tests/integration_test.rs b/oak_functions_launcher/tests/integration_test.rs index 8c161336d1d..25bb838de41 100644 --- a/oak_functions_launcher/tests/integration_test.rs +++ b/oak_functions_launcher/tests/integration_test.rs @@ -17,6 +17,7 @@ use std::{io::Write, time::Duration}; +use oak_client::verifier::InsecureAttestationVerifier; use oak_functions_client::OakFunctionsClient; use oak_functions_launcher::{ proto::oak::functions::OakFunctionsAsyncClient, update_lookup_data, LookupDataConfig, @@ -45,9 +46,12 @@ async fn test_launcher_key_value_lookup() { // Wait for the server to start up. tokio::time::sleep(Duration::from_secs(20)).await; - let mut client = OakFunctionsClient::new(&format!("http://localhost:{port}")) - .await - .expect("failed to create client"); + let mut client = OakFunctionsClient::new( + &format!("http://localhost:{port}"), + &InsecureAttestationVerifier {}, + ) + .await + .expect("failed to create client"); let response = client.invoke(b"test_key").await.expect("failed to invoke"); assert_eq!(response, b"test_value"); @@ -73,9 +77,12 @@ async fn test_launcher_echo() { // Wait for the server to start up. tokio::time::sleep(Duration::from_secs(20)).await; - let mut client = OakFunctionsClient::new(&format!("http://localhost:{port}")) - .await - .expect("failed to create client"); + let mut client = OakFunctionsClient::new( + &format!("http://localhost:{port}"), + &InsecureAttestationVerifier {}, + ) + .await + .expect("failed to create client"); let response = client.invoke(b"xxxyyyzzz").await.expect("failed to invoke"); assert_eq!(std::str::from_utf8(&response).unwrap(), "xxxyyyzzz"); @@ -109,9 +116,12 @@ async fn test_launcher_weather_lookup() { // Wait for the server to start up. tokio::time::sleep(Duration::from_secs(20)).await; - let mut client = OakFunctionsClient::new(&format!("http://localhost:{port}")) - .await - .expect("failed to create client"); + let mut client = OakFunctionsClient::new( + &format!("http://localhost:{port}"), + &InsecureAttestationVerifier {}, + ) + .await + .expect("failed to create client"); let response = client .invoke(br#"{"lat":0,"lng":0}"#) diff --git a/oak_functions_test_utils/Cargo.toml b/oak_functions_test_utils/Cargo.toml index 7ebc7dd4f54..dee150ffa73 100644 --- a/oak_functions_test_utils/Cargo.toml +++ b/oak_functions_test_utils/Cargo.toml @@ -13,6 +13,7 @@ log = "*" http = "*" hyper = { version = "*", features = ["client", "http1", "runtime", "server"] } nix = { version = "*", features = ["process", "signal"] } +oak_client = { workspace = true } oak_functions_abi = { workspace = true } oak_functions_client = { workspace = true } port_check = "*" diff --git a/oak_functions_test_utils/src/lib.rs b/oak_functions_test_utils/src/lib.rs index fc402a46b90..6f4ee7f92a2 100644 --- a/oak_functions_test_utils/src/lib.rs +++ b/oak_functions_test_utils/src/lib.rs @@ -24,6 +24,7 @@ use std::{ use anyhow::Context; use log::info; use nix::unistd::Pid; +use oak_client::verifier::InsecureAttestationVerifier; use oak_functions_abi::Response; use oak_functions_client::OakFunctionsClient; use prost::Message; @@ -262,7 +263,7 @@ pub async fn make_request(port: u16, request_body: &[u8]) -> Vec { let uri = format!("http://localhost:{port}/"); // Create client - let mut client = OakFunctionsClient::new(&uri) + let mut client = OakFunctionsClient::new(&uri, &InsecureAttestationVerifier {}) .await .expect("couldn't create client"); From 2a9a18a016c5cd5c4ed3915fc53cf1831818d907 Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Fri, 19 Jan 2024 13:18:20 +0000 Subject: [PATCH 08/42] Add a step to ensure some crates are no_std (#4686) --- justfile | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/justfile b/justfile index 144b07ad1d7..9c93104b977 100644 --- a/justfile +++ b/justfile @@ -63,6 +63,12 @@ oak_functions_containers_launcher: all_oak_functions_containers_binaries: stage0_bin stage1_cpio oak_containers_kernel oak_containers_system_image oak_functions_containers_container_bundle_tar oak_functions_containers_launcher +ensure_no_std package: + cargo check --target=x86_64-unknown-none --package='{{package}}' + +# TODO(#4682): Add "oak_attestation_verification" when it is no_std compatible +all_ensure_no_std: (ensure_no_std "micro_rpc") + # Entry points for Kokoro CI. kokoro_build_binaries_rust: all_enclave_apps oak_restricted_kernel_bin oak_restricted_kernel_simple_io_bin oak_restricted_kernel_simple_io_wrapper stage0_bin @@ -70,7 +76,7 @@ kokoro_build_binaries_rust: all_enclave_apps oak_restricted_kernel_bin oak_restr kokoro_oak_containers: all_oak_containers_binaries oak_functions_containers_container_bundle_tar RUST_LOG="debug" cargo nextest run --all-targets --hide-progress-bar --package='oak_containers_hello_world_untrusted_app' -kokoro_run_tests: +kokoro_run_tests: all_ensure_no_std RUST_LOG="debug" cargo nextest run --all-targets --hide-progress-bar --workspace --exclude='oak_containers_hello_world_untrusted_app' clang-tidy: From 45793298f027b39c81c74e7ac6deb0d0c6d70972 Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Fri, 19 Jan 2024 14:19:11 +0000 Subject: [PATCH 09/42] Combine tests that rely on containers (#4687) --- .../tests/integration_test.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/oak_containers_hello_world_untrusted_app/tests/integration_test.rs b/oak_containers_hello_world_untrusted_app/tests/integration_test.rs index 146bd6c0ea9..284e92c7b7f 100644 --- a/oak_containers_hello_world_untrusted_app/tests/integration_test.rs +++ b/oak_containers_hello_world_untrusted_app/tests/integration_test.rs @@ -96,12 +96,10 @@ async fn run_hello_world_test(container_bundle: std::path::PathBuf) { assert_eq!(greeting, "Hello from the trusted side, fancy test! Btw, the Trusted App has a config with a length of 0 bytes."); } -#[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn hello_world() { run_hello_world_test(Args::default_for_test().container_bundle).await; } -#[tokio::test(flavor = "multi_thread", worker_threads = 4)] async fn cc_hello_world() { run_hello_world_test( format!( @@ -112,3 +110,14 @@ async fn cc_hello_world() { ) .await; } + +#[tokio::test(flavor = "multi_thread", worker_threads = 4)] +async fn all_hello_world() { + // Combine both test cases into one test to avoid running them in parallel. + // Nextest runs each test case in a separate process, which means that even though they appear + // to be sharing synchronized state, that state is not synchronized, and therefore they both end + // up rebuilding the dependencies through the Lazy cell above. + // See https://nexte.st/book/usage.html?highlight=process#limitations + hello_world().await; + cc_hello_world().await; +} From cdd980909e1ba3e22ccc2a3b874b51a806c8f4e4 Mon Sep 17 00:00:00 2001 From: Ivan Petrov Date: Fri, 19 Jan 2024 15:17:45 +0000 Subject: [PATCH 10/42] Copy crypto protos into the global proto directory (#4661) This PR copies Crypto proto files into the `proto` directory. We need this step to first update internal dependencies to use the new proto directory. Ref https://github.com/project-oak/oak/issues/4392 --- oak_crypto/proto/v1/crypto.proto | 2 + proto/crypto/BUILD | 55 +++++++++++++++++++++++++++ proto/crypto/crypto.proto | 65 ++++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 proto/crypto/BUILD create mode 100644 proto/crypto/crypto.proto diff --git a/oak_crypto/proto/v1/crypto.proto b/oak_crypto/proto/v1/crypto.proto index ca999a4446e..5dcd755f197 100644 --- a/oak_crypto/proto/v1/crypto.proto +++ b/oak_crypto/proto/v1/crypto.proto @@ -16,6 +16,8 @@ syntax = "proto3"; +// TODO(#4392): Currently in the process of moving to `/proto` directory. +// DO NOT MODIFY THOSE FILES WHILE THEY ARE BEING MOVED. package oak.crypto.v1; option java_multiple_files = true; diff --git a/proto/crypto/BUILD b/proto/crypto/BUILD new file mode 100644 index 00000000000..b2fa98121a8 --- /dev/null +++ b/proto/crypto/BUILD @@ -0,0 +1,55 @@ +# +# Copyright 2023 The Project Oak Authors +# +# Licensed 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. +# + +load("@bazel_skylib//rules:build_test.bzl", "build_test") +load("@rules_cc//cc:defs.bzl", "cc_proto_library") +load("@rules_java//java:defs.bzl", "java_lite_proto_library", "java_proto_library") +load("@rules_proto//proto:defs.bzl", "proto_library") + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], +) + +proto_library( + name = "crypto_proto", + srcs = ["crypto.proto"], +) + +cc_proto_library( + name = "crypto_cc_proto", + deps = [":crypto_proto"], +) + +java_proto_library( + name = "crypto_java_proto", + deps = [":crypto_proto"], +) + +java_lite_proto_library( + name = "crypto_java_proto_lite", + deps = [":crypto_proto"], +) + +build_test( + name = "build_test", + targets = [ + ":crypto_proto", + ":crypto_cc_proto", + ":crypto_java_proto", + ":crypto_java_proto_lite", + ], +) diff --git a/proto/crypto/crypto.proto b/proto/crypto/crypto.proto new file mode 100644 index 00000000000..5dcd755f197 --- /dev/null +++ b/proto/crypto/crypto.proto @@ -0,0 +1,65 @@ +// +// Copyright 2023 The Project Oak Authors +// +// Licensed 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. +// + +syntax = "proto3"; + +// TODO(#4392): Currently in the process of moving to `/proto` directory. +// DO NOT MODIFY THOSE FILES WHILE THEY ARE BEING MOVED. +package oak.crypto.v1; + +option java_multiple_files = true; +option java_package = "com.google.oak.crypto.v1"; + +// Request message encrypted using Hybrid Public Key Encryption (HPKE). +// +message EncryptedRequest { + // Message encrypted with Authenticated Encryption with Associated Data (AEAD) + // using the derived session key. + AeadEncryptedMessage encrypted_message = 1; + // Ephemeral Diffie-Hellman client public key that is needed to derive a session key. + // Only sent in the first message of the secure session. + optional bytes serialized_encapsulated_public_key = 2; +} + +// Response message encrypted Hybrid Public Key Encryption (HPKE), which uses a +// response key generated as part of bidirectional encryption. +// +message EncryptedResponse { + // Message encrypted with Authenticated Encryption with Associated Data (AEAD) + // using the derived session key. + AeadEncryptedMessage encrypted_message = 1; +} + +// Message encrypted with Authenticated Encryption with Associated Data (AEAD). +// +message AeadEncryptedMessage { + bytes ciphertext = 1; + bytes associated_data = 2; + bytes nonce = 3; +} + +// Envelope containing session keys required to encrypt/decrypt messages within a secure session. +// Needed to serialize contexts in order to send them over an RPC. +message SessionKeys { + // AEAD key for encrypting/decrypting client requests. + bytes request_key = 1; + // AEAD key for encrypting/decrypting enclave responses. + bytes response_key = 4; +} + +message Signature { + bytes signature = 1; +} From 61ab55e430868de0f549faba1e09b370284381bc Mon Sep 17 00:00:00 2001 From: Ivan Petrov Date: Fri, 19 Jan 2024 15:18:01 +0000 Subject: [PATCH 11/42] Rename RecipientContextGenerator into EncryptionKeyHandle in Rust (#4672) Ref https://github.com/project-oak/oak/issues/4490 --- oak_attestation/src/handler.rs | 29 ++++++------- oak_containers_orchestrator/src/crypto.rs | 2 +- oak_containers_sdk/src/crypto.rs | 26 +----------- oak_containers_sdk/src/lib.rs | 2 +- oak_crypto/src/encryptor.rs | 50 ++++++----------------- oak_crypto/src/tests.rs | 24 +++++------ oak_functions_containers_app/src/lib.rs | 10 ++--- oak_functions_service/src/lib.rs | 15 +++---- oak_restricted_kernel_sdk/src/dice.rs | 15 ++++--- 9 files changed, 61 insertions(+), 112 deletions(-) diff --git a/oak_attestation/src/handler.rs b/oak_attestation/src/handler.rs index 5fc19ed0f8b..74cb531a7da 100644 --- a/oak_attestation/src/handler.rs +++ b/oak_attestation/src/handler.rs @@ -20,8 +20,7 @@ use core::future::Future; use anyhow::Context; use oak_crypto::{ encryptor::{ - AsyncRecipientContextGenerator, AsyncServerEncryptor, RecipientContextGenerator, - ServerEncryptor, + AsyncEncryptionKeyHandle, AsyncServerEncryptor, EncryptionKeyHandle, ServerEncryptor, }, proto::oak::crypto::v1::{EncryptedRequest, EncryptedResponse}, }; @@ -42,17 +41,14 @@ pub struct PublicKeyInfo { /// based on the provided encryption key. pub struct EncryptionHandler) -> Vec> { // TODO(#3442): Use attester to attest to the public key. - encryption_key_provider: Arc, + encryption_key_handle: Arc, request_handler: H, } impl) -> Vec> EncryptionHandler { - pub fn create( - encryption_key_provider: Arc, - request_handler: H, - ) -> Self { + pub fn create(encryption_key_handle: Arc, request_handler: H) -> Self { Self { - encryption_key_provider, + encryption_key_handle, request_handler, } } @@ -67,7 +63,7 @@ impl) -> Vec> EncryptionHandler { .context("initial request message doesn't contain encapsulated public key")?; let mut server_encryptor = ServerEncryptor::create( serialized_encapsulated_public_key, - self.encryption_key_provider.clone(), + self.encryption_key_handle.clone(), ) .context("couldn't create server encryptor")?; @@ -90,27 +86,27 @@ impl) -> Vec> EncryptionHandler { /// Wraps a closure to an underlying function with request encryption and response decryption logic, /// based on the provided encryption key. -/// [`AsyncEncryptionHandler`] can be used when an [`AsyncRecipientContextGenerator`] is needed. +/// [`AsyncEncryptionHandler`] can be used when an [`AsyncEncryptionKeyHandle`] is needed. pub struct AsyncEncryptionHandler where - G: AsyncRecipientContextGenerator + Send + Sync, + G: AsyncEncryptionKeyHandle + Send + Sync, H: FnOnce(Vec) -> F, F: Future>, { // TODO(#3442): Use attester to attest to the public key. - recipient_context_generator: Arc, + encryption_key_handle: Arc, request_handler: H, } impl AsyncEncryptionHandler where - G: AsyncRecipientContextGenerator + Send + Sync, + G: AsyncEncryptionKeyHandle + Send + Sync, H: FnOnce(Vec) -> F, F: Future>, { - pub fn create(recipient_context_generator: Arc, request_handler: H) -> Self { + pub fn create(encryption_key_handle: Arc, request_handler: H) -> Self { Self { - recipient_context_generator, + encryption_key_handle, request_handler, } } @@ -120,8 +116,7 @@ where encrypted_request: &EncryptedRequest, ) -> anyhow::Result { // Initialize server encryptor. - let mut server_encryptor = - AsyncServerEncryptor::new(self.recipient_context_generator.as_ref()); + let mut server_encryptor = AsyncServerEncryptor::new(self.encryption_key_handle.as_ref()); // Decrypt request. let (request, _associated_data) = server_encryptor diff --git a/oak_containers_orchestrator/src/crypto.rs b/oak_containers_orchestrator/src/crypto.rs index daee0003a1e..05b5ae5d228 100644 --- a/oak_containers_orchestrator/src/crypto.rs +++ b/oak_containers_orchestrator/src/crypto.rs @@ -17,7 +17,7 @@ use std::sync::Arc; use anyhow::{anyhow, Context}; use hpke::{kem::X25519HkdfSha256, Deserializable, Kem}; -use oak_crypto::encryptor::{EncryptionKeyProvider, RecipientContextGenerator, ServerEncryptor}; +use oak_crypto::encryptor::{EncryptionKeyHandle, EncryptionKeyProvider, ServerEncryptor}; use tonic::{Request, Response}; use crate::proto::oak::{ diff --git a/oak_containers_sdk/src/crypto.rs b/oak_containers_sdk/src/crypto.rs index 8babf08c3d9..ab425771fae 100644 --- a/oak_containers_sdk/src/crypto.rs +++ b/oak_containers_sdk/src/crypto.rs @@ -16,7 +16,7 @@ use anyhow::Context; use async_trait::async_trait; use oak_crypto::{ - encryptor::AsyncRecipientContextGenerator, hpke::RecipientContext, + encryptor::AsyncEncryptionKeyHandle, hpke::RecipientContext, proto::oak::crypto::v1::SessionKeys, }; use tonic::transport::{Endpoint, Uri}; @@ -71,28 +71,6 @@ impl OrchestratorCryptoClient { } } -#[async_trait(?Send)] -pub trait EncryptionKeyHandle { - async fn derive_session_keys( - &self, - encapsulated_public_key: &[u8], - ) -> anyhow::Result; -} - -#[async_trait(?Send)] -impl EncryptionKeyHandle for T -where - T: AsyncRecipientContextGenerator, -{ - async fn derive_session_keys( - &self, - encapsulated_public_key: &[u8], - ) -> anyhow::Result { - self.generate_recipient_context(encapsulated_public_key) - .await - } -} - pub struct InstanceEncryptionKeyHandle { orchestrator_crypto_client: OrchestratorCryptoClient, } @@ -108,7 +86,7 @@ impl InstanceEncryptionKeyHandle { } #[async_trait] -impl AsyncRecipientContextGenerator for InstanceEncryptionKeyHandle { +impl AsyncEncryptionKeyHandle for InstanceEncryptionKeyHandle { async fn generate_recipient_context( &self, encapsulated_public_key: &[u8], diff --git a/oak_containers_sdk/src/lib.rs b/oak_containers_sdk/src/lib.rs index ac176c72b7a..6fdc69374d6 100644 --- a/oak_containers_sdk/src/lib.rs +++ b/oak_containers_sdk/src/lib.rs @@ -42,5 +42,5 @@ static IGNORED_ENDPOINT_URI: &str = "file://[::]:0"; const ORCHESTRATOR_IPC_SOCKET: &str = "/oak_utils/orchestrator_ipc"; // Re-export structs so that they are available at the top level of the SDK. -pub use crypto::{EncryptionKeyHandle, InstanceEncryptionKeyHandle}; +pub use crypto::InstanceEncryptionKeyHandle; pub use orchestrator_client::OrchestratorClient; diff --git a/oak_crypto/src/encryptor.rs b/oak_crypto/src/encryptor.rs index eeccee57fa1..ce3dab09626 100644 --- a/oak_crypto/src/encryptor.rs +++ b/oak_crypto/src/encryptor.rs @@ -106,40 +106,14 @@ impl EncryptionKeyProvider { } } -// This trait just aliases [`RecipientContextGenerator`], while using different naming -// as defined in the Oak SDK design doc. -// TODO(#3841): rename the relevant trait and struct in our crypto crates. -/// Generate [`SessionKeys`] for the provided public key. pub trait EncryptionKeyHandle { - fn generate_session_keys(&self, encapsulated_public_key: &[u8]) -> anyhow::Result; -} - -// Alias this struct in order to conform to the naming outlined in the restricted kernel SDK design -// doc. -// TODO(#3841): rename the relevant struct and remove this alias. -use crate::encryptor::RecipientContext as SessionKeys; - -impl RecipientContextGenerator for T -where - T: EncryptionKeyHandle, -{ - fn generate_recipient_context( - &self, - encapsulated_public_key: &[u8], - ) -> anyhow::Result { - self.generate_session_keys(encapsulated_public_key) - } -} - -pub trait RecipientContextGenerator { - // TODO(#3841): Implement Oak Kernel Crypto API and return corresponding session keys instead. fn generate_recipient_context( &self, encapsulated_public_key: &[u8], ) -> anyhow::Result; } -impl RecipientContextGenerator for EncryptionKeyProvider { +impl EncryptionKeyHandle for EncryptionKeyProvider { fn generate_recipient_context( &self, encapsulated_public_key: &[u8], @@ -150,7 +124,7 @@ impl RecipientContextGenerator for EncryptionKeyProvider { } #[async_trait] -pub trait AsyncRecipientContextGenerator { +pub trait AsyncEncryptionKeyHandle { async fn generate_recipient_context( &self, encapsulated_public_key: &[u8], @@ -158,12 +132,12 @@ pub trait AsyncRecipientContextGenerator { } #[async_trait] -impl AsyncRecipientContextGenerator for EncryptionKeyProvider { +impl AsyncEncryptionKeyHandle for EncryptionKeyProvider { async fn generate_recipient_context( &self, encapsulated_public_key: &[u8], ) -> anyhow::Result { - (self as &dyn RecipientContextGenerator).generate_recipient_context(encapsulated_public_key) + (self as &dyn EncryptionKeyHandle).generate_recipient_context(encapsulated_public_key) } } @@ -258,9 +232,9 @@ pub struct ServerEncryptor { impl ServerEncryptor { pub fn create( serialized_encapsulated_public_key: &[u8], - recipient_context_generator: Arc, + encryption_key_handle: Arc, ) -> anyhow::Result { - let recipient_context = recipient_context_generator + let recipient_context = encryption_key_handle .generate_recipient_context(serialized_encapsulated_public_key) .context("couldn't generate recipient crypto context")?; Ok(Self::new(recipient_context)) @@ -331,19 +305,19 @@ impl ServerEncryptor { // the Restricted Kernel. pub struct AsyncServerEncryptor<'a, G> where - G: AsyncRecipientContextGenerator + Send + Sync, + G: AsyncEncryptionKeyHandle + Send + Sync, { - recipient_context_generator: &'a G, + encryption_key_handle: &'a G, inner: Option, } impl<'a, G> AsyncServerEncryptor<'a, G> where - G: AsyncRecipientContextGenerator + Send + Sync, + G: AsyncEncryptionKeyHandle + Send + Sync, { - pub fn new(recipient_context_generator: &'a G) -> Self { + pub fn new(encryption_key_handle: &'a G) -> Self { Self { - recipient_context_generator, + encryption_key_handle, inner: None, } } @@ -363,7 +337,7 @@ where .as_ref() .context("initial request message doesn't contain encapsulated public key")?; let recipient_context = self - .recipient_context_generator + .encryption_key_handle .generate_recipient_context(serialized_encapsulated_public_key) .await .context("couldn't generate recipient crypto context")?; diff --git a/oak_crypto/src/tests.rs b/oak_crypto/src/tests.rs index 654a6241b81..d32a734d923 100644 --- a/oak_crypto/src/tests.rs +++ b/oak_crypto/src/tests.rs @@ -21,8 +21,8 @@ use async_trait::async_trait; use crate::{ encryptor::{ - AsyncRecipientContextGenerator, AsyncServerEncryptor, ClientEncryptor, - EncryptionKeyProvider, ServerEncryptor, OAK_HPKE_INFO, + AsyncEncryptionKeyHandle, AsyncServerEncryptor, ClientEncryptor, EncryptionKeyProvider, + ServerEncryptor, OAK_HPKE_INFO, }, hpke::{ aead::{AEAD_ALGORITHM_KEY_SIZE_BYTES, AEAD_NONCE_SIZE_BYTES}, @@ -125,8 +125,8 @@ fn test_hpke() { #[test] fn test_encryptor() { - let key_provider = Arc::new(EncryptionKeyProvider::generate()); - let serialized_server_public_key = key_provider.get_serialized_public_key(); + let encryption_key = Arc::new(EncryptionKeyProvider::generate()); + let serialized_server_public_key = encryption_key.get_serialized_public_key(); let mut client_encryptor = ClientEncryptor::create(&serialized_server_public_key) .expect("couldn't create client encryptor"); @@ -152,7 +152,7 @@ fn test_encryptor() { .as_ref() .expect("initial request message doesn't contain encapsulated public key"); server_encryptor = Some( - ServerEncryptor::create(serialized_encapsulated_public_key, key_provider.clone()) + ServerEncryptor::create(serialized_encapsulated_public_key, encryption_key.clone()) .expect("couldn't create server encryptor"), ); } @@ -186,17 +186,17 @@ fn test_encryptor() { assert_eq!(TEST_RESPONSE_ASSOCIATED_DATA, response_associated_data); } -struct TestEncryptionKeyProvider { +struct TestEncryptionKey { key_pair: KeyPair, } -impl Default for TestEncryptionKeyProvider { +impl Default for TestEncryptionKey { fn default() -> Self { Self::new() } } -impl TestEncryptionKeyProvider { +impl TestEncryptionKey { pub fn new() -> Self { Self { key_pair: KeyPair::generate(), @@ -209,7 +209,7 @@ impl TestEncryptionKeyProvider { } #[async_trait] -impl AsyncRecipientContextGenerator for TestEncryptionKeyProvider { +impl AsyncEncryptionKeyHandle for TestEncryptionKey { async fn generate_recipient_context( &self, encapsulated_public_key: &[u8], @@ -221,12 +221,12 @@ impl AsyncRecipientContextGenerator for TestEncryptionKeyProvider { #[tokio::test] async fn test_async_encryptor() { - let key_provider = TestEncryptionKeyProvider::new(); - let serialized_server_public_key = key_provider.get_serialized_public_key(); + let encryption_key = TestEncryptionKey::new(); + let serialized_server_public_key = encryption_key.get_serialized_public_key(); let mut client_encryptor = ClientEncryptor::create(&serialized_server_public_key) .expect("couldn't create client encryptor"); - let mut server_encryptor = AsyncServerEncryptor::new(&key_provider); + let mut server_encryptor = AsyncServerEncryptor::new(&encryption_key); let encrypted_request = client_encryptor .encrypt(TEST_REQUEST_MESSAGE, TEST_REQUEST_ASSOCIATED_DATA) diff --git a/oak_functions_containers_app/src/lib.rs b/oak_functions_containers_app/src/lib.rs index 5cb4bf3f280..d3dca29d876 100644 --- a/oak_functions_containers_app/src/lib.rs +++ b/oak_functions_containers_app/src/lib.rs @@ -23,7 +23,7 @@ use std::{ use anyhow::Context; use oak_attestation::handler::AsyncEncryptionHandler; -use oak_crypto::encryptor::AsyncRecipientContextGenerator; +use oak_crypto::encryptor::AsyncEncryptionKeyHandle; use oak_functions_service::{ instance::OakFunctionsInstance, proto::oak::functions::{ @@ -61,13 +61,13 @@ pub mod proto { } // Instance of the OakFunctions service for Oak Containers. -pub struct OakFunctionsContainersService { +pub struct OakFunctionsContainersService { instance: OnceLock, encryption_key_handle: Arc, observer: Option>, } -impl OakFunctionsContainersService { +impl OakFunctionsContainersService { pub fn new( encryption_key_handle: Arc, observer: Option>, @@ -110,7 +110,7 @@ fn map_status(status: micro_rpc::Status) -> tonic::Status { } #[tonic::async_trait] -impl OakFunctions +impl OakFunctions for OakFunctionsContainersService { async fn initialize( @@ -375,7 +375,7 @@ static GRPC_SUCCESS: http::header::HeaderValue = http::header::HeaderValue::from const GRPC_STATUS_HEADER_CODE: &str = "grpc-status"; // Starts up and serves an OakFunctionsContainersService instance from the provided TCP listener. -pub async fn serve( +pub async fn serve( listener: TcpListener, encryption_key_handle: Arc, meter: Meter, diff --git a/oak_functions_service/src/lib.rs b/oak_functions_service/src/lib.rs index 613f9f10a5b..0b4ffb6b923 100644 --- a/oak_functions_service/src/lib.rs +++ b/oak_functions_service/src/lib.rs @@ -47,6 +47,7 @@ use alloc::{format, string::ToString, sync::Arc, vec::Vec}; use instance::OakFunctionsInstance; use oak_attestation::handler::EncryptionHandler; use oak_core::sync::OnceCell; +use oak_crypto::encryptor::EncryptionKeyHandle; use prost::Message; use proto::oak::functions::{ AbortNextLookupDataResponse, Empty, ExtendNextLookupDataRequest, ExtendNextLookupDataResponse, @@ -61,7 +62,7 @@ pub trait Observer { } pub struct OakFunctionsService< - EKH: oak_restricted_kernel_sdk::EncryptionKeyHandle + 'static, + EKH: EncryptionKeyHandle + 'static, EP: oak_restricted_kernel_sdk::EvidenceProvider, > { evidence_provider: EP, @@ -70,10 +71,8 @@ pub struct OakFunctionsService< observer: Option>, } -impl< - EKH: oak_restricted_kernel_sdk::EncryptionKeyHandle + 'static, - EP: oak_restricted_kernel_sdk::EvidenceProvider, - > OakFunctionsService +impl + OakFunctionsService { pub fn new( evidence_provider: EP, @@ -118,10 +117,8 @@ impl< } } -impl< - EKH: oak_restricted_kernel_sdk::EncryptionKeyHandle + 'static, - EP: oak_restricted_kernel_sdk::EvidenceProvider, - > OakFunctions for OakFunctionsService +impl + OakFunctions for OakFunctionsService { fn initialize( &self, diff --git a/oak_restricted_kernel_sdk/src/dice.rs b/oak_restricted_kernel_sdk/src/dice.rs index fdc7a8add25..4db1f1be5d0 100644 --- a/oak_restricted_kernel_sdk/src/dice.rs +++ b/oak_restricted_kernel_sdk/src/dice.rs @@ -15,10 +15,9 @@ // use anyhow::Ok; -pub use oak_crypto::encryptor::EncryptionKeyHandle; use oak_crypto::{ - encryptor::{EncryptionKeyProvider, RecipientContextGenerator}, - hpke::RecipientContext as SessionKeys, + encryptor::{EncryptionKeyHandle, EncryptionKeyProvider}, + hpke::RecipientContext, }; use oak_dice::evidence::{Evidence, RestrictedKernelDiceData, P256_PRIVATE_KEY_SIZE}; use oak_restricted_kernel_interface::{syscall::read, DICE_DATA_FD}; @@ -178,7 +177,10 @@ impl InstanceEncryptionKeyHandle { } impl EncryptionKeyHandle for InstanceEncryptionKeyHandle { - fn generate_session_keys(&self, encapsulated_public_key: &[u8]) -> anyhow::Result { + fn generate_recipient_context( + &self, + encapsulated_public_key: &[u8], + ) -> anyhow::Result { self.key.generate_recipient_context(encapsulated_public_key) } } @@ -207,7 +209,10 @@ impl MockEncryptionKeyHandle { #[cfg(feature = "mock_attestation")] impl EncryptionKeyHandle for MockEncryptionKeyHandle { - fn generate_session_keys(&self, encapsulated_public_key: &[u8]) -> anyhow::Result { + fn generate_recipient_context( + &self, + encapsulated_public_key: &[u8], + ) -> anyhow::Result { self.key.generate_recipient_context(encapsulated_public_key) } } From e476759fc4f7408b0a9c1a958cb69e939b9a3b25 Mon Sep 17 00:00:00 2001 From: Ivan Petrov Date: Fri, 19 Jan 2024 15:18:15 +0000 Subject: [PATCH 12/42] Rename RecipientContextGenerator into EncryptionKeyHandle in C++ (#4673) Ref https://github.com/project-oak/oak/issues/4490 --- cc/client/BUILD | 2 +- cc/client/client_test.cc | 19 +++++++++---------- cc/containers/hello_world_trusted_app/BUILD | 2 +- .../orchestrator_client.h | 4 ++-- cc/crypto/BUILD | 10 +++++----- ...tion_key_provider.cc => encryption_key.cc} | 2 +- ...yption_key_provider.h => encryption_key.h} | 12 ++++++------ cc/crypto/encryptor_test.cc | 18 +++++++++--------- cc/crypto/server_encryptor.cc | 2 +- cc/crypto/server_encryptor.h | 10 +++++----- 10 files changed, 40 insertions(+), 41 deletions(-) rename cc/crypto/{encryption_key_provider.cc => encryption_key.cc} (96%) rename cc/crypto/{encryption_key_provider.h => encryption_key.h} (83%) diff --git a/cc/client/BUILD b/cc/client/BUILD index dda906bbf8c..99f919867a8 100644 --- a/cc/client/BUILD +++ b/cc/client/BUILD @@ -46,7 +46,7 @@ cc_test( srcs = ["client_test.cc"], deps = [ ":client", - "//cc/crypto:encryption_key_provider", + "//cc/crypto:encryption_key", "//cc/crypto:server_encryptor", "//cc/crypto/hpke:recipient_context", "//cc/remote_attestation:insecure_attestation_verifier", diff --git a/cc/client/client_test.cc b/cc/client/client_test.cc index 2694e2cc095..a3c963ba84b 100644 --- a/cc/client/client_test.cc +++ b/cc/client/client_test.cc @@ -23,7 +23,7 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" -#include "cc/crypto/encryption_key_provider.h" +#include "cc/crypto/encryption_key.h" #include "cc/crypto/hpke/recipient_context.h" #include "cc/crypto/server_encryptor.h" #include "cc/remote_attestation/insecure_attestation_verifier.h" @@ -55,25 +55,24 @@ constexpr uint8_t kTestSessionSize = 8; class TestTransport : public TransportWrapper { public: static absl::StatusOr> Create() { - auto encryption_key_provider = EncryptionKeyProvider::Create(); - if (!encryption_key_provider.ok()) { - return encryption_key_provider.status(); + auto encryption_key = EncryptionKeyProvider::Create(); + if (!encryption_key.ok()) { + return encryption_key.status(); } - return std::make_unique(*encryption_key_provider); + return std::make_unique(*encryption_key); } - explicit TestTransport(EncryptionKeyProvider encryption_key_provider) - : encryption_key_provider_(encryption_key_provider) {} + explicit TestTransport(EncryptionKeyProvider encryption_key) : encryption_key_(encryption_key) {} absl::StatusOr GetEvidence() override { AttestationBundle endorsed_evidence; endorsed_evidence.mutable_attestation_evidence()->set_encryption_public_key( - encryption_key_provider_.GetSerializedPublicKey()); + encryption_key_.GetSerializedPublicKey()); return endorsed_evidence; } absl::StatusOr Invoke(const EncryptedRequest& encrypted_request) override { - ServerEncryptor server_encryptor = ServerEncryptor(encryption_key_provider_); + ServerEncryptor server_encryptor = ServerEncryptor(encryption_key_); auto decrypted_request = server_encryptor.Decrypt(encrypted_request); if (!decrypted_request.ok()) { return decrypted_request.status(); @@ -89,7 +88,7 @@ class TestTransport : public TransportWrapper { } private: - EncryptionKeyProvider encryption_key_provider_; + EncryptionKeyProvider encryption_key_; }; // Client can process attestation evidence and invoke the backend. diff --git a/cc/containers/hello_world_trusted_app/BUILD b/cc/containers/hello_world_trusted_app/BUILD index 3f3489c12d5..5b15f2d88e0 100644 --- a/cc/containers/hello_world_trusted_app/BUILD +++ b/cc/containers/hello_world_trusted_app/BUILD @@ -41,7 +41,7 @@ cc_library( srcs = ["orchestrator_client.cc"], hdrs = ["orchestrator_client.h"], deps = [ - "//cc/crypto:encryption_key_provider", + "//cc/crypto:encryption_key", "//cc/crypto/hpke:recipient_context", "//oak_containers/proto:interfaces_cc_grpc", "//oak_containers/proto:interfaces_cc_proto", diff --git a/cc/containers/hello_world_trusted_app/orchestrator_client.h b/cc/containers/hello_world_trusted_app/orchestrator_client.h index fcb748d820b..745fc89fc8a 100644 --- a/cc/containers/hello_world_trusted_app/orchestrator_client.h +++ b/cc/containers/hello_world_trusted_app/orchestrator_client.h @@ -22,7 +22,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/string_view.h" -#include "cc/crypto/encryption_key_provider.h" +#include "cc/crypto/encryption_key.h" #include "cc/crypto/hpke/recipient_context.h" #include "grpcpp/channel.h" #include "oak_containers/proto/interfaces.grpc.pb.h" @@ -31,7 +31,7 @@ namespace oak::oak_containers_hello_world_trusted_app { -class OrchestratorClient : public crypto::RecipientContextGenerator { +class OrchestratorClient : public crypto::EncryptionKeyHandle { public: OrchestratorClient(); diff --git a/cc/crypto/BUILD b/cc/crypto/BUILD index 1737ecc1517..05a1820ef33 100644 --- a/cc/crypto/BUILD +++ b/cc/crypto/BUILD @@ -39,7 +39,7 @@ cc_library( hdrs = ["server_encryptor.h"], deps = [ ":common", - ":encryption_key_provider", + ":encryption_key", "//cc/crypto/hpke:recipient_context", "//cc/crypto/hpke:utils", "//oak_crypto/proto/v1:crypto_cc_proto", @@ -50,9 +50,9 @@ cc_library( ) cc_library( - name = "encryption_key_provider", - srcs = ["encryption_key_provider.cc"], - hdrs = ["encryption_key_provider.h"], + name = "encryption_key", + srcs = ["encryption_key.cc"], + hdrs = ["encryption_key.h"], deps = [ ":common", "//cc/crypto/hpke:recipient_context", @@ -77,7 +77,7 @@ cc_test( deps = [ ":client_encryptor", ":common", - ":encryption_key_provider", + ":encryption_key", ":server_encryptor", "//cc/crypto/hpke:recipient_context", "@com_google_absl//absl/strings", diff --git a/cc/crypto/encryption_key_provider.cc b/cc/crypto/encryption_key.cc similarity index 96% rename from cc/crypto/encryption_key_provider.cc rename to cc/crypto/encryption_key.cc index 8375d29ee2f..3b3f78805c9 100644 --- a/cc/crypto/encryption_key_provider.cc +++ b/cc/crypto/encryption_key.cc @@ -14,7 +14,7 @@ * limitations under the License. */ -#include "cc/crypto/encryption_key_provider.h" +#include "cc/crypto/encryption_key.h" #include diff --git a/cc/crypto/encryption_key_provider.h b/cc/crypto/encryption_key.h similarity index 83% rename from cc/crypto/encryption_key_provider.h rename to cc/crypto/encryption_key.h index bdc06e8878c..04516f6f7a8 100644 --- a/cc/crypto/encryption_key_provider.h +++ b/cc/crypto/encryption_key.h @@ -14,8 +14,8 @@ * limitations under the License. */ -#ifndef CC_CRYPTO_ENCRYPTION_KEY_PROVIDER_H_ -#define CC_CRYPTO_ENCRYPTION_KEY_PROVIDER_H_ +#ifndef CC_CRYPTO_ENCRYPTION_KEY_H_ +#define CC_CRYPTO_ENCRYPTION_KEY_H_ #include #include @@ -27,15 +27,15 @@ namespace oak::crypto { -class RecipientContextGenerator { +class EncryptionKeyHandle { public: virtual absl::StatusOr> GenerateRecipientContext( absl::string_view serialized_encapsulated_public_key) = 0; - virtual ~RecipientContextGenerator() = default; + virtual ~EncryptionKeyHandle() = default; }; -class EncryptionKeyProvider : public RecipientContextGenerator { +class EncryptionKeyProvider : public EncryptionKeyHandle { public: static absl::StatusOr Create(); @@ -52,4 +52,4 @@ class EncryptionKeyProvider : public RecipientContextGenerator { } // namespace oak::crypto -#endif // CC_CRYPTO_ENCRYPTION_KEY_PROVIDER_H_ +#endif // CC_CRYPTO_ENCRYPTION_KEY_H_ diff --git a/cc/crypto/encryptor_test.cc b/cc/crypto/encryptor_test.cc index cac6bd77b82..4e1b4eac87d 100644 --- a/cc/crypto/encryptor_test.cc +++ b/cc/crypto/encryptor_test.cc @@ -18,7 +18,7 @@ #include "absl/strings/string_view.h" #include "cc/crypto/client_encryptor.h" -#include "cc/crypto/encryption_key_provider.h" +#include "cc/crypto/encryption_key.h" #include "cc/crypto/hpke/recipient_context.h" #include "cc/crypto/server_encryptor.h" #include "gmock/gmock.h" @@ -33,12 +33,12 @@ constexpr absl::string_view kOakHPKEInfoTest = "Oak Hybrid Public Key Encryption // Client Encryptor and Server Encryptor can communicate. TEST(EncryptorTest, ClientEncryptorAndServerEncryptorCommunicateSuccess) { // Set up client and server encryptors. - auto encryption_key_provider = EncryptionKeyProvider::Create(); - ASSERT_TRUE(encryption_key_provider.ok()); - std::string public_key = encryption_key_provider->GetSerializedPublicKey(); + auto encryption_key = EncryptionKeyProvider::Create(); + ASSERT_TRUE(encryption_key.ok()); + std::string public_key = encryption_key->GetSerializedPublicKey(); auto client_encryptor = ClientEncryptor::Create(public_key); ASSERT_TRUE(client_encryptor.ok()); - ServerEncryptor server_encryptor = ServerEncryptor(*encryption_key_provider); + ServerEncryptor server_encryptor = ServerEncryptor(*encryption_key); // Here we have the client send 2 encrypted messages to the server to ensure that nonce's align // for multi-message communication. @@ -85,14 +85,14 @@ TEST(EncryptorTest, ClientEncryptorAndServerEncryptorCommunicateSuccess) { TEST(EncryptorTest, ClientEncryptorAndServerEncryptorCommunicateMismatchPublicKeysFailure) { // Set up client and server encryptors. - auto encryption_key_provider = EncryptionKeyProvider::Create(); - ASSERT_TRUE(encryption_key_provider.ok()); - std::string wrong_public_key = encryption_key_provider->GetSerializedPublicKey(); + auto encryption_key = EncryptionKeyProvider::Create(); + ASSERT_TRUE(encryption_key.ok()); + std::string wrong_public_key = encryption_key->GetSerializedPublicKey(); // Edit the public key that the client uses to make it incorrect. wrong_public_key[0] = (wrong_public_key[0] + 1) % 128; auto client_encryptor = ClientEncryptor::Create(wrong_public_key); ASSERT_TRUE(client_encryptor.ok()); - ServerEncryptor server_encryptor = ServerEncryptor(*encryption_key_provider); + ServerEncryptor server_encryptor = ServerEncryptor(*encryption_key); std::string client_plaintext_message = "Hello server"; diff --git a/cc/crypto/server_encryptor.cc b/cc/crypto/server_encryptor.cc index 70511025574..ffe24affc59 100644 --- a/cc/crypto/server_encryptor.cc +++ b/cc/crypto/server_encryptor.cc @@ -97,7 +97,7 @@ absl::Status ServerEncryptor::InitializeRecipientContexts(const EncryptedRequest // Create recipient contexts. absl::StatusOr> recipient_context = - recipient_context_generator_.GenerateRecipientContext(serialized_encapsulated_public_key); + encryption_key_handle_.GenerateRecipientContext(serialized_encapsulated_public_key); if (!recipient_context.ok()) { return recipient_context.status(); } diff --git a/cc/crypto/server_encryptor.h b/cc/crypto/server_encryptor.h index 438e83c781c..bbf635e444b 100644 --- a/cc/crypto/server_encryptor.h +++ b/cc/crypto/server_encryptor.h @@ -25,7 +25,7 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "cc/crypto/common.h" -#include "cc/crypto/encryption_key_provider.h" +#include "cc/crypto/encryption_key.h" #include "cc/crypto/hpke/recipient_context.h" #include "oak_crypto/proto/v1/crypto.pb.h" @@ -40,10 +40,10 @@ namespace oak::crypto { class ServerEncryptor { public: // Constructor for `ServerEncryptor`. - // `RecipientContextGenerator` argument is a long-term object containing the private key and + // `EncryptionKeyHandle` argument is a long-term object containing the private key and // should outlive the per-session `ServerEncryptor` object. - ServerEncryptor(RecipientContextGenerator& recipient_context_generator) - : recipient_context_generator_(recipient_context_generator), recipient_context_(nullptr){}; + ServerEncryptor(EncryptionKeyHandle& encryption_key_handle) + : encryption_key_handle_(encryption_key_handle), recipient_context_(nullptr){}; // Decrypts a [`EncryptedRequest`] proto message using AEAD. // @@ -60,7 +60,7 @@ class ServerEncryptor { absl::string_view associated_data); private: - RecipientContextGenerator& recipient_context_generator_; + EncryptionKeyHandle& encryption_key_handle_; std::unique_ptr recipient_context_; absl::Status InitializeRecipientContexts(const oak::crypto::v1::EncryptedRequest& request); From 499bd5874efdbef5d772a6079428176cd5e28265 Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Fri, 19 Jan 2024 16:14:09 +0000 Subject: [PATCH 13/42] Create Oak client only once in benchmarks (#4670) It still takes 9ms for the invocation to complete, but at least we can measure optimizations in the service impl now. Ref #3757 --- Cargo.lock | 2 ++ oak_functions/examples/echo/module/Cargo.toml | 4 ++-- .../examples/key_value_lookup/module/Cargo.toml | 8 +++++--- .../examples/key_value_lookup/module/src/tests.rs | 11 +++++++++-- .../examples/weather_lookup/module/Cargo.toml | 8 ++++---- 5 files changed, 22 insertions(+), 11 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index fdde07caed2..659fb8897c1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1517,7 +1517,9 @@ dependencies = [ "http", "log", "maplit", + "oak_client", "oak_functions_abi", + "oak_functions_client", "oak_functions_sdk", "oak_functions_test_utils", "tokio", diff --git a/oak_functions/examples/echo/module/Cargo.toml b/oak_functions/examples/echo/module/Cargo.toml index b3d500c54b3..4877ab5d9d7 100644 --- a/oak_functions/examples/echo/module/Cargo.toml +++ b/oak_functions/examples/echo/module/Cargo.toml @@ -9,7 +9,7 @@ license = "Apache-2.0" crate-type = ["cdylib", "rlib"] [dependencies] -oak_functions_sdk = { path = "../../../../oak_functions_sdk" } +oak_functions_sdk = { workspace = true } [dev-dependencies] -oak_functions_abi = { path = "../../../../oak_functions_abi" } +oak_functions_abi = { workspace = true } diff --git a/oak_functions/examples/key_value_lookup/module/Cargo.toml b/oak_functions/examples/key_value_lookup/module/Cargo.toml index 1b06eb3ed3c..68d73aaba86 100644 --- a/oak_functions/examples/key_value_lookup/module/Cargo.toml +++ b/oak_functions/examples/key_value_lookup/module/Cargo.toml @@ -9,14 +9,16 @@ license = "Apache-2.0" crate-type = ["cdylib", "rlib"] [dependencies] -oak_functions_sdk = { path = "../../../../oak_functions_sdk" } +oak_functions_sdk = { workspace = true } [dev-dependencies] -oak_functions_abi = { path = "../../../../oak_functions_abi" } +oak_client = { workspace = true } +oak_functions_abi = { workspace = true } +oak_functions_client = { workspace = true } hashbrown = "*" http = "*" maplit = "*" -oak_functions_test_utils = { path = "../../../../oak_functions_test_utils" } +oak_functions_test_utils = { workspace = true } tokio = { version = "*", features = [ "fs", "macros", diff --git a/oak_functions/examples/key_value_lookup/module/src/tests.rs b/oak_functions/examples/key_value_lookup/module/src/tests.rs index db24d9265fd..8caa9a6ff3f 100644 --- a/oak_functions/examples/key_value_lookup/module/src/tests.rs +++ b/oak_functions/examples/key_value_lookup/module/src/tests.rs @@ -18,6 +18,8 @@ extern crate test; use std::time::Duration; use maplit::hashmap; +use oak_client::verifier::InsecureAttestationVerifier; +use oak_functions_client::OakFunctionsClient; use oak_functions_test_utils::make_request; use test::Bencher; @@ -100,10 +102,15 @@ fn bench_wasm_handler(bencher: &mut Bencher) { // Wait for the server to start up. std::thread::sleep(Duration::from_secs(20)); + let uri = format!("http://localhost:{server_port}/"); + let mut client = runtime + .block_on(OakFunctionsClient::new(&uri, &InsecureAttestationVerifier)) + .expect("couldn't create client"); + let summary = bencher.bench(|bencher| { bencher.iter(|| { - let response = runtime.block_on(make_request(server_port, b"key_1")); - assert_eq!(b"value_1", &response.as_ref()); + let response = runtime.block_on(client.invoke(b"key_1")); + assert_eq!(b"value_1", &response.unwrap().as_ref()); }); Ok(()) }); diff --git a/oak_functions/examples/weather_lookup/module/Cargo.toml b/oak_functions/examples/weather_lookup/module/Cargo.toml index ee6d3e0a736..9a63e02c119 100644 --- a/oak_functions/examples/weather_lookup/module/Cargo.toml +++ b/oak_functions/examples/weather_lookup/module/Cargo.toml @@ -9,17 +9,17 @@ license = "Apache-2.0" crate-type = ["cdylib", "rlib"] [dependencies] -location_utils = { path = "../../../location_utils" } -oak_functions_sdk = { path = "../../../../oak_functions_sdk" } +location_utils = { workspace = true } +oak_functions_sdk = { workspace = true } serde = { version = "*", features = ["derive"] } serde_json = "*" [dev-dependencies] lookup_data_generator = { path = "../../../lookup_data_generator" } -oak_functions_abi = { path = "../../../../oak_functions_abi" } +oak_functions_abi = { workspace = true } maplit = "*" rand = "*" -oak_functions_test_utils = { path = "../../../../oak_functions_test_utils" } +oak_functions_test_utils = { workspace = true } tokio = { version = "*", features = [ "fs", "macros", From c01c2a14408bcdf3bc2d9d0da51f86c8cc8fcd01 Mon Sep 17 00:00:00 2001 From: Andri Saar Date: Thu, 18 Jan 2024 17:28:37 +0000 Subject: [PATCH 14/42] Set the max incoming packet size to 1 GiB for Oak Functions gRPC --- oak_functions_containers_app/src/lib.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/oak_functions_containers_app/src/lib.rs b/oak_functions_containers_app/src/lib.rs index d3dca29d876..a9b3f4a639c 100644 --- a/oak_functions_containers_app/src/lib.rs +++ b/oak_functions_containers_app/src/lib.rs @@ -374,6 +374,10 @@ static GRPC_SUCCESS: http::header::HeaderValue = http::header::HeaderValue::from // Equivalent to `tonic::status::GRPC_STATUS_HEADER_CODE`. const GRPC_STATUS_HEADER_CODE: &str = "grpc-status"; +// Tonic limits the incoming RPC size to 4 MB by default; bump it up to 1 GiB. We're not sending +// traffic over a "real" network anyway, after all. +const MAX_DECODING_MESSAGE_SIZE: usize = 1024 * 1024 * 1024; + // Starts up and serves an OakFunctionsContainersService instance from the provided TCP listener. pub async fn serve( listener: TcpListener, @@ -398,10 +402,13 @@ pub async fn serve( ) .layer(tower::load_shed::LoadShedLayer::new()) .layer(MonitoringLayer::new(meter.clone())) - .add_service(OakFunctionsServer::new(OakFunctionsContainersService::new( - encryption_key_handle, - Some(Arc::new(OtelObserver::new(meter))), - ))) + .add_service( + OakFunctionsServer::new(OakFunctionsContainersService::new( + encryption_key_handle, + Some(Arc::new(OtelObserver::new(meter))), + )) + .max_decoding_message_size(MAX_DECODING_MESSAGE_SIZE), + ) .serve_with_incoming(TcpListenerStream::new(listener)) .await .context("failed to start up the service") From d97e3830e7b8a7939632661ae36bc2d880e41400 Mon Sep 17 00:00:00 2001 From: Andri Saar Date: Thu, 18 Jan 2024 17:19:55 +0000 Subject: [PATCH 15/42] Rename some attributes when we export RPC metrics --- oak_functions_containers_app/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/oak_functions_containers_app/src/lib.rs b/oak_functions_containers_app/src/lib.rs index a9b3f4a639c..62a32f3bb01 100644 --- a/oak_functions_containers_app/src/lib.rs +++ b/oak_functions_containers_app/src/lib.rs @@ -283,10 +283,10 @@ where let mut attributes = Vec::new(); let mut parts = req.uri().path().rsplitn(3, '/'); if let Some(method) = parts.next() { - attributes.push(KeyValue::new("method", method.to_string())); + attributes.push(KeyValue::new("rpc_method", method.to_string())); } if let Some(service) = parts.next() { - attributes.push(KeyValue::new("service", service.to_string())); + attributes.push(KeyValue::new("rpc_service_name", service.to_string())); } // copied from the example in `tower::Service` to guarantee that `poll_ready` has been From ea794e3f3e7121a86d387c11792f3834e8079f42 Mon Sep 17 00:00:00 2001 From: conradgrobler <58467069+conradgrobler@users.noreply.github.com> Date: Fri, 19 Jan 2024 17:49:18 +0000 Subject: [PATCH 16/42] Stop generating provenances for the ELF Restricted Kernel (#4691) --- .github/workflows/provenance.yaml | 2 -- buildconfigs/oak_restricted_kernel_bin.toml | 12 ------------ .../oak_restricted_kernel_simple_io_bin.toml | 15 --------------- kokoro/build_binaries_rust.sh | 2 -- 4 files changed, 31 deletions(-) delete mode 100644 buildconfigs/oak_restricted_kernel_bin.toml delete mode 100644 buildconfigs/oak_restricted_kernel_simple_io_bin.toml diff --git a/.github/workflows/provenance.yaml b/.github/workflows/provenance.yaml index 8121081ef71..00e29027123 100644 --- a/.github/workflows/provenance.yaml +++ b/.github/workflows/provenance.yaml @@ -27,8 +27,6 @@ jobs: - buildconfigs/key_xor_test_app.toml - buildconfigs/oak_echo_enclave_app.toml - buildconfigs/oak_echo_raw_enclave_app.toml - - buildconfigs/oak_restricted_kernel_bin.toml - - buildconfigs/oak_restricted_kernel_simple_io_bin.toml - buildconfigs/oak_restricted_kernel_simple_io_wrapper.toml - buildconfigs/oak_functions_enclave_app.toml - buildconfigs/oak_functions_insecure_enclave_app.toml diff --git a/buildconfigs/oak_restricted_kernel_bin.toml b/buildconfigs/oak_restricted_kernel_bin.toml deleted file mode 100644 index 636fb3c555e..00000000000 --- a/buildconfigs/oak_restricted_kernel_bin.toml +++ /dev/null @@ -1,12 +0,0 @@ -command = [ - "nix", - "develop", - ".#rust", - "--command", - "env", - "--chdir=oak_restricted_kernel_bin", - "cargo", - "build", - "--release" -] -artifact_path = "./oak_restricted_kernel_bin/target/x86_64-unknown-none/release/oak_restricted_kernel_bin" diff --git a/buildconfigs/oak_restricted_kernel_simple_io_bin.toml b/buildconfigs/oak_restricted_kernel_simple_io_bin.toml deleted file mode 100644 index 783bf1d4483..00000000000 --- a/buildconfigs/oak_restricted_kernel_simple_io_bin.toml +++ /dev/null @@ -1,15 +0,0 @@ -command = [ - "nix", - "develop", - ".#rust", - "--command", - "env", - "--chdir=oak_restricted_kernel_bin", - "cargo", - "build", - "--release", - "--no-default-features", - "--features", - "simple_io_channel" -] -artifact_path = "./oak_restricted_kernel_bin/target/x86_64-unknown-none/release/oak_restricted_kernel_simple_io_bin" diff --git a/kokoro/build_binaries_rust.sh b/kokoro/build_binaries_rust.sh index 86230a2285a..afac84422da 100755 --- a/kokoro/build_binaries_rust.sh +++ b/kokoro/build_binaries_rust.sh @@ -30,8 +30,6 @@ touch "${KOKORO_ARTIFACTS_DIR}/binaries/git_commit_${KOKORO_GIT_COMMIT_oak:?}" # Copy the generated binaries to Placer. The timestamps are used to convey # the creation time. readonly generated_binaries=( - ./oak_restricted_kernel_bin/target/x86_64-unknown-none/release/oak_restricted_kernel_bin - ./oak_restricted_kernel_bin/target/x86_64-unknown-none/release/oak_restricted_kernel_simple_io_bin ./oak_restricted_kernel_wrapper/target/x86_64-unknown-none/release/oak_restricted_kernel_simple_io_wrapper_bin ./stage0_bin/target/x86_64-unknown-none/release/stage0_bin ./enclave_apps/target/x86_64-unknown-none/release/key_xor_test_app From 446e4e3bcfec562217c74eb3f1261c6a7a2c0f6a Mon Sep 17 00:00:00 2001 From: jul-sh Date: Fri, 19 Jan 2024 12:54:46 -0500 Subject: [PATCH 17/42] Implement signing functionality in the rust containers SDK (#4678) --- oak_containers_sdk/src/crypto.rs | 48 +++++++++++++++++++++++++++++++- 1 file changed, 47 insertions(+), 1 deletion(-) diff --git a/oak_containers_sdk/src/crypto.rs b/oak_containers_sdk/src/crypto.rs index ab425771fae..55aea7b7149 100644 --- a/oak_containers_sdk/src/crypto.rs +++ b/oak_containers_sdk/src/crypto.rs @@ -25,7 +25,7 @@ use tower::service_fn; use crate::{ proto::oak::containers::v1::{ orchestrator_crypto_client::OrchestratorCryptoClient as GrpcOrchestratorCryptoClient, - DeriveSessionKeysRequest, KeyOrigin, + DeriveSessionKeysRequest, KeyOrigin, SignRequest, }, IGNORED_ENDPOINT_URI, ORCHESTRATOR_IPC_SOCKET, }; @@ -69,6 +69,24 @@ impl OrchestratorCryptoClient { .context("session keys weren't provided by the Orchestrator")?; Ok(context) } + + async fn sign( + &self, + key_origin: KeyOrigin, + message: Vec, + ) -> anyhow::Result { + self.inner + // TODO(#4477): Remove unnecessary copies of the Orchestrator client. + .clone() + .sign(SignRequest { + message, + key_origin: key_origin.into(), + }) + .await? + .into_inner() + .signature + .context("signature was not provided by the Orchestrator") + } } pub struct InstanceEncryptionKeyHandle { @@ -108,3 +126,31 @@ impl AsyncEncryptionKeyHandle for InstanceEncryptionKeyHandle { Ok(crypto_context) } } + +#[async_trait(?Send)] +pub trait Signer { + async fn sign(&self, message: &[u8]) -> anyhow::Result; +} + +pub struct InstanceSigner { + orchestrator_crypto_client: OrchestratorCryptoClient, +} + +impl InstanceSigner { + pub async fn create() -> anyhow::Result { + Ok(Self { + orchestrator_crypto_client: OrchestratorCryptoClient::create() + .await + .context("couldn't create Orchestrator crypto client")?, + }) + } +} + +#[async_trait(?Send)] +impl Signer for InstanceSigner { + async fn sign(&self, message: &[u8]) -> anyhow::Result { + self.orchestrator_crypto_client + .sign(KeyOrigin::Instance, message.to_vec()) + .await + } +} From d90298ffe2eb828507dd1d71bdc13d3d7050a64f Mon Sep 17 00:00:00 2001 From: Brett McLarnon Date: Fri, 19 Jan 2024 13:46:43 -0800 Subject: [PATCH 18/42] Remove oak_attestation_verification dependency on std. (#4685) The crate itself already set no_std, but it relied on a lot of std-only features in its dependencies, such as anyhow::Context and serde json serialization. Since all canonical serde json serialization libraries require std, it's necessary to manually serialize one structure. But since the structure doesn't have optional fields, nesting, or complex types, the serialization logic is straightforward. Fixes #4682 --- Cargo.lock | 14 +--- justfile | 3 +- oak_attestation_verification/Cargo.toml | 30 +++++-- oak_attestation_verification/src/claims.rs | 26 ++++-- .../src/endorsement.rs | 11 ++- oak_attestation_verification/src/rekor.rs | 83 +++++++++++-------- oak_attestation_verification/src/util.rs | 45 ++++++---- oak_attestation_verification/src/verifier.rs | 23 +++-- oak_ml_transparency/runner/Cargo.lock | 20 +---- oak_ml_transparency/runner/Cargo.toml | 4 +- 10 files changed, 145 insertions(+), 114 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 659fb8897c1..4e0388738b0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1873,6 +1873,7 @@ dependencies = [ "base64 0.21.7", "coset", "ecdsa", + "getrandom", "hex", "oak_dice", "oak_sev_guest", @@ -1880,7 +1881,6 @@ dependencies = [ "prost", "prost-build", "serde", - "serde_canonical_json", "serde_json", "sha2", "time", @@ -3413,18 +3413,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_canonical_json" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef94ee2661f3ce924fa936258393d02155fa22c5a81125016a24069e23a0465" -dependencies = [ - "itoa", - "lazy_static", - "regex", - "serde_json", -] - [[package]] name = "serde_derive" version = "1.0.195" diff --git a/justfile b/justfile index 9c93104b977..70a52d133b1 100644 --- a/justfile +++ b/justfile @@ -66,8 +66,7 @@ all_oak_functions_containers_binaries: stage0_bin stage1_cpio oak_containers_ker ensure_no_std package: cargo check --target=x86_64-unknown-none --package='{{package}}' -# TODO(#4682): Add "oak_attestation_verification" when it is no_std compatible -all_ensure_no_std: (ensure_no_std "micro_rpc") +all_ensure_no_std: (ensure_no_std "micro_rpc") (ensure_no_std "oak_attestation_verification") (ensure_no_std "oak_restricted_kernel_sdk") # Entry points for Kokoro CI. diff --git a/oak_attestation_verification/Cargo.toml b/oak_attestation_verification/Cargo.toml index 20e190007ad..edc04316e60 100644 --- a/oak_attestation_verification/Cargo.toml +++ b/oak_attestation_verification/Cargo.toml @@ -5,22 +5,36 @@ authors = ["Razieh Behjati "] edition = "2021" license = "Apache-2.0" +[features] +serialize = ["time/formatting"] + # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -anyhow = "*" -base64 = "0.21" +anyhow = { version = "*", default-features = false } +base64 = { version = "0.21", default-features = false, features = ["alloc"] } coset = { version = "*", default-features = false } ecdsa = { version = "*", features = ["pkcs8", "pem"] } -hex = "*" +getrandom = { version = "*", features = [ + # While getrandom isn't used directly, rdrand is required to support x64_64-unknown-none. + "rdrand" +] } +hex = { version = "*", default-features = false } oak_dice = { workspace = true } oak_sev_guest = { workspace = true } prost = { workspace = true } -p256 = { version = "*", features = ["ecdsa-core", "ecdsa", "pem"] } -serde = { version = "*", features = ["derive"] } -serde_canonical_json = "*" -serde_json = "*" +p256 = { version = "*", default-features = false, features = [ + "alloc", + "ecdsa-core", + "ecdsa", + "pem" +] } +serde = { version = "*", default-features = false, features = ["derive"] } +serde_json = { version = "*", default-features = false, features = ["alloc"] } sha2 = { version = "*", default-features = false } -time = { version = "0.3.28", features = ["serde", "parsing", "formatting"] } +time = { version = "0.3.28", default-features = false, features = [ + "serde", + "parsing" +] } zerocopy = "*" [build-dependencies] diff --git a/oak_attestation_verification/src/claims.rs b/oak_attestation_verification/src/claims.rs index df9d3f2eb35..e15996a90cc 100644 --- a/oak_attestation_verification/src/claims.rs +++ b/oak_attestation_verification/src/claims.rs @@ -23,8 +23,9 @@ extern crate alloc; use alloc::{collections::BTreeMap, string::String, vec::Vec}; -use anyhow::Context; -use serde::{Deserialize, Serialize}; +use serde::Deserialize; +#[cfg(feature = "serialize")] +use serde::Serialize; use time::OffsetDateTime; use crate::proto::oak::HexDigest; @@ -45,14 +46,16 @@ pub const STATEMENT_INTOTO_V01: &str = "https://in-toto.io/Statement/v0.1"; pub type DigestSet = BTreeMap; /// A software artifact identified by its name and a set of artifacts. -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct Subject { pub name: String, pub digest: DigestSet, } /// Represents a generic statement that binds a predicate to a subject. -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct Statement

{ pub _type: String, #[serde(rename = "predicateType")] @@ -70,7 +73,8 @@ pub enum InvalidClaimData { } /// Detailed content of a claim. -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct ClaimPredicate { /// URI indicating the type of the claim. It determines the meaning of /// `claimSpec` and `evidence`. @@ -95,7 +99,8 @@ pub struct ClaimPredicate { } /// Validity time range of an issued claim. -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct ClaimValidity { /// The timestamp (encoded as an Epoch time) from which the claim is /// effective. @@ -110,7 +115,8 @@ pub struct ClaimValidity { } /// Metadata about an artifact that serves as the evidence for the truth of a claim. -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct ClaimEvidence { /// Optional field specifying the role of this evidence within the claim. pub role: Option, @@ -121,14 +127,16 @@ pub struct ClaimEvidence { } /// Inner type for a simple claim with no further fields. -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct Claimless {} pub type EndorsementStatement = Statement>; /// Converts the given byte array into an endorsement statement. pub fn parse_endorsement_statement(bytes: &[u8]) -> anyhow::Result { - serde_json::from_slice(bytes).context("parsing endorsement bytes") + serde_json::from_slice(bytes) + .map_err(|error| anyhow::anyhow!("parsing endorsement bytes: {}", error)) } /// Checks that the given statement is a valid claim: diff --git a/oak_attestation_verification/src/endorsement.rs b/oak_attestation_verification/src/endorsement.rs index 7ac7fccf17d..ffe3ac6f117 100644 --- a/oak_attestation_verification/src/endorsement.rs +++ b/oak_attestation_verification/src/endorsement.rs @@ -16,7 +16,6 @@ //! Verifies binary endorsements as coming from Transparent Release. -use anyhow::Context; use base64::{prelude::BASE64_STANDARD, Engine as _}; use crate::{ @@ -81,8 +80,14 @@ pub fn verify_endorser_public_key( let actual_pem_vec = BASE64_STANDARD .decode(body.spec.signature.public_key.content) - .context("couldn't base64-decode public key bytes from server")?; - let actual_pem = core::str::from_utf8(&actual_pem_vec)?; + .map_err(|error| { + anyhow::anyhow!( + "couldn't base64-decode public key bytes from server: {}", + error + ) + })?; + let actual_pem = + core::str::from_utf8(&actual_pem_vec).map_err(|error| anyhow::anyhow!(error))?; let actual = convert_pem_to_raw(actual_pem)?; if !equal_keys(endorser_public_key, &actual)? { diff --git a/oak_attestation_verification/src/rekor.rs b/oak_attestation_verification/src/rekor.rs index 91fcb8c6d50..4f616a8bda1 100644 --- a/oak_attestation_verification/src/rekor.rs +++ b/oak_attestation_verification/src/rekor.rs @@ -17,19 +17,20 @@ //! This module provides structs for representing a Rekor LogEntry, as well as logic for parsing and //! verifying signatures in a Rekor LogEntry. -use alloc::{collections::BTreeMap, string::String, vec::Vec}; +use alloc::{collections::BTreeMap, format, string::String, vec::Vec}; use anyhow::Context; use base64::{prelude::BASE64_STANDARD, Engine as _}; -use serde::{Deserialize, Serialize}; -use serde_canonical_json::CanonicalFormatter; -use serde_json::Serializer; +use serde::Deserialize; +#[cfg(feature = "serialize")] +use serde::Serialize; use crate::util::{convert_pem_to_raw, hash_sha2_256, verify_signature_raw}; /// Struct representing a Rekor LogEntry. /// Based on -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct LogEntry { /// We cannot directly use the type `Body` here, since body is Base64-encoded. #[serde(rename = "body")] @@ -55,7 +56,8 @@ pub struct LogEntry { } /// Struct representing the body in a Rekor LogEntry. -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct Body { #[serde(rename = "apiVersion")] pub api_version: String, @@ -65,7 +67,8 @@ pub struct Body { /// Struct representing the `spec` in the body of a Rekor LogEntry. /// Based on -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct Spec { pub data: Data, pub signature: GenericSignature, @@ -73,14 +76,16 @@ pub struct Spec { /// Struct representing the hashed data in the body of a Rekor LogEntry. /// Based on -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct Data { pub hash: Hash, } /// Struct representing a hash digest. /// Based on -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct Hash { pub algorithm: String, pub value: String, @@ -88,7 +93,8 @@ pub struct Hash { /// Struct representing a signature in the body of a Rekor LogEntry. /// Based on -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct GenericSignature { /// Base64 content that is signed. pub content: String, @@ -99,7 +105,8 @@ pub struct GenericSignature { /// Struct representing a public key included in the body of a Rekor LogEntry. /// Based on -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct PublicKey { /// Base64 content of a public key. pub content: String, @@ -108,7 +115,8 @@ pub struct PublicKey { /// Struct representing a verification object in a Rekor LogEntry. The verification object in Rekor /// also contains an inclusion proof. Since we currently don't verify the inclusion proof in the /// client, it is omitted from this struct. -#[derive(Debug, Deserialize, PartialEq, Serialize)] +#[derive(Debug, Deserialize, PartialEq)] +#[cfg_attr(feature = "serialize", derive(Serialize))] pub struct LogEntryVerification { // Base64-encoded signature over the body, integratedTime, logID, and logIndex. #[serde(rename = "signedEntryTimestamp")] @@ -136,22 +144,21 @@ impl TryFrom<&LogEntry> for RekorSignatureBundle { type Error = anyhow::Error; fn try_from(log_entry: &LogEntry) -> anyhow::Result { - // Create a copy of the LogEntry, but skip the verification. - let entry_subset = LogEntry { - body: log_entry.body.clone(), - integrated_time: log_entry.integrated_time, - log_id: log_entry.log_id.clone(), - log_index: log_entry.log_index, - verification: None, - }; - - // Canonicalized JSON document that is signed. Canonicalization should follow the RFC 8785 - // rules. - let mut serializer = Serializer::with_formatter(Vec::new(), CanonicalFormatter::new()); - entry_subset - .serialize(&mut serializer) - .context("Failed to serialize Rekor signed payload to JSON")?; - let signed_json_bytes: Vec = serializer.into_inner(); + // Canonicalized JSON document that is signed; note that verification is omitted. + // Canonicalization should follow the RFC 8785 rules. We hardcode the canonical + // serialization because serialization with serde_json requires std; if we get the + // serialization wrong (e.g., because a string contain characters requiring special + // escaping), the signature will fail to match. Thus, this should result in incorrectly + // rejecting some valid signature bundles, not incorrectly accepting valid ones. + anyhow::ensure!(!log_entry.body.contains('"')); + anyhow::ensure!(!log_entry.log_id.contains('"')); + let canonicalized = format!( + r#"{{"body":"{body}","integratedTime":{time},"logID":"{id}","logIndex":{index}}}"#, + body = &log_entry.body, + time = log_entry.integrated_time, + id = &log_entry.log_id, + index = log_entry.log_index + ); // Extract the signature from the LogEntry. let sig_base64 = log_entry @@ -162,10 +169,10 @@ impl TryFrom<&LogEntry> for RekorSignatureBundle { .clone(); let signature = BASE64_STANDARD .decode(sig_base64) - .context("couldn't decode Base64 signature")?; + .map_err(|error| anyhow::anyhow!("couldn't decode Base64 signature: {}", error))?; Ok(Self { - canonicalized: signed_json_bytes, + canonicalized: canonicalized.as_bytes().to_vec(), signature, }) } @@ -195,15 +202,18 @@ pub fn verify_rekor_log_entry( /// instance of `Body`. pub fn get_rekor_log_entry_body(log_entry: &[u8]) -> anyhow::Result { let parsed: BTreeMap = - serde_json::from_slice(log_entry).context("couldn't parse bytes into a LogEntry object")?; + serde_json::from_slice(log_entry).map_err(|error| { + anyhow::anyhow!("couldn't parse bytes into a LogEntry object: {}", error) + })?; let entry = parsed.values().next().context("no entry in the map")?; // Parse base64-encoded entry.body into an instance of Body. let body_bytes: Vec = BASE64_STANDARD .decode(entry.body.clone()) - .context("couldn't decode Base64 signature")?; + .map_err(|error| anyhow::anyhow!("couldn't decode Base64 signature: {}", error))?; - serde_json::from_slice(&body_bytes).context("couldn't parse bytes into a Body object") + serde_json::from_slice(&body_bytes) + .map_err(|error| anyhow::anyhow!("couldn't parse bytes into a Body object: {}", error)) } /// Parses a blob into a Rekor log entry and verifies the signature in @@ -254,7 +264,8 @@ pub fn verify_rekor_body(body: &Body, contents_bytes: &[u8]) -> anyhow::Result<( let public_key_pem_vec: Vec = BASE64_STANDARD .decode(body.spec.signature.public_key.content.as_bytes()) .expect("couldn't decode the public key in the Rekor LogEntry body"); - let public_key_pem = core::str::from_utf8(&public_key_pem_vec)?; + let public_key_pem = + core::str::from_utf8(&public_key_pem_vec).map_err(|error| anyhow::anyhow!(error))?; let public_key = convert_pem_to_raw(public_key_pem)?; verify_signature_raw(&signature, contents_bytes, &public_key) @@ -263,7 +274,9 @@ pub fn verify_rekor_body(body: &Body, contents_bytes: &[u8]) -> anyhow::Result<( fn rekor_signature_bundle(log_entry: &[u8]) -> anyhow::Result { let parsed: BTreeMap = - serde_json::from_slice(log_entry).context("couldn't parse bytes into a LogEntry object")?; + serde_json::from_slice(log_entry).map_err(|error| { + anyhow::anyhow!("couldn't parse bytes into a LogEntry object: {}", error) + })?; let entry = parsed.values().next().context("no entry in the map")?; RekorSignatureBundle::try_from(entry) diff --git a/oak_attestation_verification/src/util.rs b/oak_attestation_verification/src/util.rs index 73a186a0f5b..3bd6cebc9db 100644 --- a/oak_attestation_verification/src/util.rs +++ b/oak_attestation_verification/src/util.rs @@ -17,7 +17,6 @@ use alloc::{string::String, vec::Vec}; use core::{cmp::Ordering, str::FromStr}; -use anyhow::Context; use base64::{prelude::BASE64_STANDARD, Engine as _}; use ecdsa::{signature::Verifier, Signature}; use p256::ecdsa::VerifyingKey; @@ -44,7 +43,9 @@ pub fn convert_pem_to_raw(public_key_pem: &str) -> anyhow::Result> { .expect("could not find expected footer"); let remove_newlines = stripped.replace('\n', ""); - Ok(BASE64_STANDARD.decode(remove_newlines)?) + BASE64_STANDARD + .decode(remove_newlines) + .map_err(|error| anyhow::anyhow!(error)) } /// Converts a raw public key to PEM format. @@ -64,8 +65,12 @@ pub fn convert_raw_to_pem(public_key: &[u8]) -> String { /// Converts a PEM-encoded x509/PKIX public key to a verifying key. pub fn convert_pem_to_verifying_key(public_key_pem: &str) -> anyhow::Result { - VerifyingKey::from_str(public_key_pem) - .context("couldn't parse pem as a p256::ecdsa::VerifyingKey") + VerifyingKey::from_str(public_key_pem).map_err(|error| { + anyhow::anyhow!( + "couldn't parse pem as a p256::ecdsa::VerifyingKey: {}", + error + ) + }) } /// Converts a raw public key to a verifying key. @@ -90,11 +95,12 @@ pub fn verify_signature_raw( contents: &[u8], public_key: &[u8], ) -> anyhow::Result<()> { - let sig = Signature::from_der(signature).context("invalid ASN.1 signature")?; + let sig = Signature::from_der(signature) + .map_err(|error| anyhow::anyhow!("invalid ASN.1 signature: {}", error))?; let key = convert_raw_to_verifying_key(public_key)?; key.verify(contents, &sig) - .context("couldn't verify signature") + .map_err(|error| anyhow::anyhow!("couldn't verify signature: {}", error)) } pub fn hash_sha2_256(input: &[u8]) -> [u8; 32] { @@ -199,15 +205,24 @@ pub fn raw_to_hex_digest(r: &RawDigest) -> HexDigest { /// Converts hex digest to raw digest. pub fn hex_to_raw_digest(h: &HexDigest) -> anyhow::Result { let raw = RawDigest { - psha2: hex::decode(&h.psha2).context("could not decode field psha2")?, - sha1: hex::decode(&h.sha1).context("could not decode field sha1")?, - sha2_256: hex::decode(&h.sha2_256).context("could not decode field sha2_256")?, - sha2_512: hex::decode(&h.sha2_512).context("could not decode field sha2_512")?, - sha3_512: hex::decode(&h.sha3_512).context("could not decode field sha3_512")?, - sha3_384: hex::decode(&h.sha3_384).context("could not decode field sha3_384")?, - sha3_256: hex::decode(&h.sha3_256).context("could not decode field sha3_256")?, - sha3_224: hex::decode(&h.sha3_224).context("could not decode field sha3_224")?, - sha2_384: hex::decode(&h.sha2_384).context("could not decode field sha2_384")?, + psha2: hex::decode(&h.psha2) + .map_err(|error| anyhow::anyhow!("could not decode field psha2: {}", error))?, + sha1: hex::decode(&h.sha1) + .map_err(|error| anyhow::anyhow!("could not decode field sha1: {}", error))?, + sha2_256: hex::decode(&h.sha2_256) + .map_err(|error| anyhow::anyhow!("could not decode field sha2_256: {}", error))?, + sha2_512: hex::decode(&h.sha2_512) + .map_err(|error| anyhow::anyhow!("could not decode field sha2_512: {}", error))?, + sha3_512: hex::decode(&h.sha3_512) + .map_err(|error| anyhow::anyhow!("could not decode field sha3_512: {}", error))?, + sha3_384: hex::decode(&h.sha3_384) + .map_err(|error| anyhow::anyhow!("could not decode field sha3_384: {}", error))?, + sha3_256: hex::decode(&h.sha3_256) + .map_err(|error| anyhow::anyhow!("could not decode field sha3_256: {}", error))?, + sha3_224: hex::decode(&h.sha3_224) + .map_err(|error| anyhow::anyhow!("could not decode field sha3_224: {}", error))?, + sha2_384: hex::decode(&h.sha2_384) + .map_err(|error| anyhow::anyhow!("could not decode field sha2_384: {}", error))?, }; Ok(raw) diff --git a/oak_attestation_verification/src/verifier.rs b/oak_attestation_verification/src/verifier.rs index 8d69dd05b8f..04833bc3e65 100644 --- a/oak_attestation_verification/src/verifier.rs +++ b/oak_attestation_verification/src/verifier.rs @@ -136,7 +136,8 @@ pub fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result cert.verify_signature(ADDITIONAL_DATA, |signature, contents| { let sig = Signature::from_slice(signature)?; verifying_key.verify(contents, &sig) - })?; + }) + .map_err(|error| anyhow::anyhow!(error))?; let payload = cert .payload .ok_or_else(|| anyhow::anyhow!("no cert payload"))?; @@ -158,10 +159,12 @@ pub fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result let encryption_cert = coset::CoseSign1::from_slice(&appl_keys.encryption_public_key_certificate) .map_err(|_cose_err| anyhow::anyhow!("could not parse encryption certificate"))?; - encryption_cert.verify_signature(ADDITIONAL_DATA, |signature, contents| { - let sig = Signature::from_slice(signature)?; - verifying_key.verify(contents, &sig) - })?; + encryption_cert + .verify_signature(ADDITIONAL_DATA, |signature, contents| { + let sig = Signature::from_slice(signature)?; + verifying_key.verify(contents, &sig) + }) + .map_err(|error| anyhow::anyhow!(error))?; let encryption_payload = encryption_cert .payload .ok_or_else(|| anyhow::anyhow!("no encryption cert payload"))?; @@ -175,10 +178,12 @@ pub fn verify_dice_chain(evidence: &Evidence) -> anyhow::Result // Process signing certificate. let signing_cert = coset::CoseSign1::from_slice(&appl_keys.signing_public_key_certificate) .map_err(|_cose_err| anyhow::anyhow!("could not parse encryption certificate"))?; - signing_cert.verify_signature(ADDITIONAL_DATA, |signature, contents| { - let sig = Signature::from_slice(signature)?; - verifying_key.verify(contents, &sig) - })?; + signing_cert + .verify_signature(ADDITIONAL_DATA, |signature, contents| { + let sig = Signature::from_slice(signature)?; + verifying_key.verify(contents, &sig) + }) + .map_err(|error| anyhow::anyhow!(error))?; let signing_payload = signing_cert .payload .ok_or_else(|| anyhow::anyhow!("no signing cert payload"))?; diff --git a/oak_ml_transparency/runner/Cargo.lock b/oak_ml_transparency/runner/Cargo.lock index 10389ea1703..a40c2eda1aa 100644 --- a/oak_ml_transparency/runner/Cargo.lock +++ b/oak_ml_transparency/runner/Cargo.lock @@ -510,12 +510,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" -[[package]] -name = "lazy_static" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" - [[package]] name = "libc" version = "0.2.150" @@ -564,6 +558,7 @@ dependencies = [ "base64", "coset", "ecdsa", + "getrandom", "hex", "oak_dice", "oak_sev_guest", @@ -571,7 +566,6 @@ dependencies = [ "prost", "prost-build", "serde", - "serde_canonical_json", "serde_json", "sha2", "time", @@ -881,18 +875,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde_canonical_json" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ef94ee2661f3ce924fa936258393d02155fa22c5a81125016a24069e23a0465" -dependencies = [ - "itoa", - "lazy_static", - "regex", - "serde_json", -] - [[package]] name = "serde_derive" version = "1.0.193" diff --git a/oak_ml_transparency/runner/Cargo.toml b/oak_ml_transparency/runner/Cargo.toml index a58da205120..dc953d6e70c 100644 --- a/oak_ml_transparency/runner/Cargo.toml +++ b/oak_ml_transparency/runner/Cargo.toml @@ -10,7 +10,9 @@ license = "Apache-2.0" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] anyhow = "*" -oak_attestation_verification = { path = "../../oak_attestation_verification" } +oak_attestation_verification = { path = "../../oak_attestation_verification", features = [ + "serialize" +] } clap = { version = "*", features = ["derive"] } env_logger = "*" hex = "*" From e132e7561aac1bbf607e2f638b337ef7c6ad6b6c Mon Sep 17 00:00:00 2001 From: Brett McLarnon Date: Sat, 20 Jan 2024 10:25:07 -0800 Subject: [PATCH 19/42] Rename oak_attestation_verification "serialize" feature to "std". (#4694) The new name was requested in https://github.com/project-oak/oak/pull/4685#discussion_r1459674232. --- oak_attestation_verification/Cargo.toml | 2 +- oak_attestation_verification/src/claims.rs | 14 +++++++------- oak_attestation_verification/src/rekor.rs | 18 +++++++++--------- oak_ml_transparency/runner/Cargo.toml | 2 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/oak_attestation_verification/Cargo.toml b/oak_attestation_verification/Cargo.toml index edc04316e60..dfc9c4434a0 100644 --- a/oak_attestation_verification/Cargo.toml +++ b/oak_attestation_verification/Cargo.toml @@ -6,7 +6,7 @@ edition = "2021" license = "Apache-2.0" [features] -serialize = ["time/formatting"] +std = ["time/formatting"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] diff --git a/oak_attestation_verification/src/claims.rs b/oak_attestation_verification/src/claims.rs index e15996a90cc..cbfa07aca52 100644 --- a/oak_attestation_verification/src/claims.rs +++ b/oak_attestation_verification/src/claims.rs @@ -24,7 +24,7 @@ extern crate alloc; use alloc::{collections::BTreeMap, string::String, vec::Vec}; use serde::Deserialize; -#[cfg(feature = "serialize")] +#[cfg(feature = "std")] use serde::Serialize; use time::OffsetDateTime; @@ -47,7 +47,7 @@ pub type DigestSet = BTreeMap; /// A software artifact identified by its name and a set of artifacts. #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct Subject { pub name: String, pub digest: DigestSet, @@ -55,7 +55,7 @@ pub struct Subject { /// Represents a generic statement that binds a predicate to a subject. #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct Statement

{ pub _type: String, #[serde(rename = "predicateType")] @@ -74,7 +74,7 @@ pub enum InvalidClaimData { /// Detailed content of a claim. #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct ClaimPredicate { /// URI indicating the type of the claim. It determines the meaning of /// `claimSpec` and `evidence`. @@ -100,7 +100,7 @@ pub struct ClaimPredicate { /// Validity time range of an issued claim. #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct ClaimValidity { /// The timestamp (encoded as an Epoch time) from which the claim is /// effective. @@ -116,7 +116,7 @@ pub struct ClaimValidity { /// Metadata about an artifact that serves as the evidence for the truth of a claim. #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct ClaimEvidence { /// Optional field specifying the role of this evidence within the claim. pub role: Option, @@ -128,7 +128,7 @@ pub struct ClaimEvidence { /// Inner type for a simple claim with no further fields. #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct Claimless {} pub type EndorsementStatement = Statement>; diff --git a/oak_attestation_verification/src/rekor.rs b/oak_attestation_verification/src/rekor.rs index 4f616a8bda1..6fd1370d177 100644 --- a/oak_attestation_verification/src/rekor.rs +++ b/oak_attestation_verification/src/rekor.rs @@ -22,7 +22,7 @@ use alloc::{collections::BTreeMap, format, string::String, vec::Vec}; use anyhow::Context; use base64::{prelude::BASE64_STANDARD, Engine as _}; use serde::Deserialize; -#[cfg(feature = "serialize")] +#[cfg(feature = "std")] use serde::Serialize; use crate::util::{convert_pem_to_raw, hash_sha2_256, verify_signature_raw}; @@ -30,7 +30,7 @@ use crate::util::{convert_pem_to_raw, hash_sha2_256, verify_signature_raw}; /// Struct representing a Rekor LogEntry. /// Based on #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct LogEntry { /// We cannot directly use the type `Body` here, since body is Base64-encoded. #[serde(rename = "body")] @@ -57,7 +57,7 @@ pub struct LogEntry { /// Struct representing the body in a Rekor LogEntry. #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct Body { #[serde(rename = "apiVersion")] pub api_version: String, @@ -68,7 +68,7 @@ pub struct Body { /// Struct representing the `spec` in the body of a Rekor LogEntry. /// Based on #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct Spec { pub data: Data, pub signature: GenericSignature, @@ -77,7 +77,7 @@ pub struct Spec { /// Struct representing the hashed data in the body of a Rekor LogEntry. /// Based on #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct Data { pub hash: Hash, } @@ -85,7 +85,7 @@ pub struct Data { /// Struct representing a hash digest. /// Based on #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct Hash { pub algorithm: String, pub value: String, @@ -94,7 +94,7 @@ pub struct Hash { /// Struct representing a signature in the body of a Rekor LogEntry. /// Based on #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct GenericSignature { /// Base64 content that is signed. pub content: String, @@ -106,7 +106,7 @@ pub struct GenericSignature { /// Struct representing a public key included in the body of a Rekor LogEntry. /// Based on #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct PublicKey { /// Base64 content of a public key. pub content: String, @@ -116,7 +116,7 @@ pub struct PublicKey { /// also contains an inclusion proof. Since we currently don't verify the inclusion proof in the /// client, it is omitted from this struct. #[derive(Debug, Deserialize, PartialEq)] -#[cfg_attr(feature = "serialize", derive(Serialize))] +#[cfg_attr(feature = "std", derive(Serialize))] pub struct LogEntryVerification { // Base64-encoded signature over the body, integratedTime, logID, and logIndex. #[serde(rename = "signedEntryTimestamp")] diff --git a/oak_ml_transparency/runner/Cargo.toml b/oak_ml_transparency/runner/Cargo.toml index dc953d6e70c..9cbcdc7b117 100644 --- a/oak_ml_transparency/runner/Cargo.toml +++ b/oak_ml_transparency/runner/Cargo.toml @@ -11,7 +11,7 @@ license = "Apache-2.0" [dependencies] anyhow = "*" oak_attestation_verification = { path = "../../oak_attestation_verification", features = [ - "serialize" + "std" ] } clap = { version = "*", features = ["derive"] } env_logger = "*" From d6c5d9345e8e542c07438b9bba71cd08d48f8531 Mon Sep 17 00:00:00 2001 From: Brett McLarnon Date: Mon, 22 Jan 2024 09:08:46 -0800 Subject: [PATCH 20/42] Remove WORKSPACE_ROOT from oak_attestation_verification. (#4696) `WORKSPACE_ROOT` makes a crate incompatible with use from another repository because there's no way for it to be set relative to the root of the oak repository. `cargo:rerun-if-change` supports relative paths, however, which solve this problem. --- oak_attestation_verification/build.rs | 15 +++++---------- 1 file changed, 5 insertions(+), 10 deletions(-) diff --git a/oak_attestation_verification/build.rs b/oak_attestation_verification/build.rs index 422da6d9b29..c0af47365c5 100644 --- a/oak_attestation_verification/build.rs +++ b/oak_attestation_verification/build.rs @@ -16,22 +16,17 @@ fn main() -> Result<(), Box> { let proto_paths = [ - "proto/attestation/endorsement.proto", - "proto/attestation/evidence.proto", - "proto/attestation/reference_value.proto", - "proto/attestation/verification.proto", + "../proto/attestation/endorsement.proto", + "../proto/attestation/evidence.proto", + "../proto/attestation/reference_value.proto", + "../proto/attestation/verification.proto", ]; prost_build::compile_protos(&proto_paths, &[".."]).expect("proto compilation failed"); // Tell cargo to rerun this build script if the proto file has changed. // https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorerun-if-changedpath for proto_path in proto_paths.iter() { - let file_path = std::path::Path::new(proto_path); - println!( - "cargo:rerun-if-changed={}{}", - env!("WORKSPACE_ROOT"), - file_path.display() - ); + println!("cargo:rerun-if-changed={}", proto_path); } Ok(()) From a103bcad4ca2d98dbfd6af36a8794649037b0f03 Mon Sep 17 00:00:00 2001 From: Brett McLarnon Date: Mon, 22 Jan 2024 09:20:54 -0800 Subject: [PATCH 21/42] Clean up oci_runtime_bundle temporary files. (#4695) * Clean up oci_runtime_bundle temporary files. bazel doesn't handle this automatically. As-is, the right outputs are produced and there aren't any warnings or errors, but temporary files do accumulate in /tmp. They'll go away on reboot, but that may not happen that frequently on all machines. Cleaning up after ourselves is straightforward and a best practice. --- FORCE_CI | 2 +- bazel/private/oci_runtime_bundle.sh.tpl | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/FORCE_CI b/FORCE_CI index 6f4247a6255..f64f5d8d85a 100644 --- a/FORCE_CI +++ b/FORCE_CI @@ -1 +1 @@ -26 +27 diff --git a/bazel/private/oci_runtime_bundle.sh.tpl b/bazel/private/oci_runtime_bundle.sh.tpl index 8eaf749f9c8..cf54ea2479a 100644 --- a/bazel/private/oci_runtime_bundle.sh.tpl +++ b/bazel/private/oci_runtime_bundle.sh.tpl @@ -23,11 +23,13 @@ readonly YQ="{{yq}}" # Add tags to the image index generated by oci_image so that it's compatible # with umoci. readonly IMAGE_DIR=$(mktemp -d) +trap 'rm -rf -- "${IMAGE_DIR}"' EXIT cp -RL "$1/." "${IMAGE_DIR}" "${YQ}" -i -o=json '.manifests[0].annotations["org.opencontainers.image.ref.name"] = "latest"' "${IMAGE_DIR}/index.json" # Use umoci to unpack the image into a runtime bundle. readonly BUNDLE_DIR=$(mktemp -d) +trap 'rm -rf -- "${BUNDLE_DIR}"' EXIT "${UMOCI}" unpack --rootless --image "${IMAGE_DIR}" "${BUNDLE_DIR}" # Sort mount options, which umoci emits in a non-deterministic order. From 9ab970c21492c452eb54173d8f3e6dc7e6e704fd Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Mon, 22 Jan 2024 20:11:54 +0000 Subject: [PATCH 22/42] Update Nix deps (#4697) --- flake.lock | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/flake.lock b/flake.lock index 63fa86adbb4..63f2c237ecd 100644 --- a/flake.lock +++ b/flake.lock @@ -7,11 +7,11 @@ ] }, "locked": { - "lastModified": 1703439018, - "narHash": "sha256-VT+06ft/x3eMZ1MJxWzQP3zXFGcrxGo5VR2rB7t88hs=", + "lastModified": 1705625727, + "narHash": "sha256-naMq6+TNLpEiBBjc0XaCbMLYJxJXWTZz4JGSpYGgIOM=", "owner": "ipetkov", "repo": "crane", - "rev": "afdcd41180e3dfe4dac46b5ee396e3b12ccc967a", + "rev": "8f515142e805dc377cf8edb0ff75d14a11307f89", "type": "github" }, "original": { @@ -27,11 +27,11 @@ ] }, "locked": { - "lastModified": 1701680307, - "narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", + "lastModified": 1705309234, + "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", "owner": "numtide", "repo": "flake-utils", - "rev": "4022d587cbbfd70fe950c1e2083a02621806a725", + "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", "type": "github" }, "original": { @@ -42,11 +42,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1703961334, - "narHash": "sha256-M1mV/Cq+pgjk0rt6VxoyyD+O8cOUiai8t9Q6Yyq4noY=", + "lastModified": 1705856552, + "narHash": "sha256-JXfnuEf5Yd6bhMs/uvM67/joxYKoysyE3M2k6T3eWbg=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "b0d36bd0a420ecee3bc916c91886caca87c894e9", + "rev": "612f97239e2cc474c13c9dafa0df378058c5ad8d", "type": "github" }, "original": { @@ -75,11 +75,11 @@ ] }, "locked": { - "lastModified": 1704075545, - "narHash": "sha256-L3zgOuVKhPjKsVLc3yTm2YJ6+BATyZBury7wnhyc8QU=", + "lastModified": 1705889935, + "narHash": "sha256-77KPBK5e0ACNzIgJDMuptTtEqKvHBxTO3ksqXHHVO+4=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "a0df72e106322b67e9c6e591fe870380bd0da0d5", + "rev": "e36f66bb10b09f5189dc3b1706948eaeb9a1c555", "type": "github" }, "original": { From a898822481096e72dda0a84750ed54709176e6f9 Mon Sep 17 00:00:00 2001 From: jblebrun Date: Mon, 22 Jan 2024 21:01:24 +0000 Subject: [PATCH 23/42] Modifications to oak_containers_launcher for external use (#4666) * Don't reference WORKSPACE_ROOT env from code that outside projects depend on * Expose the Qemu params struct --- .../tests/integration_test.rs | 4 ++-- oak_containers_launcher/src/lib.rs | 14 +++++--------- oak_containers_launcher/src/qemu.rs | 17 +++++------------ 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/oak_containers_hello_world_untrusted_app/tests/integration_test.rs b/oak_containers_hello_world_untrusted_app/tests/integration_test.rs index 284e92c7b7f..b1671525bca 100644 --- a/oak_containers_hello_world_untrusted_app/tests/integration_test.rs +++ b/oak_containers_hello_world_untrusted_app/tests/integration_test.rs @@ -58,7 +58,7 @@ async fn run_hello_world_test(container_bundle: std::path::PathBuf) { let app_args = Args { container_bundle, - ..Args::default_for_test() + ..Args::default_for_root(env!("WORKSPACE_ROOT")) }; let mut untrusted_app = oak_containers_hello_world_untrusted_app::UntrustedApp::create(app_args) @@ -97,7 +97,7 @@ async fn run_hello_world_test(container_bundle: std::path::PathBuf) { } async fn hello_world() { - run_hello_world_test(Args::default_for_test().container_bundle).await; + run_hello_world_test(Args::default_for_root(env!("WORKSPACE_ROOT")).container_bundle).await; } async fn cc_hello_world() { diff --git a/oak_containers_launcher/src/lib.rs b/oak_containers_launcher/src/lib.rs index c2d0ab7cc3c..84b0f5431b1 100644 --- a/oak_containers_launcher/src/lib.rs +++ b/oak_containers_launcher/src/lib.rs @@ -44,6 +44,7 @@ use clap::Parser; use oak_attestation::proto::oak::attestation::v1::{ endorsements, Endorsements, Evidence, OakRestrictedKernelEndorsements, }; +pub use qemu::Params as QemuParams; use tokio::{ net::TcpListener, sync::oneshot::{channel, Receiver, Sender}, @@ -84,21 +85,16 @@ pub struct Args { } impl Args { - pub fn default_for_test() -> Self { - let system_image = format!( - "{}oak_containers_system_image/target/image.tar.xz", - env!("WORKSPACE_ROOT") - ) - .into(); + pub fn default_for_root(root: &str) -> Self { + let system_image = format!("{root}oak_containers_system_image/target/image.tar.xz",).into(); let container_bundle = format!( - "{}oak_containers_hello_world_container/target/oak_container_example_oci_filesystem_bundle.tar", - env!("WORKSPACE_ROOT") + "{root}oak_containers_hello_world_container/target/oak_container_example_oci_filesystem_bundle.tar", ).into(); Self { system_image, container_bundle, application_config: None, - qemu_params: qemu::Params::default_for_test(), + qemu_params: qemu::Params::default_for_root(root), } } } diff --git a/oak_containers_launcher/src/qemu.rs b/oak_containers_launcher/src/qemu.rs index b03e9270638..16749fe3f7b 100644 --- a/oak_containers_launcher/src/qemu.rs +++ b/oak_containers_launcher/src/qemu.rs @@ -67,19 +67,12 @@ pub struct Params { } impl Params { - pub fn default_for_test() -> Self { + pub fn default_for_root(root: &str) -> Self { let vmm_binary = which::which("qemu-system-x86_64").expect("could not find qemu path"); - let stage0_binary = format!( - "{}stage0_bin/target/x86_64-unknown-none/release/stage0_bin", - env!("WORKSPACE_ROOT") - ) - .into(); - let kernel = format!( - "{}oak_containers_kernel/target/bzImage", - env!("WORKSPACE_ROOT") - ) - .into(); - let initrd = format!("{}/target/stage1.cpio", env!("WORKSPACE_ROOT")).into(); + let stage0_binary = + format!("{root}stage0_bin/target/x86_64-unknown-none/release/stage0_bin",).into(); + let kernel = format!("{root}oak_containers_kernel/target/bzImage",).into(); + let initrd = format!("{root}/target/stage1.cpio").into(); Self { vmm_binary, stage0_binary, From 5b0b924cfac011aca615444f6783ab0cd98a43fe Mon Sep 17 00:00:00 2001 From: jul-sh Date: Tue, 23 Jan 2024 10:13:00 -0500 Subject: [PATCH 24/42] Expose init & panic handler through oak kernel sdk (#4699) * Expose init & panic handler through oak kernel sdk * appease clippy --- Cargo.lock | 1 + enclave_apps/Cargo.lock | 8 +------- enclave_apps/Cargo.toml | 1 - enclave_apps/key_xor_test_app/Cargo.toml | 1 - enclave_apps/key_xor_test_app/src/main.rs | 13 ++++--------- enclave_apps/oak_echo_enclave_app/Cargo.toml | 2 -- enclave_apps/oak_echo_enclave_app/src/main.rs | 13 ++++--------- .../oak_echo_raw_enclave_app/Cargo.toml | 2 -- .../oak_echo_raw_enclave_app/src/main.rs | 13 ++++--------- .../oak_functions_enclave_app/Cargo.toml | 2 -- .../oak_functions_enclave_app/src/main.rs | 13 ++++--------- oak_restricted_kernel_sdk/Cargo.toml | 1 + oak_restricted_kernel_sdk/src/lib.rs | 17 +++++++++++++++++ oak_restricted_kernel_sdk/src/logging.rs | 2 ++ 14 files changed, 38 insertions(+), 51 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 4e0388738b0..7c797585a92 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2505,6 +2505,7 @@ dependencies = [ "oak_channel", "oak_crypto", "oak_dice", + "oak_enclave_runtime_support", "oak_restricted_kernel_dice", "oak_restricted_kernel_interface", "oak_stage0_dice", diff --git a/enclave_apps/Cargo.lock b/enclave_apps/Cargo.lock index 7dc4568aab7..3f0e9383492 100644 --- a/enclave_apps/Cargo.lock +++ b/enclave_apps/Cargo.lock @@ -587,7 +587,6 @@ dependencies = [ "log", "micro_rpc", "oak_channel", - "oak_enclave_runtime_support", "oak_restricted_kernel_interface", "oak_restricted_kernel_sdk", "static_assertions", @@ -756,8 +755,6 @@ dependencies = [ "oak_channel", "oak_core", "oak_echo_service", - "oak_enclave_runtime_support", - "oak_restricted_kernel_interface", "oak_restricted_kernel_sdk", "static_assertions", ] @@ -769,8 +766,6 @@ dependencies = [ "log", "micro_rpc", "oak_channel", - "oak_enclave_runtime_support", - "oak_restricted_kernel_interface", "oak_restricted_kernel_sdk", "static_assertions", ] @@ -819,9 +814,7 @@ dependencies = [ "oak_core", "oak_crypto", "oak_dice", - "oak_enclave_runtime_support", "oak_functions_service", - "oak_restricted_kernel_interface", "oak_restricted_kernel_sdk", "static_assertions", "zerocopy", @@ -878,6 +871,7 @@ dependencies = [ "oak_channel", "oak_crypto", "oak_dice", + "oak_enclave_runtime_support", "oak_restricted_kernel_interface", "p256", "strum", diff --git a/enclave_apps/Cargo.toml b/enclave_apps/Cargo.toml index 798cd909777..c05c196e1f5 100644 --- a/enclave_apps/Cargo.toml +++ b/enclave_apps/Cargo.toml @@ -10,7 +10,6 @@ members = [ [workspace.dependencies] micro_rpc = { path = "../micro_rpc" } oak_attestation = { path = "../oak_attestation" } -oak_enclave_runtime_support = { path = "../oak_enclave_runtime_support" } oak_channel = { path = "../oak_channel" } oak_core = { path = "../oak_core" } oak_crypto = { path = "../oak_crypto" } diff --git a/enclave_apps/key_xor_test_app/Cargo.toml b/enclave_apps/key_xor_test_app/Cargo.toml index a78f65ca6be..193f8dbb790 100644 --- a/enclave_apps/key_xor_test_app/Cargo.toml +++ b/enclave_apps/key_xor_test_app/Cargo.toml @@ -8,7 +8,6 @@ license = "Apache-2.0" [dependencies] log = "*" micro_rpc = { workspace = true } -oak_enclave_runtime_support = { workspace = true } oak_channel = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } oak_restricted_kernel_interface = { workspace = true } diff --git a/enclave_apps/key_xor_test_app/src/main.rs b/enclave_apps/key_xor_test_app/src/main.rs index bc80846ad54..3bd50f6fe9d 100644 --- a/enclave_apps/key_xor_test_app/src/main.rs +++ b/enclave_apps/key_xor_test_app/src/main.rs @@ -25,15 +25,11 @@ use core::panic::PanicInfo; use log::info; use oak_channel::{Read, Write}; use oak_restricted_kernel_interface::{syscall::read, DERIVED_KEY_FD}; -use oak_restricted_kernel_sdk::{FileDescriptorChannel, StderrLogger}; - -static LOGGER: StderrLogger = StderrLogger {}; +use oak_restricted_kernel_sdk::FileDescriptorChannel; #[no_mangle] fn _start() -> ! { - log::set_logger(&LOGGER).unwrap(); - log::set_max_level(log::LevelFilter::Debug); - oak_enclave_runtime_support::init(); + oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); main(); } @@ -61,11 +57,10 @@ fn run_server() -> ! { #[alloc_error_handler] fn out_of_memory(layout: ::core::alloc::Layout) -> ! { - panic!("error allocating memory: {:#?}", layout); + oak_restricted_kernel_sdk::alloc_error_handler(layout); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { - log::error!("PANIC: {}", info); - oak_restricted_kernel_interface::syscall::exit(-1); + oak_restricted_kernel_sdk::panic_handler(info); } diff --git a/enclave_apps/oak_echo_enclave_app/Cargo.toml b/enclave_apps/oak_echo_enclave_app/Cargo.toml index 8e32d1047b6..ddf67d5c138 100644 --- a/enclave_apps/oak_echo_enclave_app/Cargo.toml +++ b/enclave_apps/oak_echo_enclave_app/Cargo.toml @@ -9,11 +9,9 @@ license = "Apache-2.0" oak_echo_service = { path = "../../testing/oak_echo_service" } log = "*" micro_rpc = { workspace = true } -oak_enclave_runtime_support = { workspace = true } oak_channel = { workspace = true } oak_core = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } -oak_restricted_kernel_interface = { workspace = true } static_assertions = "*" [[bin]] diff --git a/enclave_apps/oak_echo_enclave_app/src/main.rs b/enclave_apps/oak_echo_enclave_app/src/main.rs index ce14ec95851..188bd71df43 100644 --- a/enclave_apps/oak_echo_enclave_app/src/main.rs +++ b/enclave_apps/oak_echo_enclave_app/src/main.rs @@ -25,15 +25,11 @@ use core::panic::PanicInfo; use log::info; use oak_core::samplestore::StaticSampleStore; -use oak_restricted_kernel_sdk::{FileDescriptorChannel, StderrLogger}; - -static LOGGER: StderrLogger = StderrLogger {}; +use oak_restricted_kernel_sdk::FileDescriptorChannel; #[no_mangle] fn _start() -> ! { - log::set_logger(&LOGGER).unwrap(); - log::set_max_level(log::LevelFilter::Debug); - oak_enclave_runtime_support::init(); + oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); main(); } @@ -58,11 +54,10 @@ fn start_echo_server() -> ! { #[alloc_error_handler] fn out_of_memory(layout: ::core::alloc::Layout) -> ! { - panic!("error allocating memory: {:#?}", layout); + oak_restricted_kernel_sdk::alloc_error_handler(layout); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { - log::error!("PANIC: {}", info); - oak_restricted_kernel_interface::syscall::exit(-1); + oak_restricted_kernel_sdk::panic_handler(info); } diff --git a/enclave_apps/oak_echo_raw_enclave_app/Cargo.toml b/enclave_apps/oak_echo_raw_enclave_app/Cargo.toml index 631d2b9248e..fa4beba224a 100644 --- a/enclave_apps/oak_echo_raw_enclave_app/Cargo.toml +++ b/enclave_apps/oak_echo_raw_enclave_app/Cargo.toml @@ -8,10 +8,8 @@ license = "Apache-2.0" [dependencies] log = "*" micro_rpc = { workspace = true } -oak_enclave_runtime_support = { workspace = true } oak_channel = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } -oak_restricted_kernel_interface = { workspace = true } static_assertions = "*" [[bin]] diff --git a/enclave_apps/oak_echo_raw_enclave_app/src/main.rs b/enclave_apps/oak_echo_raw_enclave_app/src/main.rs index db0c8018562..8cd98178944 100644 --- a/enclave_apps/oak_echo_raw_enclave_app/src/main.rs +++ b/enclave_apps/oak_echo_raw_enclave_app/src/main.rs @@ -25,17 +25,13 @@ use core::panic::PanicInfo; use log::info; use oak_channel::{Read, Write}; -use oak_restricted_kernel_sdk::{FileDescriptorChannel, StderrLogger}; +use oak_restricted_kernel_sdk::FileDescriptorChannel; const MESSAGE_SIZE: usize = 1; -static LOGGER: StderrLogger = StderrLogger {}; - #[no_mangle] fn _start() -> ! { - log::set_logger(&LOGGER).unwrap(); - log::set_max_level(log::LevelFilter::Debug); - oak_enclave_runtime_support::init(); + oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); main(); } @@ -60,11 +56,10 @@ fn start_echo_server() -> ! { #[alloc_error_handler] fn out_of_memory(layout: ::core::alloc::Layout) -> ! { - panic!("error allocating memory: {:#?}", layout); + oak_restricted_kernel_sdk::alloc_error_handler(layout); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { - log::error!("PANIC: {}", info); - oak_restricted_kernel_interface::syscall::exit(-1); + oak_restricted_kernel_sdk::panic_handler(info); } diff --git a/enclave_apps/oak_functions_enclave_app/Cargo.toml b/enclave_apps/oak_functions_enclave_app/Cargo.toml index c0719a9bfe1..a755a74343f 100644 --- a/enclave_apps/oak_functions_enclave_app/Cargo.toml +++ b/enclave_apps/oak_functions_enclave_app/Cargo.toml @@ -18,13 +18,11 @@ oak_functions_service = { path = "../../oak_functions_service", default-features log = "*" micro_rpc = { workspace = true } oak_attestation = { workspace = true } -oak_enclave_runtime_support = { workspace = true } oak_core = { workspace = true } oak_channel = { workspace = true } oak_crypto = { workspace = true } oak_dice = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } -oak_restricted_kernel_interface = { workspace = true } static_assertions = "*" zerocopy = "*" diff --git a/enclave_apps/oak_functions_enclave_app/src/main.rs b/enclave_apps/oak_functions_enclave_app/src/main.rs index 06c7e406f19..cb1d91350d0 100644 --- a/enclave_apps/oak_functions_enclave_app/src/main.rs +++ b/enclave_apps/oak_functions_enclave_app/src/main.rs @@ -25,15 +25,11 @@ use core::panic::PanicInfo; use log::info; use oak_core::samplestore::StaticSampleStore; -use oak_restricted_kernel_sdk::{FileDescriptorChannel, StderrLogger}; - -static LOGGER: StderrLogger = StderrLogger {}; +use oak_restricted_kernel_sdk::FileDescriptorChannel; #[no_mangle] fn _start() -> ! { - log::set_logger(&LOGGER).unwrap(); - log::set_max_level(log::LevelFilter::Debug); - oak_enclave_runtime_support::init(); + oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); main(); } @@ -67,11 +63,10 @@ fn main() -> ! { #[alloc_error_handler] fn out_of_memory(layout: ::core::alloc::Layout) -> ! { - panic!("error allocating memory: {:#?}", layout); + oak_restricted_kernel_sdk::alloc_error_handler(layout); } #[panic_handler] fn panic(info: &PanicInfo) -> ! { - log::error!("PANIC: {}", info); - oak_restricted_kernel_interface::syscall::exit(-1); + oak_restricted_kernel_sdk::panic_handler(info); } diff --git a/oak_restricted_kernel_sdk/Cargo.toml b/oak_restricted_kernel_sdk/Cargo.toml index fcc77830837..c0aebb59a00 100644 --- a/oak_restricted_kernel_sdk/Cargo.toml +++ b/oak_restricted_kernel_sdk/Cargo.toml @@ -16,6 +16,7 @@ oak_channel = { workspace = true } oak_crypto = { workspace = true } oak_dice = { workspace = true } oak_restricted_kernel_interface = { workspace = true } +oak_enclave_runtime_support = { workspace = true } oak_restricted_kernel_dice = { workspace = true, optional = true } oak_stage0_dice = { workspace = true, optional = true } p256 = { version = "*", default-features = false, features = ["ecdsa"] } diff --git a/oak_restricted_kernel_sdk/src/lib.rs b/oak_restricted_kernel_sdk/src/lib.rs index a255b8f5aba..26a42af3177 100644 --- a/oak_restricted_kernel_sdk/src/lib.rs +++ b/oak_restricted_kernel_sdk/src/lib.rs @@ -25,3 +25,20 @@ mod logging; pub use channel::FileDescriptorChannel; pub use dice::*; pub use logging::StderrLogger; +use logging::STDERR_LOGGER; + +/// Initialization function that sets up the allocator and logger. +pub fn init(log_level: log::LevelFilter) { + log::set_logger(&STDERR_LOGGER).expect("failed to set logger"); + log::set_max_level(log_level); + oak_enclave_runtime_support::init(); +} + +pub fn alloc_error_handler(layout: ::core::alloc::Layout) -> ! { + panic!("error allocating memory: {:#?}", layout); +} + +pub fn panic_handler(info: &core::panic::PanicInfo) -> ! { + log::error!("PANIC: {}", info); + oak_restricted_kernel_interface::syscall::exit(-1); +} diff --git a/oak_restricted_kernel_sdk/src/logging.rs b/oak_restricted_kernel_sdk/src/logging.rs index ff8aa35aca9..b539cd49eb3 100644 --- a/oak_restricted_kernel_sdk/src/logging.rs +++ b/oak_restricted_kernel_sdk/src/logging.rs @@ -18,6 +18,8 @@ use core::fmt::Write; use oak_restricted_kernel_interface::syscall::{fsync, write}; +pub static STDERR_LOGGER: StderrLogger = StderrLogger {}; + struct Stderr {} impl Stderr { From 5641a139a4e325b4b28e8bcfb3bc04b47d04831f Mon Sep 17 00:00:00 2001 From: Ernesto Ocampo Date: Tue, 23 Jan 2024 16:00:53 +0000 Subject: [PATCH 25/42] no_std test: use cargo build (with flags) instead of cargo check (#4703) Cargo check does not detect all failures. Running cargo build without extra flags exposes failures. --- justfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/justfile b/justfile index 70a52d133b1..93b7f13b21e 100644 --- a/justfile +++ b/justfile @@ -64,7 +64,7 @@ oak_functions_containers_launcher: all_oak_functions_containers_binaries: stage0_bin stage1_cpio oak_containers_kernel oak_containers_system_image oak_functions_containers_container_bundle_tar oak_functions_containers_launcher ensure_no_std package: - cargo check --target=x86_64-unknown-none --package='{{package}}' + RUSTFLAGS="-C target-feature=+sse,+sse2,+ssse3,+sse4.1,+sse4.2,+avx,+avx2,+rdrand,-soft-float" cargo build --target=x86_64-unknown-none --package='{{package}}' all_ensure_no_std: (ensure_no_std "micro_rpc") (ensure_no_std "oak_attestation_verification") (ensure_no_std "oak_restricted_kernel_sdk") From 31ccc6a036ba2443df71ee9c85fd92a0cc37022d Mon Sep 17 00:00:00 2001 From: Andri Saar Date: Tue, 23 Jan 2024 19:38:33 +0000 Subject: [PATCH 26/42] Accept compressed RPCs in Oak Functions trusted app --- Cargo.lock | 1 + oak_functions_containers_app/Cargo.toml | 2 +- oak_functions_containers_app/src/lib.rs | 4 +++- oak_functions_containers_app/tests/integration_test.rs | 4 ++-- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7c797585a92..102161b8302 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3964,6 +3964,7 @@ dependencies = [ "axum", "base64 0.21.7", "bytes", + "flate2", "futures-core", "futures-util", "h2", diff --git a/oak_functions_containers_app/Cargo.toml b/oak_functions_containers_app/Cargo.toml index d79e6d9ab99..e504bd318ad 100644 --- a/oak_functions_containers_app/Cargo.toml +++ b/oak_functions_containers_app/Cargo.toml @@ -33,7 +33,7 @@ opentelemetry-otlp = { version = "*", default-features = false, features = [ prost = "*" tokio = { version = "*", features = ["rt-multi-thread", "macros", "sync"] } tokio-stream = { version = "*", features = ["net"] } -tonic = { workspace = true } +tonic = { workspace = true, features = ["gzip"] } tower = { version = "*", features = ["load-shed"] } tower-http = { version = "*", features = ["trace"] } tracing = "*" diff --git a/oak_functions_containers_app/src/lib.rs b/oak_functions_containers_app/src/lib.rs index 62a32f3bb01..c34f0f549af 100644 --- a/oak_functions_containers_app/src/lib.rs +++ b/oak_functions_containers_app/src/lib.rs @@ -41,6 +41,7 @@ use opentelemetry::{ use prost::Message; use tokio::net::TcpListener; use tokio_stream::{wrappers::TcpListenerStream, StreamExt}; +use tonic::codec::CompressionEncoding; use tracing::Span; use crate::proto::oak::functions::oak_functions_server::{OakFunctions, OakFunctionsServer}; @@ -407,7 +408,8 @@ pub async fn serve( encryption_key_handle, Some(Arc::new(OtelObserver::new(meter))), )) - .max_decoding_message_size(MAX_DECODING_MESSAGE_SIZE), + .max_decoding_message_size(MAX_DECODING_MESSAGE_SIZE) + .accept_compressed(CompressionEncoding::Gzip), ) .serve_with_incoming(TcpListenerStream::new(listener)) .await diff --git a/oak_functions_containers_app/tests/integration_test.rs b/oak_functions_containers_app/tests/integration_test.rs index 73e9e60d0c9..54a17a5daa3 100644 --- a/oak_functions_containers_app/tests/integration_test.rs +++ b/oak_functions_containers_app/tests/integration_test.rs @@ -34,7 +34,7 @@ use oak_functions_containers_app::serve; use oak_functions_service::proto::oak::functions::InitializeRequest; use opentelemetry::metrics::{noop::NoopMeterProvider, MeterProvider}; use tokio::net::TcpListener; -use tonic::transport::Endpoint; +use tonic::{codec::CompressionEncoding, transport::Endpoint}; use crate::proto::oak::functions::oak_functions_client::OakFunctionsClient; @@ -60,7 +60,7 @@ async fn test_lookup() { .connect() .await .expect("couldn't connect to trusted app"); - OakFunctionsClient::new(channel) + OakFunctionsClient::new(channel).send_compressed(CompressionEncoding::Gzip) }; let _ = oak_functions_client From 988d8f50e69a12090badab193882eaf10ee530b7 Mon Sep 17 00:00:00 2001 From: jul-sh Date: Tue, 23 Jan 2024 21:15:40 -0500 Subject: [PATCH 27/42] Proc macro to reduce enclave app boilerplate (#4700) * Proc macro to reduce enclave app boilerplate Macro that simplifies the creation of an enclave app. See enclave_apps/oak_functions_enclave_app/src/main.rs to see it in use. * fix incorrect name * Validate function type signature * re-export the macro from the sdk * fix CI * fix lock file issue --- Cargo.lock | 9 +++ Cargo.toml | 2 + enclave_apps/Cargo.lock | 9 +++ .../oak_functions_enclave_app/src/main.rs | 22 +----- oak_restricted_kernel_sdk/Cargo.toml | 1 + oak_restricted_kernel_sdk/src/lib.rs | 1 + .../Cargo.toml | 13 ++++ .../src/lib.rs | 78 +++++++++++++++++++ 8 files changed, 115 insertions(+), 20 deletions(-) create mode 100644 oak_restricted_kernel_sdk_proc_macro/Cargo.toml create mode 100644 oak_restricted_kernel_sdk_proc_macro/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 102161b8302..5ee1f19cfef 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2508,12 +2508,21 @@ dependencies = [ "oak_enclave_runtime_support", "oak_restricted_kernel_dice", "oak_restricted_kernel_interface", + "oak_restricted_kernel_sdk_proc_macro", "oak_stage0_dice", "p256", "strum 0.25.0", "zerocopy", ] +[[package]] +name = "oak_restricted_kernel_sdk_proc_macro" +version = "0.1.0" +dependencies = [ + "quote", + "syn 1.0.109", +] + [[package]] name = "oak_sev_guest" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b8a788641f5..6a0b0c1e492 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -57,6 +57,7 @@ members = [ "stage0_dice", "testing/oak_echo_service", "xtask", + "oak_restricted_kernel_sdk_proc_macro" ] exclude = [ "fuzz", @@ -104,6 +105,7 @@ oak_linux_boot_params = { path = "./linux_boot_params" } oak_logger = { path = "./oak_functions/logger" } oak_restricted_kernel_dice = { path = "./oak_restricted_kernel_dice" } oak_restricted_kernel_sdk = { path = "./oak_restricted_kernel_sdk" } +oak_restricted_kernel_sdk_proc_macro = { path = "./oak_restricted_kernel_sdk_proc_macro" } oak_restricted_kernel_interface = { path = "./oak_restricted_kernel_interface" } oak_sev_guest = { path = "./oak_sev_guest", default-features = false } oak_stage0_dice = { path = "./stage0_dice" } diff --git a/enclave_apps/Cargo.lock b/enclave_apps/Cargo.lock index 3f0e9383492..2a6929d5f34 100644 --- a/enclave_apps/Cargo.lock +++ b/enclave_apps/Cargo.lock @@ -873,11 +873,20 @@ dependencies = [ "oak_dice", "oak_enclave_runtime_support", "oak_restricted_kernel_interface", + "oak_restricted_kernel_sdk_proc_macro", "p256", "strum", "zerocopy", ] +[[package]] +name = "oak_restricted_kernel_sdk_proc_macro" +version = "0.1.0" +dependencies = [ + "quote", + "syn 1.0.102", +] + [[package]] name = "once_cell" version = "1.15.0" diff --git a/enclave_apps/oak_functions_enclave_app/src/main.rs b/enclave_apps/oak_functions_enclave_app/src/main.rs index cb1d91350d0..018411ceee7 100644 --- a/enclave_apps/oak_functions_enclave_app/src/main.rs +++ b/enclave_apps/oak_functions_enclave_app/src/main.rs @@ -21,20 +21,12 @@ extern crate alloc; use alloc::{boxed::Box, sync::Arc}; -use core::panic::PanicInfo; -use log::info; use oak_core::samplestore::StaticSampleStore; -use oak_restricted_kernel_sdk::FileDescriptorChannel; - -#[no_mangle] -fn _start() -> ! { - oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); - main(); -} +use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel}; +#[entrypoint] fn main() -> ! { - info!("In main!"); #[cfg(feature = "deny_sensitive_logging")] { // Only log warnings and errors to reduce the risk of accidentally leaking execution @@ -60,13 +52,3 @@ fn main() -> ! { ) .expect("server encountered an unrecoverable error"); } - -#[alloc_error_handler] -fn out_of_memory(layout: ::core::alloc::Layout) -> ! { - oak_restricted_kernel_sdk::alloc_error_handler(layout); -} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - oak_restricted_kernel_sdk::panic_handler(info); -} diff --git a/oak_restricted_kernel_sdk/Cargo.toml b/oak_restricted_kernel_sdk/Cargo.toml index c0aebb59a00..9e4f15ead16 100644 --- a/oak_restricted_kernel_sdk/Cargo.toml +++ b/oak_restricted_kernel_sdk/Cargo.toml @@ -17,6 +17,7 @@ oak_crypto = { workspace = true } oak_dice = { workspace = true } oak_restricted_kernel_interface = { workspace = true } oak_enclave_runtime_support = { workspace = true } +oak_restricted_kernel_sdk_proc_macro = { workspace = true } oak_restricted_kernel_dice = { workspace = true, optional = true } oak_stage0_dice = { workspace = true, optional = true } p256 = { version = "*", default-features = false, features = ["ecdsa"] } diff --git a/oak_restricted_kernel_sdk/src/lib.rs b/oak_restricted_kernel_sdk/src/lib.rs index 26a42af3177..e5c58eca829 100644 --- a/oak_restricted_kernel_sdk/src/lib.rs +++ b/oak_restricted_kernel_sdk/src/lib.rs @@ -26,6 +26,7 @@ pub use channel::FileDescriptorChannel; pub use dice::*; pub use logging::StderrLogger; use logging::STDERR_LOGGER; +pub use oak_restricted_kernel_sdk_proc_macro::entrypoint; /// Initialization function that sets up the allocator and logger. pub fn init(log_level: log::LevelFilter) { diff --git a/oak_restricted_kernel_sdk_proc_macro/Cargo.toml b/oak_restricted_kernel_sdk_proc_macro/Cargo.toml new file mode 100644 index 00000000000..76eef13bd37 --- /dev/null +++ b/oak_restricted_kernel_sdk_proc_macro/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "oak_restricted_kernel_sdk_proc_macro" +version = "0.1.0" +authors = ["Juliette Pluto "] +edition = "2021" +license = "Apache-2.0" + +[lib] +proc-macro = true + +[dependencies] +syn = { version = "*", features = ["full"] } +quote = "*" diff --git a/oak_restricted_kernel_sdk_proc_macro/src/lib.rs b/oak_restricted_kernel_sdk_proc_macro/src/lib.rs new file mode 100644 index 00000000000..8cb93ea6988 --- /dev/null +++ b/oak_restricted_kernel_sdk_proc_macro/src/lib.rs @@ -0,0 +1,78 @@ +// +// Copyright 2022 The Project Oak Authors +// +// Licensed 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. +// + +use proc_macro::TokenStream; +use quote::quote; +use syn::{parse_macro_input, Item, ItemFn, Result}; + +#[proc_macro_attribute] +pub fn entrypoint(_attr: TokenStream, entry: TokenStream) -> TokenStream { + let entry_item = parse_macro_input!(entry as syn::Item); + match validate_type_signature(entry_item) { + Ok(entry_fn) => process_entry_fn(entry_fn), + Err(error) => error.to_compile_error().into(), + } +} + +fn validate_type_signature(entry_item: Item) -> Result { + let entry_fn: ItemFn = match entry_item { + syn::Item::Fn(entry_fn) => Ok(entry_fn), + _ => Err(syn::Error::new( + syn::spanned::Spanned::span(&entry_item), + "the entrypoint macro can only be applied to functions", + )), + }?; + + let return_type_error = syn::Error::new( + syn::spanned::Spanned::span(&entry_fn.sig.output), + "the entrypoint function should have return type !", + ); + match &entry_fn.sig.output { + syn::ReturnType::Default => Err(return_type_error), + syn::ReturnType::Type(_, return_type) => match **return_type { + syn::Type::Never(_) => Ok(entry_fn), + _ => Err(return_type_error), + }, + } +} + +fn process_entry_fn(entry_fn: ItemFn) -> TokenStream { + let entry_fn_name = &entry_fn.sig.ident; + + let generated = quote! { + #entry_fn + + static LOGGER: oak_restricted_kernel_sdk::StderrLogger = oak_restricted_kernel_sdk::StderrLogger {}; + + #[no_mangle] + fn _start() -> ! { + oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); + log::info!("In main!"); + #entry_fn_name(); + } + + #[alloc_error_handler] + fn out_of_memory(layout: ::core::alloc::Layout) -> ! { + oak_restricted_kernel_sdk::alloc_error_handler(layout); + } + + #[panic_handler] + fn panic(info: &core::panic::PanicInfo) -> ! { + oak_restricted_kernel_sdk::panic_handler(info); + } + }; + TokenStream::from(generated) +} From ee52d98bb422c99635f013b540b8751037aa724f Mon Sep 17 00:00:00 2001 From: Ernesto Ocampo Date: Wed, 24 Jan 2024 12:08:34 +0000 Subject: [PATCH 28/42] Declare crate for generated proto Rust code (#4701) --- Cargo.lock | 25 +++++--- Cargo.toml | 2 + enclave_apps/Cargo.lock | 11 +++- fuzz/Cargo.lock | 2 - oak_attestation_verification/Cargo.toml | 6 +- oak_attestation_verification/src/claims.rs | 3 +- .../src/endorsement.rs | 2 +- oak_attestation_verification/src/lib.rs | 18 ------ oak_attestation_verification/src/util.rs | 3 +- oak_attestation_verification/src/verifier.rs | 61 +++++++++---------- .../tests/endorsement_tests.rs | 2 +- .../tests/verifier_tests.rs | 41 +++++++------ oak_client/Cargo.toml | 1 + oak_client/src/lib.rs | 2 +- oak_client/src/verifier.rs | 6 +- .../examples/benchmark/module/Cargo.toml | 4 +- .../examples/benchmark/module/build.rs | 29 --------- .../examples/benchmark/module/src/lib.rs | 7 +-- oak_functions/lookup_data_checker/Cargo.toml | 2 +- oak_functions/lookup_data_checker/src/main.rs | 6 +- .../lookup_data_generator/Cargo.toml | 2 +- .../lookup_data_generator/src/data.rs | 2 +- oak_functions_abi/Cargo.toml | 4 -- oak_functions_abi/build.rs | 32 ---------- oak_functions_abi/src/lib.rs | 5 -- oak_functions_containers_launcher/Cargo.toml | 2 +- .../src/lookup.rs | 6 +- oak_functions_launcher/Cargo.toml | 1 + oak_functions_launcher/src/lookup.rs | 6 +- oak_functions_service/Cargo.toml | 2 +- .../tests/integration_test.rs | 4 +- oak_functions_test_utils/Cargo.toml | 1 + oak_functions_test_utils/src/lib.rs | 2 +- oak_ml_transparency/runner/Cargo.lock | 11 +++- oak_proto_rust/Cargo.toml | 11 ++++ .../build.rs | 3 + oak_proto_rust/src/lib.rs | 47 ++++++++++++++ 37 files changed, 190 insertions(+), 184 deletions(-) delete mode 100644 oak_functions/examples/benchmark/module/build.rs delete mode 100644 oak_functions_abi/build.rs create mode 100644 oak_proto_rust/Cargo.toml rename {oak_attestation_verification => oak_proto_rust}/build.rs (89%) create mode 100644 oak_proto_rust/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 5ee1f19cfef..d3bf569b421 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -342,8 +342,8 @@ name = "benchmark" version = "0.1.0" dependencies = [ "oak_functions_sdk", + "oak_proto_rust", "prost", - "prost-build", ] [[package]] @@ -1596,7 +1596,7 @@ dependencies = [ "env_logger", "location_utils", "log", - "oak_functions_abi", + "oak_proto_rust", "prost", ] @@ -1609,7 +1609,7 @@ dependencies = [ "clap", "location_utils", "multimap 0.10.0", - "oak_functions_abi", + "oak_proto_rust", "prost", "rand", "serde", @@ -1876,10 +1876,10 @@ dependencies = [ "getrandom", "hex", "oak_dice", + "oak_proto_rust", "oak_sev_guest", "p256", "prost", - "prost-build", "serde", "serde_json", "sha2", @@ -1911,6 +1911,7 @@ dependencies = [ "oak_attestation_verification", "oak_crypto", "oak_grpc_utils", + "oak_proto_rust", "prost", "tonic", ] @@ -2174,8 +2175,6 @@ name = "oak_functions_abi" version = "0.1.0" dependencies = [ "anyhow", - "prost", - "prost-build", "static_assertions", "strum 0.25.0", ] @@ -2241,11 +2240,11 @@ dependencies = [ "oak_client", "oak_containers_launcher", "oak_crypto", - "oak_functions_abi", "oak_functions_client", "oak_functions_launcher", "oak_functions_test_utils", "oak_grpc_utils", + "oak_proto_rust", "prost", "tokio", "tonic", @@ -2278,6 +2277,7 @@ dependencies = [ "oak_functions_test_utils", "oak_grpc_utils", "oak_launcher_utils", + "oak_proto_rust", "prost", "rand", "serde", @@ -2340,7 +2340,6 @@ name = "oak_functions_service" version = "0.1.0" dependencies = [ "anyhow", - "benchmark", "byteorder", "bytes", "env_logger", @@ -2355,6 +2354,7 @@ dependencies = [ "oak_functions_abi", "oak_functions_sdk", "oak_functions_test_utils", + "oak_proto_rust", "oak_restricted_kernel_sdk", "prost", "spinning_top 0.3.0", @@ -2376,6 +2376,7 @@ dependencies = [ "oak_client", "oak_functions_abi", "oak_functions_client", + "oak_proto_rust", "port_check", "prost", "tempfile", @@ -2431,6 +2432,14 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "oak_proto_rust" +version = "0.0.1" +dependencies = [ + "prost", + "prost-build", +] + [[package]] name = "oak_restricted_kernel" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 6a0b0c1e492..4cbd0496bc3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -43,6 +43,7 @@ members = [ "oak_functions_test_utils", "oak_hello_world_linux_init", "oak_launcher_utils", + "oak_proto_rust", "oak_restricted_kernel", "oak_restricted_kernel_dice", "oak_restricted_kernel_interface", @@ -103,6 +104,7 @@ oak_grpc_utils = { path = "./oak_grpc_utils" } oak_launcher_utils = { path = "./oak_launcher_utils" } oak_linux_boot_params = { path = "./linux_boot_params" } oak_logger = { path = "./oak_functions/logger" } +oak_proto_rust = { path = "./oak_proto_rust" } oak_restricted_kernel_dice = { path = "./oak_restricted_kernel_dice" } oak_restricted_kernel_sdk = { path = "./oak_restricted_kernel_sdk" } oak_restricted_kernel_sdk_proc_macro = { path = "./oak_restricted_kernel_sdk_proc_macro" } diff --git a/enclave_apps/Cargo.lock b/enclave_apps/Cargo.lock index 2a6929d5f34..315176172b7 100644 --- a/enclave_apps/Cargo.lock +++ b/enclave_apps/Cargo.lock @@ -797,8 +797,6 @@ name = "oak_functions_abi" version = "0.1.0" dependencies = [ "anyhow", - "prost", - "prost-build", "static_assertions", "strum", ] @@ -847,12 +845,21 @@ dependencies = [ "oak_dice", "oak_functions_abi", "oak_functions_sdk", + "oak_proto_rust", "oak_restricted_kernel_sdk", "prost", "spinning_top", "wasmi", ] +[[package]] +name = "oak_proto_rust" +version = "0.0.1" +dependencies = [ + "prost", + "prost-build", +] + [[package]] name = "oak_restricted_kernel_interface" version = "0.1.0" diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index 5a351807274..9de5dbdd56d 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -215,8 +215,6 @@ name = "oak_functions_abi" version = "0.1.0" dependencies = [ "anyhow", - "prost", - "prost-build", "static_assertions", "strum", ] diff --git a/oak_attestation_verification/Cargo.toml b/oak_attestation_verification/Cargo.toml index dfc9c4434a0..4ea36355313 100644 --- a/oak_attestation_verification/Cargo.toml +++ b/oak_attestation_verification/Cargo.toml @@ -20,8 +20,8 @@ getrandom = { version = "*", features = [ ] } hex = { version = "*", default-features = false } oak_dice = { workspace = true } +oak_proto_rust = { workspace = true } oak_sev_guest = { workspace = true } -prost = { workspace = true } p256 = { version = "*", default-features = false, features = [ "alloc", "ecdsa-core", @@ -37,5 +37,5 @@ time = { version = "0.3.28", default-features = false, features = [ ] } zerocopy = "*" -[build-dependencies] -prost-build = { workspace = true } +[dev-dependencies] +prost = { workspace = true } diff --git a/oak_attestation_verification/src/claims.rs b/oak_attestation_verification/src/claims.rs index cbfa07aca52..0bc4261c16a 100644 --- a/oak_attestation_verification/src/claims.rs +++ b/oak_attestation_verification/src/claims.rs @@ -23,13 +23,12 @@ extern crate alloc; use alloc::{collections::BTreeMap, string::String, vec::Vec}; +use oak_proto_rust::oak::HexDigest; use serde::Deserialize; #[cfg(feature = "std")] use serde::Serialize; use time::OffsetDateTime; -use crate::proto::oak::HexDigest; - /// PredicateType which identifies a V1 Claim, for in-toto statements. pub const CLAIM_V1: &str = "https://github.com/project-oak/transparent-release/claim/v1"; diff --git a/oak_attestation_verification/src/endorsement.rs b/oak_attestation_verification/src/endorsement.rs index ffe3ac6f117..b0585cb90f7 100644 --- a/oak_attestation_verification/src/endorsement.rs +++ b/oak_attestation_verification/src/endorsement.rs @@ -17,13 +17,13 @@ //! Verifies binary endorsements as coming from Transparent Release. use base64::{prelude::BASE64_STANDARD, Engine as _}; +use oak_proto_rust::oak::HexDigest; use crate::{ claims::{ get_digest, parse_endorsement_statement, validate_endorsement, verify_validity_duration, EndorsementStatement, }, - proto::oak::HexDigest, rekor::{get_rekor_log_entry_body, verify_rekor_log_entry}, util::{convert_pem_to_raw, equal_keys, is_hex_digest_match, MatchResult}, }; diff --git a/oak_attestation_verification/src/lib.rs b/oak_attestation_verification/src/lib.rs index 5cf6f591327..c443c654dcf 100644 --- a/oak_attestation_verification/src/lib.rs +++ b/oak_attestation_verification/src/lib.rs @@ -19,24 +19,6 @@ extern crate alloc; -// Inlined from tonic::include_proto in order to cut dependency on tonic. -macro_rules! include_proto { - ($package: tt) => { - include!(concat!(env!("OUT_DIR"), concat!("/", $package, ".rs"))); - }; -} - -pub mod proto { - pub mod oak { - include_proto!("oak"); - pub mod attestation { - pub mod v1 { - include_proto!("oak.attestation.v1"); - } - } - } -} - pub mod claims; pub mod endorsement; pub mod rekor; diff --git a/oak_attestation_verification/src/util.rs b/oak_attestation_verification/src/util.rs index 3bd6cebc9db..31032618d01 100644 --- a/oak_attestation_verification/src/util.rs +++ b/oak_attestation_verification/src/util.rs @@ -19,11 +19,10 @@ use core::{cmp::Ordering, str::FromStr}; use base64::{prelude::BASE64_STANDARD, Engine as _}; use ecdsa::{signature::Verifier, Signature}; +use oak_proto_rust::oak::{HexDigest, RawDigest}; use p256::ecdsa::VerifyingKey; use sha2::{Digest, Sha256, Sha384, Sha512}; -use crate::proto::oak::{HexDigest, RawDigest}; - const PEM_HEADER: &str = "-----BEGIN PUBLIC KEY-----"; const PEM_FOOTER: &str = "-----END PUBLIC KEY-----"; diff --git a/oak_attestation_verification/src/verifier.rs b/oak_attestation_verification/src/verifier.rs index 04833bc3e65..fc018b15cdc 100644 --- a/oak_attestation_verification/src/verifier.rs +++ b/oak_attestation_verification/src/verifier.rs @@ -27,6 +27,21 @@ use oak_dice::cert::{ LAYER_2_CODE_MEASUREMENT_ID, LAYER_3_CODE_MEASUREMENT_ID, LAYER_3_CONFIG_MEASUREMENT_ID, MEMORY_MAP_MEASUREMENT_ID, SETUP_DATA_MEASUREMENT_ID, SHA2_256_ID, SYSTEM_IMAGE_LAYER_ID, }; +use oak_proto_rust::oak::{ + attestation::v1::{ + attestation_results::Status, binary_reference_value, endorsements, reference_values, + AmdSevReferenceValues, ApplicationKeys, ApplicationLayerEndorsements, + ApplicationLayerReferenceValues, AttestationResults, BinaryReferenceValue, CbEndorsements, + CbReferenceValues, ContainerLayerEndorsements, ContainerLayerReferenceValues, Endorsements, + Evidence, IntelTdxReferenceValues, KernelLayerEndorsements, KernelLayerReferenceValues, + LayerEvidence, OakContainersEndorsements, OakContainersReferenceValues, + OakRestrictedKernelEndorsements, OakRestrictedKernelReferenceValues, ReferenceValues, + RootLayerEndorsements, RootLayerEvidence, RootLayerReferenceValues, + SystemLayerEndorsements, SystemLayerReferenceValues, TeePlatform, + TransparentReleaseEndorsement, + }, + RawDigest, +}; use oak_sev_guest::guest::{AttestationReport, PolicyFlags}; use zerocopy::FromBytes; @@ -34,22 +49,6 @@ use crate::{ alloc::string::ToString, claims::{get_digest, parse_endorsement_statement}, endorsement::verify_binary_endorsement, - proto::oak::{ - attestation::v1::{ - attestation_results::Status, binary_reference_value, endorsements, reference_values, - AmdSevReferenceValues, ApplicationKeys, ApplicationLayerEndorsements, - ApplicationLayerReferenceValues, AttestationResults, BinaryReferenceValue, - CbEndorsements, CbReferenceValues, ContainerLayerEndorsements, - ContainerLayerReferenceValues, Endorsements, Evidence, IntelTdxReferenceValues, - KernelLayerEndorsements, KernelLayerReferenceValues, LayerEvidence, - OakContainersEndorsements, OakContainersReferenceValues, - OakRestrictedKernelEndorsements, OakRestrictedKernelReferenceValues, ReferenceValues, - RootLayerEndorsements, RootLayerEvidence, RootLayerReferenceValues, - SystemLayerEndorsements, SystemLayerReferenceValues, TeePlatform, - TransparentReleaseEndorsement, - }, - RawDigest, - }, util::{ hex_to_raw_digest, is_hex_digest_match, is_raw_digest_match, raw_to_hex_digest, MatchResult, }, @@ -63,21 +62,21 @@ pub struct DiceChainResult { pub signing_public_key: Vec, } -impl From<&anyhow::Result> for AttestationResults { - fn from(value: &anyhow::Result) -> Self { - match value { - Ok(dice_chain_result) => AttestationResults { - status: Status::Success.into(), - encryption_public_key: dice_chain_result.encryption_public_key.clone(), - signing_public_key: dice_chain_result.signing_public_key.clone(), - ..Default::default() - }, - Err(err) => AttestationResults { - status: Status::GenericFailure.into(), - reason: err.to_string(), - ..Default::default() - }, - } +pub fn to_attestation_results( + verify_result: &anyhow::Result, +) -> AttestationResults { + match verify_result { + Ok(dice_chain_result) => AttestationResults { + status: Status::Success.into(), + encryption_public_key: dice_chain_result.encryption_public_key.clone(), + signing_public_key: dice_chain_result.signing_public_key.clone(), + ..Default::default() + }, + Err(err) => AttestationResults { + status: Status::GenericFailure.into(), + reason: err.to_string(), + ..Default::default() + }, } } diff --git a/oak_attestation_verification/tests/endorsement_tests.rs b/oak_attestation_verification/tests/endorsement_tests.rs index 5d7cccb3e06..0aaf4bd3098 100644 --- a/oak_attestation_verification/tests/endorsement_tests.rs +++ b/oak_attestation_verification/tests/endorsement_tests.rs @@ -25,10 +25,10 @@ use oak_attestation_verification::{ verify_binary_digest, verify_binary_endorsement, verify_endorsement_statement, verify_endorser_public_key, }, - proto::oak::HexDigest, rekor::{verify_rekor_log_entry, verify_rekor_signature}, util::{convert_pem_to_raw, MatchResult}, }; +use oak_proto_rust::oak::HexDigest; const BINARY_DIGEST: &str = "39051983bbb600bbfb91bd22ee4c976420f8f0c6a895fd083dcb0d153ddd5fd6"; const ENDORSEMENT_PATH: &str = "testdata/endorsement.json"; diff --git a/oak_attestation_verification/tests/verifier_tests.rs b/oak_attestation_verification/tests/verifier_tests.rs index a84cb23738b..af00340cdb2 100644 --- a/oak_attestation_verification/tests/verifier_tests.rs +++ b/oak_attestation_verification/tests/verifier_tests.rs @@ -17,17 +17,16 @@ use std::fs; use oak_attestation_verification::{ - proto::oak::attestation::v1::{ - attestation_results::Status, AmdSevReferenceValues, AttestationResults, - BinaryReferenceValue, ContainerLayerEndorsements, ContainerLayerReferenceValues, - EndorsementReferenceValue, Endorsements, Evidence, KernelLayerEndorsements, - KernelLayerReferenceValues, OakContainersEndorsements, OakContainersReferenceValues, - ReferenceValues, RootLayerEndorsements, RootLayerReferenceValues, SkipVerification, - StringReferenceValue, SystemLayerEndorsements, SystemLayerReferenceValues, - TransparentReleaseEndorsement, - }, util::convert_pem_to_raw, - verifier::verify, + verifier::{to_attestation_results, verify}, +}; +use oak_proto_rust::oak::attestation::v1::{ + attestation_results::Status, AmdSevReferenceValues, BinaryReferenceValue, + ContainerLayerEndorsements, ContainerLayerReferenceValues, EndorsementReferenceValue, + Endorsements, Evidence, KernelLayerEndorsements, KernelLayerReferenceValues, + OakContainersEndorsements, OakContainersReferenceValues, ReferenceValues, + RootLayerEndorsements, RootLayerReferenceValues, SkipVerification, StringReferenceValue, + SystemLayerEndorsements, SystemLayerReferenceValues, TransparentReleaseEndorsement, }; use prost::Message; @@ -87,7 +86,7 @@ fn create_endorsements() -> Endorsements { container_layer: Some(container_layer), }; Endorsements { - r#type: Some(oak_attestation_verification::proto::oak::attestation::v1::endorsements::Type::OakContainers(ends)), + r#type: Some(oak_proto_rust::oak::attestation::v1::endorsements::Type::OakContainers(ends)), } } @@ -108,10 +107,16 @@ fn create_reference_values() -> ReferenceValues { rekor_public_key, }; let skip = BinaryReferenceValue { - r#type: Some(oak_attestation_verification::proto::oak::attestation::v1::binary_reference_value::Type::Skip(SkipVerification {})), + r#type: Some( + oak_proto_rust::oak::attestation::v1::binary_reference_value::Type::Skip( + SkipVerification {}, + ), + ), }; let brv = BinaryReferenceValue { - r#type: Some(oak_attestation_verification::proto::oak::attestation::v1::binary_reference_value::Type::Endorsement(erv)), + r#type: Some( + oak_proto_rust::oak::attestation::v1::binary_reference_value::Type::Endorsement(erv), + ), }; let srv = StringReferenceValue { values: ["whatever".to_owned()].to_vec(), @@ -150,7 +155,9 @@ fn create_reference_values() -> ReferenceValues { container_layer: Some(container_layer), }; ReferenceValues { - r#type: Some(oak_attestation_verification::proto::oak::attestation::v1::reference_values::Type::OakContainers(vs)), + r#type: Some( + oak_proto_rust::oak::attestation::v1::reference_values::Type::OakContainers(vs), + ), } } @@ -161,7 +168,7 @@ fn verify_succeeds() { let reference_values = create_reference_values(); let r = verify(NOW_UTC_MILLIS, &evidence, &endorsements, &reference_values); - let p = AttestationResults::from(&r); + let p = to_attestation_results(&r); eprintln!("======================================"); eprintln!("code={} reason={}", p.status as i32, p.reason); @@ -178,7 +185,7 @@ fn verify_fails_with_manipulated_root_public_key() { let reference_values = create_reference_values(); let r = verify(NOW_UTC_MILLIS, &evidence, &endorsements, &reference_values); - let p = AttestationResults::from(&r); + let p = to_attestation_results(&r); eprintln!("======================================"); eprintln!("code={} reason={}", p.status as i32, p.reason); @@ -194,7 +201,7 @@ fn verify_fails_with_empty_args() { let reference_values = ReferenceValues::default(); let r = verify(NOW_UTC_MILLIS, &evidence, &endorsements, &reference_values); - let p = AttestationResults::from(&r); + let p = to_attestation_results(&r); assert!(r.is_err()); assert!(p.status() == Status::GenericFailure); diff --git a/oak_client/Cargo.toml b/oak_client/Cargo.toml index 77bbcc5e3fd..e439ae9a18e 100644 --- a/oak_client/Cargo.toml +++ b/oak_client/Cargo.toml @@ -12,6 +12,7 @@ futures-util = "*" log = "*" oak_attestation_verification = { workspace = true } oak_crypto = { workspace = true } +oak_proto_rust = { workspace = true } prost = { workspace = true } tonic = { workspace = true } diff --git a/oak_client/src/lib.rs b/oak_client/src/lib.rs index bb462916384..a5741aac1b4 100644 --- a/oak_client/src/lib.rs +++ b/oak_client/src/lib.rs @@ -16,8 +16,8 @@ pub mod proto { pub mod oak { - pub use oak_attestation_verification::proto::oak::attestation; pub use oak_crypto::proto::oak::crypto; + pub use oak_proto_rust::oak::attestation; pub mod session { pub mod v1 { #![allow(clippy::return_self_not_must_use)] diff --git a/oak_client/src/verifier.rs b/oak_client/src/verifier.rs index 331ef5a2772..dc5fc984857 100644 --- a/oak_client/src/verifier.rs +++ b/oak_client/src/verifier.rs @@ -15,10 +15,8 @@ // use anyhow::Context; -use oak_attestation_verification::{ - proto::oak::attestation::v1::{Endorsements, Evidence}, - verifier::{verify_dice_chain, DiceChainResult}, -}; +use oak_attestation_verification::verifier::{verify_dice_chain, DiceChainResult}; +use oak_proto_rust::oak::attestation::v1::{Endorsements, Evidence}; pub trait AttestationVerifier { fn verify( diff --git a/oak_functions/examples/benchmark/module/Cargo.toml b/oak_functions/examples/benchmark/module/Cargo.toml index adf5c7012fd..eedd9835a70 100644 --- a/oak_functions/examples/benchmark/module/Cargo.toml +++ b/oak_functions/examples/benchmark/module/Cargo.toml @@ -10,7 +10,5 @@ crate-type = ["cdylib", "rlib"] [dependencies] oak_functions_sdk = { workspace = true } +oak_proto_rust = { workspace = true } prost = { workspace = true } - -[build-dependencies] -prost-build = { workspace = true } diff --git a/oak_functions/examples/benchmark/module/build.rs b/oak_functions/examples/benchmark/module/build.rs deleted file mode 100644 index b21694c5159..00000000000 --- a/oak_functions/examples/benchmark/module/build.rs +++ /dev/null @@ -1,29 +0,0 @@ -// -// Copyright 2021 The Project Oak Authors -// -// Licensed 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. -// - -extern crate prost_build; - -fn main() { - let file_paths = ["proto/oak_functions/benchmark.proto"]; - prost_build::compile_protos(&file_paths, &["../../../../"]).expect("proto compilation failed"); - - // Tell cargo to rerun this build script if the proto file has changed. - // https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorerun-if-changedpath - for proto_path in file_paths.iter() { - let file_path = std::path::Path::new(proto_path); - println!("cargo:rerun-if-changed=../../../../{}", file_path.display()); - } -} diff --git a/oak_functions/examples/benchmark/module/src/lib.rs b/oak_functions/examples/benchmark/module/src/lib.rs index 29d6c37bdb0..130dd63fbb7 100644 --- a/oak_functions/examples/benchmark/module/src/lib.rs +++ b/oak_functions/examples/benchmark/module/src/lib.rs @@ -15,12 +15,11 @@ // //! Oak Functions benchmark example. -pub mod proto { - include!(concat!(env!("OUT_DIR"), "/oak.functions.benchmark.rs")); -} +use oak_proto_rust::oak::oak_functions::benchmark::{ + benchmark_request::Action, BenchmarkRequest, EchoAndPanicTest, LookupTest, +}; use prost::Message; -use proto::{benchmark_request::Action, BenchmarkRequest, EchoAndPanicTest, LookupTest}; #[cfg_attr(not(test), no_mangle)] pub extern "C" fn main() { diff --git a/oak_functions/lookup_data_checker/Cargo.toml b/oak_functions/lookup_data_checker/Cargo.toml index c33003005f3..988a38bbed7 100644 --- a/oak_functions/lookup_data_checker/Cargo.toml +++ b/oak_functions/lookup_data_checker/Cargo.toml @@ -12,5 +12,5 @@ clap = { version = "*", features = ["derive"] } env_logger = "*" location_utils = { workspace = true } log = "*" -oak_functions_abi = { workspace = true } +oak_proto_rust = { workspace = true } prost = { workspace = true } diff --git a/oak_functions/lookup_data_checker/src/main.rs b/oak_functions/lookup_data_checker/src/main.rs index 883c02ccd55..2c7e6e9cbf3 100644 --- a/oak_functions/lookup_data_checker/src/main.rs +++ b/oak_functions/lookup_data_checker/src/main.rs @@ -40,8 +40,10 @@ pub fn parse_lookup_entries( let mut entries = HashMap::new(); while lookup_data_buffer.has_remaining() { let entry = - oak_functions_abi::proto::Entry::decode_length_delimited(&mut lookup_data_buffer) - .map_err(|err| anyhow::anyhow!("couldn't decode entry: {err}"))?; + oak_proto_rust::oak::oak_functions::lookup_data::Entry::decode_length_delimited( + &mut lookup_data_buffer, + ) + .map_err(|err| anyhow::anyhow!("couldn't decode entry: {err}"))?; entries.insert(entry.key, entry.value); } Ok(entries) diff --git a/oak_functions/lookup_data_generator/Cargo.toml b/oak_functions/lookup_data_generator/Cargo.toml index 60a677d72ed..372753d7c7c 100644 --- a/oak_functions/lookup_data_generator/Cargo.toml +++ b/oak_functions/lookup_data_generator/Cargo.toml @@ -11,7 +11,7 @@ bytes = "*" clap = { version = "*", features = ["derive"] } location_utils = { workspace = true } multimap = "*" -oak_functions_abi = { workspace = true } +oak_proto_rust = { workspace = true } prost = { workspace = true } rand = "*" serde = { version = "*", features = ["derive"] } diff --git a/oak_functions/lookup_data_generator/src/data.rs b/oak_functions/lookup_data_generator/src/data.rs index 5a4df211a23..8fc892a2056 100644 --- a/oak_functions/lookup_data_generator/src/data.rs +++ b/oak_functions/lookup_data_generator/src/data.rs @@ -21,7 +21,7 @@ use location_utils::{ DEFAULT_CUTOFF_RADIUS_RADIANS, S2_DEFAULT_LEVEL, }; use multimap::MultiMap; -use oak_functions_abi::proto::Entry; +use oak_proto_rust::oak::oak_functions::lookup_data::Entry; use prost::Message; use rand::Rng; use serde::Serialize; diff --git a/oak_functions_abi/Cargo.toml b/oak_functions_abi/Cargo.toml index bc32bdbc227..9a6c28914ac 100644 --- a/oak_functions_abi/Cargo.toml +++ b/oak_functions_abi/Cargo.toml @@ -7,9 +7,5 @@ license = "Apache-2.0" [dependencies] anyhow = { version = "*", default-features = false } -prost = { workspace = true } strum = { version = "*", default-features = false, features = ["derive"] } static_assertions = "*" - -[build-dependencies] -prost-build = { workspace = true } diff --git a/oak_functions_abi/build.rs b/oak_functions_abi/build.rs deleted file mode 100644 index f8eda6b0508..00000000000 --- a/oak_functions_abi/build.rs +++ /dev/null @@ -1,32 +0,0 @@ -// -// Copyright 2021 The Project Oak Authors -// -// Licensed 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. -// - -extern crate prost_build; - -fn main() { - let file_paths = [ - "proto/oak_functions/abi.proto", - "proto/oak_functions/lookup_data.proto", - ]; - prost_build::compile_protos(&file_paths, &[".."]).expect("proto compilation failed"); - - // Tell cargo to rerun this build script if the proto file has changed. - // https://doc.rust-lang.org/cargo/reference/build-scripts.html#cargorerun-if-changedpath - for proto_path in file_paths.iter() { - let file_path = std::path::Path::new(proto_path); - println!("cargo:rerun-if-changed=../{}", file_path.display()); - } -} diff --git a/oak_functions_abi/src/lib.rs b/oak_functions_abi/src/lib.rs index e2d161228bd..cb1595278b9 100644 --- a/oak_functions_abi/src/lib.rs +++ b/oak_functions_abi/src/lib.rs @@ -23,11 +23,6 @@ extern crate alloc; use alloc::vec::Vec; -pub mod proto { - include!(concat!(env!("OUT_DIR"), "/oak.functions.abi.rs")); - include!(concat!(env!("OUT_DIR"), "/oak.functions.lookup_data.rs")); -} - /// See REQUEST_RESPONSE_ENCODING.MD in the crate root. #[derive(Clone, PartialEq, Debug)] pub struct Request { diff --git a/oak_functions_containers_launcher/Cargo.toml b/oak_functions_containers_launcher/Cargo.toml index 57ed1d31335..0c6cec5ed5a 100644 --- a/oak_functions_containers_launcher/Cargo.toml +++ b/oak_functions_containers_launcher/Cargo.toml @@ -17,8 +17,8 @@ log = "*" oak_attestation = { workspace = true } oak_containers_launcher = { workspace = true } oak_crypto = { workspace = true } -oak_functions_abi = { workspace = true } oak_functions_launcher = { workspace = true } +oak_proto_rust = { workspace = true } prost = "*" tokio = { version = "*", features = ["rt-multi-thread", "macros", "sync"] } tonic = { workspace = true } diff --git a/oak_functions_containers_launcher/src/lookup.rs b/oak_functions_containers_launcher/src/lookup.rs index ba6bd6ce149..33b7b5efe88 100644 --- a/oak_functions_containers_launcher/src/lookup.rs +++ b/oak_functions_containers_launcher/src/lookup.rs @@ -137,8 +137,10 @@ fn parse_lookup_entries( let mut entries = HashMap::new(); while lookup_data_buffer.has_remaining() { let entry = - oak_functions_abi::proto::Entry::decode_length_delimited(&mut lookup_data_buffer) - .context("couldn't decode entry")?; + oak_proto_rust::oak::oak_functions::lookup_data::Entry::decode_length_delimited( + &mut lookup_data_buffer, + ) + .context("couldn't decode entry")?; entries.insert(entry.key, entry.value); } Ok(entries) diff --git a/oak_functions_launcher/Cargo.toml b/oak_functions_launcher/Cargo.toml index ccbf39d7c21..39de9a174c9 100644 --- a/oak_functions_launcher/Cargo.toml +++ b/oak_functions_launcher/Cargo.toml @@ -33,6 +33,7 @@ oak_launcher_utils = { workspace = true } micro_rpc = { workspace = true } oak_channel = { workspace = true, features = ["client"] } oak_crypto = { workspace = true } +oak_proto_rust = { workspace = true } hashbrown = "*" ubyte = "*" diff --git a/oak_functions_launcher/src/lookup.rs b/oak_functions_launcher/src/lookup.rs index a51c18d1256..dc35bb70cef 100644 --- a/oak_functions_launcher/src/lookup.rs +++ b/oak_functions_launcher/src/lookup.rs @@ -144,8 +144,10 @@ fn parse_lookup_entries( let mut entries = HashMap::new(); while lookup_data_buffer.has_remaining() { let entry = - oak_functions_abi::proto::Entry::decode_length_delimited(&mut lookup_data_buffer) - .context("couldn't decode entry")?; + oak_proto_rust::oak::oak_functions::lookup_data::Entry::decode_length_delimited( + &mut lookup_data_buffer, + ) + .context("couldn't decode entry")?; entries.insert(entry.key, entry.value); } Ok(entries) diff --git a/oak_functions_service/Cargo.toml b/oak_functions_service/Cargo.toml index 0f902ff002c..c5125d1eb17 100644 --- a/oak_functions_service/Cargo.toml +++ b/oak_functions_service/Cargo.toml @@ -25,6 +25,7 @@ oak_crypto = { workspace = true } oak_dice = { workspace = true } oak_functions_abi = { workspace = true } oak_functions_sdk = { workspace = true } +oak_proto_rust = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } spinning_top = "*" wasmi = { version = "*", default-features = false } @@ -33,7 +34,6 @@ wasmi = { version = "*", default-features = false } micro_rpc_build = { workspace = true } [dev-dependencies] -benchmark = { workspace = true } env_logger = { version = "*", default-features = false } oak_attestation = { workspace = true } oak_functions_test_utils = { workspace = true } diff --git a/oak_functions_service/tests/integration_test.rs b/oak_functions_service/tests/integration_test.rs index 89e5f7052de..ba6e6b0edfa 100644 --- a/oak_functions_service/tests/integration_test.rs +++ b/oak_functions_service/tests/integration_test.rs @@ -22,7 +22,6 @@ extern crate alloc; use core::assert_matches::assert_matches; use std::sync::Arc; -use benchmark::proto::{benchmark_request::Action, BenchmarkRequest, EchoAndPanicTest}; use oak_crypto::{encryptor::ClientEncryptor, proto::oak::crypto::v1::EncryptedRequest}; use oak_functions_service::{ proto::oak::functions::{ @@ -31,6 +30,9 @@ use oak_functions_service::{ }, OakFunctionsService, }; +use oak_proto_rust::oak::oak_functions::benchmark::{ + benchmark_request::Action, BenchmarkRequest, EchoAndPanicTest, +}; use prost::Message; const MOCK_CONSTANT_RESPONSE_SIZE: u32 = 1024; diff --git a/oak_functions_test_utils/Cargo.toml b/oak_functions_test_utils/Cargo.toml index dee150ffa73..4a4d6e568f1 100644 --- a/oak_functions_test_utils/Cargo.toml +++ b/oak_functions_test_utils/Cargo.toml @@ -16,6 +16,7 @@ nix = { version = "*", features = ["process", "signal"] } oak_client = { workspace = true } oak_functions_abi = { workspace = true } oak_functions_client = { workspace = true } +oak_proto_rust = { workspace = true } port_check = "*" prost = { workspace = true } tempfile = "*" diff --git a/oak_functions_test_utils/src/lib.rs b/oak_functions_test_utils/src/lib.rs index 6f4ee7f92a2..74d13541b4b 100644 --- a/oak_functions_test_utils/src/lib.rs +++ b/oak_functions_test_utils/src/lib.rs @@ -79,7 +79,7 @@ pub fn compile_rust_wasm(manifest_path: &str, release: bool) -> anyhow::Result, Vec>) -> Vec { let mut buf = Vec::new(); for (key, value) in entries.into_iter() { - let entry_proto = oak_functions_abi::proto::Entry { key, value }; + let entry_proto = oak_proto_rust::oak::oak_functions::lookup_data::Entry { key, value }; entry_proto .encode_length_delimited(&mut buf) .expect("couldn't encode entry as length delimited"); diff --git a/oak_ml_transparency/runner/Cargo.lock b/oak_ml_transparency/runner/Cargo.lock index a40c2eda1aa..16c25430a8b 100644 --- a/oak_ml_transparency/runner/Cargo.lock +++ b/oak_ml_transparency/runner/Cargo.lock @@ -561,10 +561,9 @@ dependencies = [ "getrandom", "hex", "oak_dice", + "oak_proto_rust", "oak_sev_guest", "p256", - "prost", - "prost-build", "serde", "serde_json", "sha2", @@ -590,6 +589,14 @@ dependencies = [ "zeroize", ] +[[package]] +name = "oak_proto_rust" +version = "0.0.1" +dependencies = [ + "prost", + "prost-build", +] + [[package]] name = "oak_sev_guest" version = "0.1.0" diff --git a/oak_proto_rust/Cargo.toml b/oak_proto_rust/Cargo.toml new file mode 100644 index 00000000000..b66a2cf95c3 --- /dev/null +++ b/oak_proto_rust/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "oak_proto_rust" +version = "0.0.1" +edition = "2021" +license = "Apache-2.0" + +[dependencies] +prost = { workspace = true } + +[build-dependencies] +prost-build = { workspace = true } diff --git a/oak_attestation_verification/build.rs b/oak_proto_rust/build.rs similarity index 89% rename from oak_attestation_verification/build.rs rename to oak_proto_rust/build.rs index c0af47365c5..61eaa2a02d7 100644 --- a/oak_attestation_verification/build.rs +++ b/oak_proto_rust/build.rs @@ -20,6 +20,9 @@ fn main() -> Result<(), Box> { "../proto/attestation/evidence.proto", "../proto/attestation/reference_value.proto", "../proto/attestation/verification.proto", + "../proto/oak_functions/abi.proto", + "../proto/oak_functions/benchmark.proto", + "../proto/oak_functions/lookup_data.proto", ]; prost_build::compile_protos(&proto_paths, &[".."]).expect("proto compilation failed"); diff --git a/oak_proto_rust/src/lib.rs b/oak_proto_rust/src/lib.rs new file mode 100644 index 00000000000..9caa002ecba --- /dev/null +++ b/oak_proto_rust/src/lib.rs @@ -0,0 +1,47 @@ +// Copyright 2023 The Project Oak Authors +// +// Licensed 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. + +// Prost generated code cannot build on its own: it needs a manually crafted module +// structure to include! it. + +#![no_std] + +// Inlined from tonic::include_proto in order to cut dependency on tonic. +macro_rules! include_proto { + ($package: tt) => { + include!(concat!(env!("OUT_DIR"), concat!("/", $package, ".rs"))); + }; +} + +pub mod oak { + include_proto!("oak"); + + pub mod attestation { + pub mod v1 { + include_proto!("oak.attestation.v1"); + } + } + + pub mod oak_functions { + pub mod abi { + include_proto!("oak.functions.abi"); + } + pub mod benchmark { + include_proto!("oak.functions.benchmark"); + } + pub mod lookup_data { + include_proto!("oak.functions.lookup_data"); + } + } +} From 51639608b05398caac0fb18633e77cc5f9d30a9d Mon Sep 17 00:00:00 2001 From: thmsbinder <129782017+thmsbinder@users.noreply.github.com> Date: Wed, 24 Jan 2024 19:39:03 +0100 Subject: [PATCH 29/42] Add verification of AMD hardware certificate chain (#4702) --- Cargo.lock | 141 ++++++++++-- deny.toml | 3 +- enclave_apps/Cargo.lock | 4 +- oak_attestation_verification/Cargo.toml | 10 +- oak_attestation_verification/data/BUILD | 36 +++ .../data/ark_genoa.pem | 37 +++ .../data/ark_milan.pem | 37 +++ .../data/ask_genoa.pem | 37 +++ .../data/ask_milan.pem | 37 +++ oak_attestation_verification/src/amd.rs | 216 ++++++++++++++++++ oak_attestation_verification/src/lib.rs | 1 + oak_attestation_verification/src/verifier.rs | 24 +- oak_attestation_verification/testdata/BUILD | 10 + .../testdata/vcek_milan.der | Bin 0 -> 1361 bytes .../testdata/vcek_milan.pem | 31 +++ .../tests/amd_tests.rs | 114 +++++++++ .../tests/verifier_tests.rs | 4 +- oak_ml_transparency/runner/Cargo.lock | 177 ++++++++++++++ 18 files changed, 898 insertions(+), 21 deletions(-) create mode 100644 oak_attestation_verification/data/BUILD create mode 100644 oak_attestation_verification/data/ark_genoa.pem create mode 100644 oak_attestation_verification/data/ark_milan.pem create mode 100644 oak_attestation_verification/data/ask_genoa.pem create mode 100644 oak_attestation_verification/data/ask_milan.pem create mode 100644 oak_attestation_verification/src/amd.rs create mode 100644 oak_attestation_verification/testdata/vcek_milan.der create mode 100644 oak_attestation_verification/testdata/vcek_milan.pem create mode 100644 oak_attestation_verification/tests/amd_tests.rs diff --git a/Cargo.lock b/Cargo.lock index d3bf569b421..62017b6331b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -64,13 +64,14 @@ dependencies = [ [[package]] name = "ahash" -version = "0.7.7" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +checksum = "77c3a9648d43b9cd48db467b3f87fdd6e146bcc88ab0180006cef2179fe11d01" dependencies = [ - "getrandom", + "cfg-if", "once_cell", "version_check", + "zerocopy", ] [[package]] @@ -82,6 +83,12 @@ dependencies = [ "memchr", ] +[[package]] +name = "allocator-api2" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5" + [[package]] name = "aml" version = "0.16.4" @@ -802,10 +809,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", + "der_derive", + "flagset", "pem-rfc7468", "zeroize", ] +[[package]] +name = "der_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.48", +] + [[package]] name = "deranged" version = "0.3.11" @@ -1009,6 +1029,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flagset" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779" + [[package]] name = "flate2" version = "1.0.28" @@ -1228,15 +1254,16 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +dependencies = [ + "ahash", + "allocator-api2", +] [[package]] name = "heck" @@ -1513,7 +1540,7 @@ dependencies = [ name = "key_value_lookup" version = "0.1.0" dependencies = [ - "hashbrown 0.12.3", + "hashbrown 0.14.3", "http", "log", "maplit", @@ -1809,6 +1836,23 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + [[package]] name = "num-integer" version = "0.1.45" @@ -1819,6 +1863,17 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.17" @@ -1826,6 +1881,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" dependencies = [ "autocfg", + "libm", ] [[package]] @@ -1879,11 +1935,14 @@ dependencies = [ "oak_proto_rust", "oak_sev_guest", "p256", + "p384", "prost", + "rsa", "serde", "serde_json", "sha2", "time", + "x509-cert", "zerocopy", ] @@ -2152,7 +2211,7 @@ name = "oak_echo_service" version = "0.1.0" dependencies = [ "async-trait", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "log", "micro_rpc", "micro_rpc_build", @@ -2264,7 +2323,7 @@ dependencies = [ "command-fds", "env_logger", "futures", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "log", "micro_rpc", "micro_rpc_build", @@ -2305,7 +2364,7 @@ dependencies = [ name = "oak_functions_sdk" version = "0.1.0" dependencies = [ - "hashbrown 0.12.3", + "hashbrown 0.14.3", "lazy_static", "micro_rpc", "micro_rpc_build", @@ -2343,7 +2402,7 @@ dependencies = [ "byteorder", "bytes", "env_logger", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "log", "micro_rpc", "micro_rpc_build", @@ -2413,7 +2472,7 @@ dependencies = [ "bmrng", "clap", "command-fds", - "hashbrown 0.12.3", + "hashbrown 0.14.3", "log", "micro_rpc", "micro_rpc_build", @@ -2770,6 +2829,18 @@ dependencies = [ "sha2", ] +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "parking_lot" version = "0.12.1" @@ -2856,6 +2927,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -3194,6 +3276,26 @@ dependencies = [ "svgbobdoc", ] +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rtnetlink" version = "0.14.0" @@ -4312,9 +4414,9 @@ dependencies = [ [[package]] name = "wasmi_arena" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" [[package]] name = "wasmi_core" @@ -4581,6 +4683,17 @@ dependencies = [ "rand_core", ] +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "spki", +] + [[package]] name = "x86" version = "0.52.0" diff --git a/deny.toml b/deny.toml index 27b38841951..176a6b4a836 100644 --- a/deny.toml +++ b/deny.toml @@ -11,7 +11,8 @@ unmaintained = "deny" unsound = "deny" yanked = "deny" notice = "deny" -ignore = [] +# For crate rsa 0.9.6. Remove when there is a safe upgrade. +ignore = ["RUSTSEC-2023-0071"] [bans] multiple-versions = "allow" diff --git a/enclave_apps/Cargo.lock b/enclave_apps/Cargo.lock index 315176172b7..6b29b87513f 100644 --- a/enclave_apps/Cargo.lock +++ b/enclave_apps/Cargo.lock @@ -1389,9 +1389,9 @@ dependencies = [ [[package]] name = "wasmi_arena" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "401c1f35e413fac1846d4843745589d9ec678977ab35a384db8ae7830525d468" +checksum = "104a7f73be44570cac297b3035d76b169d6599637631cf37a1703326a0727073" [[package]] name = "wasmi_core" diff --git a/oak_attestation_verification/Cargo.toml b/oak_attestation_verification/Cargo.toml index 4ea36355313..fc17f94e60a 100644 --- a/oak_attestation_verification/Cargo.toml +++ b/oak_attestation_verification/Cargo.toml @@ -13,8 +13,8 @@ std = ["time/formatting"] anyhow = { version = "*", default-features = false } base64 = { version = "0.21", default-features = false, features = ["alloc"] } coset = { version = "*", default-features = false } -ecdsa = { version = "*", features = ["pkcs8", "pem"] } -getrandom = { version = "*", features = [ +ecdsa = { version = "*", default-features = false, features = ["pkcs8", "pem"] } +getrandom = { version = "*", default-features = false, features = [ # While getrandom isn't used directly, rdrand is required to support x64_64-unknown-none. "rdrand" ] } @@ -28,6 +28,11 @@ p256 = { version = "*", default-features = false, features = [ "ecdsa", "pem" ] } +p384 = { version = "0.13.0", default-features = false, features = [ + "ecdsa", + "pem" +] } +rsa = { version = "0.9.6", default-features = false } serde = { version = "*", default-features = false, features = ["derive"] } serde_json = { version = "*", default-features = false, features = ["alloc"] } sha2 = { version = "*", default-features = false } @@ -35,6 +40,7 @@ time = { version = "0.3.28", default-features = false, features = [ "serde", "parsing" ] } +x509-cert = { version = "0.2.5", default-features = false, features = ["pem"] } zerocopy = "*" [dev-dependencies] diff --git a/oak_attestation_verification/data/BUILD b/oak_attestation_verification/data/BUILD new file mode 100644 index 00000000000..66718b7aa9e --- /dev/null +++ b/oak_attestation_verification/data/BUILD @@ -0,0 +1,36 @@ +# +# Copyright 2024 The Project Oak Authors +# +# Licensed 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. +# + +package( + default_visibility = ["//visibility:public"], + licenses = ["notice"], +) + +# These certificates are the root of trust of the AMD hardware verification +# chain: ARK -> ASK -> VCEK -> attestation report. The ARK and and ASK +# certificates are parameter-free (with the exception of the chip generation +# which is called `product name` elsewhere). They are valid until 2045 and +# can therefore be statically included. Since they are static it is sufficient +# to verify the ARK -> ASK validity offline in a unit test. +# +# To verify ARK, redownload from https://www.amd.com/en/developer/sev.html and +# convert and compare against the repository copies. +exports_files([ + "ark_genoa.pem", + "ask_genoa.pem", + "ark_milan.pem", + "ask_milan.pem", +]) diff --git a/oak_attestation_verification/data/ark_genoa.pem b/oak_attestation_verification/data/ark_genoa.pem new file mode 100644 index 00000000000..a68860826ea --- /dev/null +++ b/oak_attestation_verification/data/ark_genoa.pem @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIGYzCCBBKgAwIBAgIDAgAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC +BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS +BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg +Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp +Y2VzMRIwEAYDVQQDDAlBUkstR2Vub2EwHhcNMjIwMTI2MTUzNDM3WhcNNDcwMTI2 +MTUzNDM3WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS +BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j +ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLUdlbm9hMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEA3Cd95S/uFOuRIskW9vz9VDBF69NDQF79oRhL +/L2PVQGhK3YdfEBgpF/JiwWFBsT/fXDhzA01p3LkcT/7LdjcRfKXjHl+0Qq/M4dZ +kh6QDoUeKzNBLDcBKDDGWo3v35NyrxbA1DnkYwUKU5AAk4P94tKXLp80oxt84ahy +HoLmc/LqsGsp+oq1Bz4PPsYLwTG4iMKVaaT90/oZ4I8oibSru92vJhlqWO27d/Rx +c3iUMyhNeGToOvgx/iUo4gGpG61NDpkEUvIzuKcaMx8IdTpWg2DF6SwF0IgVMffn +vtJmA68BwJNWo1E4PLJdaPfBifcJpuBFwNVQIPQEVX3aP89HJSp8YbY9lySS6PlV +EqTBBtaQmi4ATGmMR+n2K/e+JAhU2Gj7jIpJhOkdH9firQDnmlA2SFfJ/Cc0mGNz +W9RmIhyOUnNFoclmkRhl3/AQU5Ys9Qsan1jT/EiyT+pCpmnA+y9edvhDCbOG8F2o +xHGRdTBkylungrkXJGYiwGrR8kaiqv7NN8QhOBMqYjcbrkEr0f8QMKklIS5ruOfq +lLMCBw8JLB3LkjpWgtD7OpxkzSsohN47Uom86RY6lp72g8eXHP1qYrnvhzaG1S70 +vw6OkbaaC9EjiH/uHgAJQGxon7u0Q7xgoREWA/e7JcBQwLg80Hq/sbRuqesxz7wB +WSY254cCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSfXfn+Ddjz +WtAzGiXvgSlPvjGoWzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG +KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvR2Vub2EvY3JsMEYGCSqG +SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI +AWUDBAICBQCiAwIBMKMDAgEBA4ICAQAdIlPBC7DQmvH7kjlOznFx3i21SzOPDs5L +7SgFjMC9rR07292GQCA7Z7Ulq97JQaWeD2ofGGse5swj4OQfKfVv/zaJUFjvosZO +nfZ63epu8MjWgBSXJg5QE/Al0zRsZsp53DBTdA+Uv/s33fexdenT1mpKYzhIg/cK +tz4oMxq8JKWJ8Po1CXLzKcfrTphjlbkh8AVKMXeBd2SpM33B1YP4g1BOdk013kqb +7bRHZ1iB2JHG5cMKKbwRCSAAGHLTzASgDcXr9Fp7Z3liDhGu/ci1opGmkp12QNiJ +uBbkTU+xDZHm5X8Jm99BX7NEpzlOwIVR8ClgBDyuBkBC2ljtr3ZSaUIYj2xuyWN9 +5KFY49nWxcz90CFa3Hzmy4zMQmBe9dVyls5eL5p9bkXcgRMDTbgmVZiAf4afe8DL +dmQcYcMFQbHhgVzMiyZHGJgcCrQmA7MkTwEIds1wx/HzMcwU4qqNBAoZV7oeIIPx +dqFXfPqHqiRlEbRDfX1TG5NFVaeByX0GyH6jzYVuezETzruaky6fp2bl2bczxPE8 +HdS38ijiJmm9vl50RGUeOAXjSuInGR4bsRufeGPB9peTa9BcBOeTWzstqTUB/F/q +aZCIZKr4X6TyfUuSDz/1JDAGl+lxdM0P9+lLaP9NahQjHCVf0zf1c1salVuGFk2w +/wMz1R1BHg== +-----END CERTIFICATE----- diff --git a/oak_attestation_verification/data/ark_milan.pem b/oak_attestation_verification/data/ark_milan.pem new file mode 100644 index 00000000000..83863173476 --- /dev/null +++ b/oak_attestation_verification/data/ark_milan.pem @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIGYzCCBBKgAwIBAgIDAQAAMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC +BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS +BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg +Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp +Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTcyMzA1WhcNNDUxMDIy +MTcyMzA1WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS +BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j +ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJQVJLLU1pbGFuMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEA0Ld52RJOdeiJlqK2JdsVmD7FktuotWwX1fNg +W41XY9Xz1HEhSUmhLz9Cu9DHRlvgJSNxbeYYsnJfvyjx1MfU0V5tkKiU1EesNFta +1kTA0szNisdYc9isqk7mXT5+KfGRbfc4V/9zRIcE8jlHN61S1ju8X93+6dxDUrG2 +SzxqJ4BhqyYmUDruPXJSX4vUc01P7j98MpqOS95rORdGHeI52Naz5m2B+O+vjsC0 +60d37jY9LFeuOP4Meri8qgfi2S5kKqg/aF6aPtuAZQVR7u3KFYXP59XmJgtcog05 +gmI0T/OitLhuzVvpZcLph0odh/1IPXqx3+MnjD97A7fXpqGd/y8KxX7jksTEzAOg +bKAeam3lm+3yKIcTYMlsRMXPcjNbIvmsBykD//xSniusuHBkgnlENEWx1UcbQQrs ++gVDkuVPhsnzIRNgYvM48Y+7LGiJYnrmE8xcrexekBxrva2V9TJQqnN3Q53kt5vi +Qi3+gCfmkwC0F0tirIZbLkXPrPwzZ0M9eNxhIySb2npJfgnqz55I0u33wh4r0ZNQ +eTGfw03MBUtyuzGesGkcw+loqMaq1qR4tjGbPYxCvpCq7+OgpCCoMNit2uLo9M18 +fHz10lOMT8nWAUvRZFzteXCm+7PHdYPlmQwUw3LvenJ/ILXoQPHfbkH0CyPfhl1j +WhJFZasCAwEAAaN+MHwwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFrBrRQ/fI +rFXUxR1BSKvVeErUUzAPBgNVHRMBAf8EBTADAQH/MDoGA1UdHwQzMDEwL6AtoCuG +KWh0dHBzOi8va2RzaW50Zi5hbWQuY29tL3ZjZWsvdjEvTWlsYW4vY3JsMEYGCSqG +SIb3DQEBCjA5oA8wDQYJYIZIAWUDBAICBQChHDAaBgkqhkiG9w0BAQgwDQYJYIZI +AWUDBAICBQCiAwIBMKMDAgEBA4ICAQC6m0kDp6zv4Ojfgy+zleehsx6ol0ocgVel +ETobpx+EuCsqVFRPK1jZ1sp/lyd9+0fQ0r66n7kagRk4Ca39g66WGTJMeJdqYriw +STjjDCKVPSesWXYPVAyDhmP5n2v+BYipZWhpvqpaiO+EGK5IBP+578QeW/sSokrK +dHaLAxG2LhZxj9aF73fqC7OAJZ5aPonw4RE299FVarh1Tx2eT3wSgkDgutCTB1Yq +zT5DuwvAe+co2CIVIzMDamYuSFjPN0BCgojl7V+bTou7dMsqIu/TW/rPCX9/EUcp +KGKqPQ3P+N9r1hjEFY1plBg93t53OOo49GNI+V1zvXPLI6xIFVsh+mto2RtgEX/e +pmMKTNN6psW88qg7c1hTWtN6MbRuQ0vm+O+/2tKBF2h8THb94OvvHHoFDpbCELlq +HnIYhxy0YKXGyaW1NjfULxrrmxVW4wcn5E8GddmvNa6yYm8scJagEi13mhGu4Jqh +3QU3sf8iUSUr09xQDwHtOQUVIqx4maBZPBtSMf+qUDtjXSSq8lfWcd8bLr9mdsUn +JZJ0+tuPMKmBnSH860llKk+VpVQsgqbzDIvOLvD6W1Umq25boxCYJ+TuBoa4s+HH +CViAvgT9kf/rBq1d+ivj6skkHxuzcxbk1xv6ZGxrteJxVH7KlX7YRdZ6eARKwLe4 +AFZEAwoKCQ== +-----END CERTIFICATE----- diff --git a/oak_attestation_verification/data/ask_genoa.pem b/oak_attestation_verification/data/ask_genoa.pem new file mode 100644 index 00000000000..215b7840912 --- /dev/null +++ b/oak_attestation_verification/data/ask_genoa.pem @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIGiTCCBDigAwIBAgIDAgACMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC +BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS +BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg +Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp +Y2VzMRIwEAYDVQQDDAlBUkstR2Vub2EwHhcNMjIxMDMxMTMzMzQ4WhcNNDcxMDMx +MTMzMzQ4WjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS +BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j +ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJU0VWLUdlbm9hMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAoHJhvk4Fwwkwb03AMfLySXJSXmEaCZMTRbLg +Paj4oEzaD9tGfxCSw/nsCAiXHQaWUt++bnbjJO05TKT5d+Cdrz4/fiRBpbhf0xzv +h11O+wJTBPj3uCzDm48vEZ8l5SXMO4wd/QqwsrejFERPD/Hdfv1mGCMW7ac0ug8t +rDzqGe+l+p8NMjp/EqBDY2vd8hLaVLmS+XjAqlYVNRksh9aTzSYL19/cTrBDmqQ2 +y8k23zNl2lW6q/BtQOpWGVs3EWvBHb/Qnf3f3S9+lC4H2jdDy9yn7kqyTWq4WCBn +E4qhYJRokulYtzMZM1Ilk4Z6RPkOTR1MJ4gdFtj7lKmrkSuOoJYmqhJIsQJ854lA +bJybgU7zyzWAwu3uaslkYKUEAQf2ja5Hyl3IBqOzpqY31SpKzbl8NXveZybRMklw +fe4iDLI25T9ku9CVetDYifCbdGeuHdTwZBBemW4NE57L7iEV8+zz8nxng8OMX//4 +pXntWqmQbEAnBLv2ToTgd1H2zYRthyDLc3V119/+FnTW17LK6bKzTCgEnCHQEcAt +0hDQLLF799+2lZTxxfBEoduAZax6IjgAMCi6e1ZfKPJSkdvb2m3BwfP8bniG7+AE +Jv1WOEmnBJc1pVQCttbJUodbi07Vfen5JRUqAvSM3ObWQOzSAGzsGnpIigwFpW6m +9F7uYVUCAwEAAaOBozCBoDAdBgNVHQ4EFgQUssZ7pDW7HJVkHAmgQf/F3EmGFVow +HwYDVR0jBBgwFoAUn135/g3Y81rQMxol74EpT74xqFswEgYDVR0TAQH/BAgwBgEB +/wIBADAOBgNVHQ8BAf8EBAMCAQQwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cHM6Ly9r +ZHNpbnRmLmFtZC5jb20vdmNlay92MS9HZW5vYS9jcmwwRgYJKoZIhvcNAQEKMDmg +DzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKID +AgEwowMCAQEDggIBAIgu3V2tQJOo0/6GvNmwLXbLDrsLKXqHUqdGyOZUpPHM3ujT +aex1G+8bEgBswwBa+wNvl1SQqRqy2x2QwP+i//BcWr3lMrUxci4G7/P8hZBV821n +rAUZtbvfqla5MrRH9AKJXWW/pmtd10czqCHkzdLQNZNjt2dnZHMQAMtGs1AtynRE +HNwEBiH2KAt7gUc/sKWnSCipztKE76puN/XXbSx+Ws+VPiFw6CBAeI9dqnEiQ1tp +EgqtWEtcKm7Ggb1XH6oWbISoowvc00/ADWfNom0xl6v2C6RIWYgUoZ2f7PCyV3Dt +bu/fQfyyZvmtVLA4gB2Ehc6Omjy21Y55WY9IweHlKENMPEUVtRqOvRVI0ml9Wbal +f049joCu2j33XPqwp3IrzevmPBDGpR2Stdm3K66a/g/BSY7Wc9/VeykP3RXlxY1T +MMJ8F1lpg6Tmu+c+vow7cliyqOoayAnR71U8+rWrL3HRHheSVX8GPYOaDNBTt831 +Z027vDWv3811vMoxYxhuTRaokvNWCSzmJ2EWrPYHcHOtkjSFKN7ot0Rc70fIRZEY +c2rb3ywLSicEq3JQCnnz6iCZ1tMfplzcrJ2LnW2F1C8yRV+okylyORlsaxOLKYOW +jaDTSFaq1NIwodHp7X9fOG48uRuJWS8GmifD969sC4Ut2FJFoklceBVUNCHR +-----END CERTIFICATE----- diff --git a/oak_attestation_verification/data/ask_milan.pem b/oak_attestation_verification/data/ask_milan.pem new file mode 100644 index 00000000000..26c059c70ea --- /dev/null +++ b/oak_attestation_verification/data/ask_milan.pem @@ -0,0 +1,37 @@ +-----BEGIN CERTIFICATE----- +MIIGiTCCBDigAwIBAgIDAQABMEYGCSqGSIb3DQEBCjA5oA8wDQYJYIZIAWUDBAIC +BQChHDAaBgkqhkiG9w0BAQgwDQYJYIZIAWUDBAICBQCiAwIBMKMDAgEBMHsxFDAS +BgNVBAsMC0VuZ2luZWVyaW5nMQswCQYDVQQGEwJVUzEUMBIGA1UEBwwLU2FudGEg +Q2xhcmExCzAJBgNVBAgMAkNBMR8wHQYDVQQKDBZBZHZhbmNlZCBNaWNybyBEZXZp +Y2VzMRIwEAYDVQQDDAlBUkstTWlsYW4wHhcNMjAxMDIyMTgyNDIwWhcNNDUxMDIy +MTgyNDIwWjB7MRQwEgYDVQQLDAtFbmdpbmVlcmluZzELMAkGA1UEBhMCVVMxFDAS +BgNVBAcMC1NhbnRhIENsYXJhMQswCQYDVQQIDAJDQTEfMB0GA1UECgwWQWR2YW5j +ZWQgTWljcm8gRGV2aWNlczESMBAGA1UEAwwJU0VWLU1pbGFuMIICIjANBgkqhkiG +9w0BAQEFAAOCAg8AMIICCgKCAgEAnU2drrNTfbhNQIllf+W2y+ROCbSzId1aKZft +2T9zjZQOzjGccl17i1mIKWl7NTcB0VYXt3JxZSzOZjsjLNVAEN2MGj9TiedL+Qew +KZX0JmQEuYjm+WKksLtxgdLp9E7EZNwNDqV1r0qRP5tB8OWkyQbIdLeu4aCz7j/S +l1FkBytev9sbFGzt7cwnjzi9m7noqsk+uRVBp3+In35QPdcj8YflEmnHBNvuUDJh +LCJMW8KOjP6++Phbs3iCitJcANEtW4qTNFoKW3CHlbcSCjTM8KsNbUx3A8ek5EVL +jZWH1pt9E3TfpR6XyfQKnY6kl5aEIPwdW3eFYaqCFPrIo9pQT6WuDSP4JCYJbZne +KKIbZjzXkJt3NQG32EukYImBb9SCkm9+fS5LZFg9ojzubMX3+NkBoSXI7OPvnHMx +jup9mw5se6QUV7GqpCA2TNypolmuQ+cAaxV7JqHE8dl9pWf+Y3arb+9iiFCwFt4l +AlJw5D0CTRTC1Y5YWFDBCrA/vGnmTnqG8C+jjUAS7cjjR8q4OPhyDmJRPnaC/ZG5 +uP0K0z6GoO/3uen9wqshCuHegLTpOeHEJRKrQFr4PVIwVOB0+ebO5FgoyOw43nyF +D5UKBDxEB4BKo/0uAiKHLRvvgLbORbU8KARIs1EoqEjmF8UtrmQWV2hUjwzqwvHF +ei8rPxMCAwEAAaOBozCBoDAdBgNVHQ4EFgQUO8ZuGCrD/T1iZEib47dHLLT8v/gw +HwYDVR0jBBgwFoAUhawa0UP3yKxV1MUdQUir1XhK1FMwEgYDVR0TAQH/BAgwBgEB +/wIBADAOBgNVHQ8BAf8EBAMCAQQwOgYDVR0fBDMwMTAvoC2gK4YpaHR0cHM6Ly9r +ZHNpbnRmLmFtZC5jb20vdmNlay92MS9NaWxhbi9jcmwwRgYJKoZIhvcNAQEKMDmg +DzANBglghkgBZQMEAgIFAKEcMBoGCSqGSIb3DQEBCDANBglghkgBZQMEAgIFAKID +AgEwowMCAQEDggIBAIgeUQScAf3lDYqgWU1VtlDbmIN8S2dC5kmQzsZ/HtAjQnLE +PI1jh3gJbLxL6gf3K8jxctzOWnkYcbdfMOOr28KT35IaAR20rekKRFptTHhe+DFr +3AFzZLDD7cWK29/GpPitPJDKCvI7A4Ug06rk7J0zBe1fz/qe4i2/F12rvfwCGYhc +RxPy7QF3q8fR6GCJdB1UQ5SlwCjFxD4uezURztIlIAjMkt7DFvKRh+2zK+5plVGG +FsjDJtMz2ud9y0pvOE4j3dH5IW9jGxaSGStqNrabnnpF236ETr1/a43b8FFKL5QN +mt8Vr9xnXRpznqCRvqjr+kVrb6dlfuTlliXeQTMlBoRWFJORL8AcBJxGZ4K2mXft +l1jU5TLeh5KXL9NW7a/qAOIUs2FiOhqrtzAhJRg9Ij8QkQ9Pk+cKGzw6El3T3kFr +Eg6zkxmvMuabZOsdKfRkWfhH2ZKcTlDfmH1H0zq0Q2bG3uvaVdiCtFY1LlWyB38J +S2fNsR/Py6t5brEJCFNvzaDky6KeC4ion/cVgUai7zzS3bGQWzKDKU35SqNU2WkP +I8xCZ00WtIiKKFnXWUQxvlKmmgZBIYPe01zD0N8atFxmWiSnfJl690B9rJpNR/fI +ajxCW3Seiws6r1Zm+tCuVbMiNtpS9ThjNX4uve5thyfE2DgoxRFvY1CsoF5M +-----END CERTIFICATE----- diff --git a/oak_attestation_verification/src/amd.rs b/oak_attestation_verification/src/amd.rs new file mode 100644 index 00000000000..20606a36a4e --- /dev/null +++ b/oak_attestation_verification/src/amd.rs @@ -0,0 +1,216 @@ +// +// Copyright 2023 The Project Oak Authors +// +// Licensed 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. +// + +//! Contains code related to AMD hardware certificates and attestation report. + +use alloc::string::String; + +use oak_sev_guest::guest::{AttestationReport, TcbVersion}; +use p256::pkcs8::ObjectIdentifier; +use rsa::{pss::Signature, signature::Verifier, RsaPublicKey}; +use sha2::Sha384; +use x509_cert::{ + der::{referenced::OwnedToRef, Encode}, + Certificate, Version, +}; +use zerocopy::{AsBytes, FromZeroes}; + +// The keys in the key-value map of X509 certificates are Object Identifiers +// (OIDs) which have a global registry. The present OIDs are taken from +// Table 8 of +// https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/specifications/57230.pdf +const RSA_SSA_PSS_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.2.840.113549.1.1.10"); +const _PRODUCT_NAME_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.3704.1.2"); +const BL_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.3704.1.3.1"); +const TEE_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.3704.1.3.2"); +const SNP_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.3704.1.3.3"); +const CHIP_ID_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.3704.1.4"); +const UCODE_OID: ObjectIdentifier = ObjectIdentifier::new_unwrap("1.3.6.1.4.1.3704.1.3.8"); + +// Verifies validity of a matching ARK, ASK certificate pair. +// +// Validate at least a subset of Appendix B.3 of +// https://www.amd.com/content/dam/amd/en/documents/epyc-technical-docs/programmer-references/55766_SEV-KM_API_Specification.pdf +// Ideally, we'd check everything listed there. +pub fn validate_ark_ask_certs(ark: &Certificate, ask: &Certificate) -> anyhow::Result<()> { + anyhow::ensure!( + ark.tbs_certificate.version == Version::V3, + "unexpected version of ARK cert" + ); + anyhow::ensure!( + ask.tbs_certificate.version == Version::V3, + "unexpected version of ASK cert" + ); + + verify_cert_signature(ark, ask)?; + verify_cert_signature(ark, ark)?; + + Ok(()) +} + +pub fn verify_cert_signature(signer: &Certificate, signee: &Certificate) -> anyhow::Result<()> { + anyhow::ensure!( + signee.signature_algorithm.oid == RSA_SSA_PSS_OID, + "unsupported signature algorithm: {:?}", + signee.signature_algorithm + ); + + let verifying_key = { + let pubkey_info = signer + .tbs_certificate + .subject_public_key_info + .owned_to_ref(); + let pubkey = RsaPublicKey::try_from(pubkey_info) + .map_err(|_err| anyhow::anyhow!("could not parse RSA public key"))?; + rsa::pss::VerifyingKey::::new(pubkey) + }; + + let message = signee + .tbs_certificate + .to_der() + .map_err(|_err| anyhow::anyhow!("could not extract message to verify RSA signature"))?; + let signature = Signature::try_from(signee.signature.raw_bytes()) + .map_err(|_err| anyhow::anyhow!("could not extract RSA signature"))?; + + verifying_key + .verify(&message, &signature) + .map_err(|_err| anyhow::anyhow!("signature verification failed")) +} + +// Currently unused, use `pub` only to disable the warning. +fn _product_name(cert: &Certificate) -> anyhow::Result { + let exts = cert + .tbs_certificate + .extensions + .as_ref() + .ok_or_else(|| anyhow::anyhow!("could not get extensions from cert"))?; + let pn_ext = exts + .iter() + .find(|&ext| ext.extn_id == _PRODUCT_NAME_OID) + .ok_or_else(|| anyhow::anyhow!("no product name found in cert"))?; + String::from_utf8(pn_ext.extn_value.as_bytes().to_vec()) + .map_err(|_utf8_err| anyhow::anyhow!("failed to read product name")) +} + +fn chip_id(cert: &Certificate) -> anyhow::Result<[u8; 64]> { + let exts = cert + .tbs_certificate + .extensions + .as_ref() + .ok_or_else(|| anyhow::anyhow!("could not get extensions from cert"))?; + let chip_id_ext = exts + .iter() + .find(|&ext| ext.extn_id == CHIP_ID_OID) + .ok_or_else(|| anyhow::anyhow!("no chip ID found in cert"))?; + let chip_id = chip_id_ext.extn_value.as_bytes().to_vec(); + anyhow::ensure!(chip_id.len() == 64, "length of chip ID value is not 64"); + + // Copy into array of fixed length. + let mut result = [0; 64]; + result.copy_from_slice(&chip_id[0..64]); + Ok(result) +} + +fn tcb_version(vcek: &Certificate) -> anyhow::Result { + let mut tcb = TcbVersion::new_zeroed(); + for ext in vcek + .tbs_certificate + .extensions + .as_ref() + .ok_or_else(|| anyhow::anyhow!("could not get extensions from VCEK cert"))? + { + // What's the appropriate way of extracting u8 from OctetString? + let arr = ext.extn_value.as_bytes(); + let last = arr.len() - 1; + if ext.extn_id == BL_OID { + tcb.boot_loader = arr[last]; + } else if ext.extn_id == TEE_OID { + tcb.tee = arr[last]; + } else if ext.extn_id == SNP_OID { + tcb.snp = arr[last]; + } else if ext.extn_id == UCODE_OID { + tcb.microcode = arr[last]; + } + } + Ok(tcb) +} + +pub fn verify_attestation_report_signature( + vcek: &Certificate, + report: &AttestationReport, +) -> anyhow::Result<()> { + // First check some necessary conditions for the signature to be valid. + let arpt_chip_id = report.data.chip_id; + let vcek_chip_id = chip_id(vcek)?; + anyhow::ensure!( + arpt_chip_id == vcek_chip_id, + "chip id differs attestation={} vcek={}", + hex::encode(arpt_chip_id), + hex::encode(vcek_chip_id) + ); + + let arpt_tcb = &report.data.reported_tcb; + let vcek_tcb = tcb_version(vcek)?; + anyhow::ensure!( + arpt_tcb.snp == vcek_tcb.snp, + "mismatch in snp field of TCB version: report={} vcek={}", + arpt_tcb.snp, + vcek_tcb.snp + ); + anyhow::ensure!( + arpt_tcb.microcode == vcek_tcb.microcode, + "mismatch in microcode field of TCB version: report={} vcek={}", + arpt_tcb.microcode, + vcek_tcb.microcode + ); + anyhow::ensure!( + arpt_tcb.tee == vcek_tcb.tee, + "mismatch in tee field of TCB version: report={} vcek={}", + arpt_tcb.tee, + vcek_tcb.tee + ); + anyhow::ensure!( + arpt_tcb.boot_loader == vcek_tcb.boot_loader, + "mismatch in boot_loader field of TCB version: report={} vcek={}", + arpt_tcb.boot_loader, + vcek_tcb.boot_loader + ); + + let verifying_key = { + let pubkey_info = vcek.tbs_certificate.subject_public_key_info.owned_to_ref(); + p384::ecdsa::VerifyingKey::from_sec1_bytes(pubkey_info.subject_public_key.raw_bytes()) + .map_err(|_err| anyhow::anyhow!("could not extract ECDSA P-384 public key")) + }?; + let message = report.data.as_bytes(); + + // `report.signature.{r,s}` contain 48 bytes, the rest is zero padded + // to allow longer signatures as well. The 48 bytes are interpreted as + // a single little-endian encoded integer. `p384::ecdsa::Signature` + // requires big-endian, so need to mirror. + let mut r: [u8; 48] = [0; 48]; + let mut s: [u8; 48] = [0; 48]; + for i in 0..48 { + let j = 47 - i; + r[i] = report.signature.r[j]; + s[i] = report.signature.s[j]; + } + let signature = p384::ecdsa::Signature::from_scalars(r, s) + .map_err(|_err| anyhow::anyhow!("could not extract ECDSA P-384 signature"))?; + + verifying_key + .verify(message, &signature) + .map_err(|_err| anyhow::anyhow!("failed to verify ECDSA P-384 signature")) +} diff --git a/oak_attestation_verification/src/lib.rs b/oak_attestation_verification/src/lib.rs index c443c654dcf..82e41bebdac 100644 --- a/oak_attestation_verification/src/lib.rs +++ b/oak_attestation_verification/src/lib.rs @@ -19,6 +19,7 @@ extern crate alloc; +pub mod amd; pub mod claims; pub mod endorsement; pub mod rekor; diff --git a/oak_attestation_verification/src/verifier.rs b/oak_attestation_verification/src/verifier.rs index fc018b15cdc..e4929bf6df9 100644 --- a/oak_attestation_verification/src/verifier.rs +++ b/oak_attestation_verification/src/verifier.rs @@ -43,10 +43,15 @@ use oak_proto_rust::oak::{ RawDigest, }; use oak_sev_guest::guest::{AttestationReport, PolicyFlags}; +use x509_cert::{ + der::{Decode, DecodePem}, + Certificate, +}; use zerocopy::FromBytes; use crate::{ alloc::string::ToString, + amd::{verify_attestation_report_signature, verify_cert_signature}, claims::{get_digest, parse_endorsement_statement}, endorsement::verify_binary_endorsement, util::{ @@ -54,6 +59,8 @@ use crate::{ }, }; +const ASK_MILAN_CERT_PEM: &str = include_str!("../data/ask_milan.pem"); + // We don't use additional authenticated data. const ADDITIONAL_DATA: &[u8] = b""; @@ -326,11 +333,16 @@ fn verify_cb( /// Verifies the AMD SEV attestation report. fn verify_amd_sev_attestation_report( report: &[u8], + vcek: &Certificate, reference_values: &AmdSevReferenceValues, ) -> anyhow::Result<()> { let parsed = AttestationReport::ref_from(report) .ok_or_else(|| anyhow::anyhow!("could not parse AMD SEV attestation report"))?; parsed.validate().map_err(|msg| anyhow::anyhow!(msg))?; + + // We demand that the attestation report is signed by the VCEK public key. + verify_attestation_report_signature(vcek, parsed)?; + let data = &parsed.data; // Verify that we are not in debug mode. @@ -370,7 +382,17 @@ fn verify_root_layer( .amd_sev .as_ref() .ok_or_else(|| anyhow::anyhow!("no AMD SEV reference values"))?; - verify_amd_sev_attestation_report(&l.remote_attestation_report, amd_sev)? + + // We demand that product-specific ASK signs the VCEK. + let vcek = Certificate::from_der(&e.tee_certificate) + .map_err(|_err| anyhow::anyhow!("could not parse VCEK cert"))?; + // Right now there are only Milan CPUs, so it is not urgent to code the + // decision between Milan and Genoa which would appear here. + let ask_milan = Certificate::from_pem(ASK_MILAN_CERT_PEM) + .map_err(|_err| anyhow::anyhow!("could not parse ASK cert"))?; + verify_cert_signature(&ask_milan, &vcek)?; + + verify_amd_sev_attestation_report(&l.remote_attestation_report, &vcek, amd_sev)? } TeePlatform::IntelTdx => { let intel_tdx = r diff --git a/oak_attestation_verification/testdata/BUILD b/oak_attestation_verification/testdata/BUILD index b00ba2e27f0..0d91903ed7a 100644 --- a/oak_attestation_verification/testdata/BUILD +++ b/oak_attestation_verification/testdata/BUILD @@ -27,4 +27,14 @@ exports_files([ "logentry.json", "oak-development.pem", "rekor_public_key.pem", + # The VCEK cert needs to match the hardware ID of the attestation report in the + # test evidence proto. The additional parameters in the URL encode the reported + # TCB version in the attestation report. + # + # URL for downloading the VCEK cert: + # https://kdsintf.amd.com/vcek/v1/Milan/cd3c4e6b5b64026ac135d76f888ea6bcc1351ec610d64b0af4028422b84c17ad2571905acfe2eb2181c119df4241e94a926d1b06c02e82845416202151212fdd?ucodeSPL=168&snpSPL=8&teeSPL=0&blSPL=3 + # ARPT reported TCB version: ucodeSPL=168&snpSPL=8&teeSPL=0&blSPL=3 + # ARPT current TCB version: ucodeSPL=209&snpSPL=14&teeSPL=0&blSPL=3 + "vcek_milan.der", + "vcek_milan.pem", ]) diff --git a/oak_attestation_verification/testdata/vcek_milan.der b/oak_attestation_verification/testdata/vcek_milan.der new file mode 100644 index 0000000000000000000000000000000000000000..91cb0d847444fece0002bba1181074af1f28820c GIT binary patch literal 1361 zcmXqLV)Zp>V*0aynTe5!iIKs;jg3>Q&7$*l(NLUIdNVi69YpdBSUjjQzOGDab9CXB(6af%w}3UPSAiK z;y4bFy$#BM?qvrC@i|*2MmARMMivGo<|GD|LROnwCUr>}D{NF0ezO%jGszZb zZ+vUQuh5^+|K|F^RJL;V=^~t>OdfY0JSx&?nY-e_yWHiSwk!*Jg9LkdocI)WN>}i` zWtotXT3T~2%(+G2{d-6E{eBf3dUr+gQlwcIH!+GEG%*STZRgZxV`O2RQ^Cl{0*opK z195~16APCZ2RICMoeT^F*|=aznZatAQG}Qv+L2YUU<$Ef3bA1dv11A`gM>JMi3s5c z4i;b{WLRO~i*PXui^EwPzwGD~rmTae*Yi92mhCxcDtAoanm5-MrWU0gKH_Ut3nxUK zfAm_h@u1{=C&!mwlX9im4(K(tgor691}f^`rB<3}ZUTm;+;MT~>O!-rmt#7T&s|u5 z#fQmy`gK*d@A(yxx9?wIRn@G#yxvUvf|tBp$cBqwJA_})TUxyJlXBq^*?e}xwYS_`g$oI*_0K0?R^h;0uxnj08>$_f>>zb1yMYU2E zccKgwPe$|#r*|jUYzsAPdOJNXt=aI<&JzviWrKcet2|`f9&G>i@du;bmTD(=+fP|$ z@vnz1c9Z$Hmh;nRblYte`hNRg%9=2_q^hVcNA$CQk&E^NBAGB>Bu_0)c1X*470-D4c6>@zbmrt zD~aaBE?3=`9`(BI&F?qz@kvJy3CnQ0-8j*m)hMn$p=-JO?0oMy*Q}Ml>L*zjzxrUn z!0gSyqIOI&thvZbex`|3pKamMs44GMQZ}<%IOUzXi8&f2` anyhow::Result<()> { + for ext in cert + .tbs_certificate + .extensions + .as_ref() + .ok_or_else(|| anyhow::anyhow!("could not get extensions from cert"))? + { + eprintln!( + "cert ext id={} val={}", + ext.extn_id, + hex::encode(ext.extn_value.as_bytes()) + ); + } + Ok(()) +} + +#[test] +fn print_all_certs() { + let ark = Certificate::from_pem(ARK_MILAN_CERT_PEM).expect("could not parse cert"); + let ask = Certificate::from_pem(ASK_MILAN_CERT_PEM).expect("could not parse cert"); + let vcek = Certificate::from_pem(VCEK_MILAN_CERT_PEM).expect("could not parse cert"); + eprint_exts(&ark).expect("error"); + eprint_exts(&ask).expect("error"); + eprint_exts(&vcek).expect("error"); +} + +#[test] +fn milan_ark_signs_itself() { + let ark = Certificate::from_pem(ARK_MILAN_CERT_PEM).expect("could not parse cert"); + assert!(verify_cert_signature(&ark, &ark).is_ok()); +} + +#[test] +fn milan_ark_signs_ask() { + let ark = Certificate::from_pem(ARK_MILAN_CERT_PEM).expect("could not parse cert"); + let ask = Certificate::from_pem(ASK_MILAN_CERT_PEM).expect("could not parse cert"); + assert!(verify_cert_signature(&ark, &ask).is_ok()); +} + +#[test] +fn milan_ask_signs_vcek() { + let ask = Certificate::from_pem(ASK_MILAN_CERT_PEM).expect("could not parse cert"); + let vcek = Certificate::from_pem(VCEK_MILAN_CERT_PEM).expect("could not parse cert"); + assert!(verify_cert_signature(&ask, &vcek).is_ok()); +} + +#[test] +fn genoa_ark_signs_itself() { + let ark = x509_cert::Certificate::from_pem(ARK_GENOA_CERT_PEM).expect("could not parse cert"); + assert!(verify_cert_signature(&ark, &ark).is_ok()); +} + +#[test] +fn genoa_ark_signs_ask() { + let ark = Certificate::from_pem(ARK_GENOA_CERT_PEM).expect("could not parse cert"); + let ask = Certificate::from_pem(ASK_GENOA_CERT_PEM).expect("could not parse cert"); + assert!(verify_cert_signature(&ark, &ask).is_ok()); +} + +// Negative test just to double check. +#[test] +fn genoa_ark_does_not_sign_milan_ask() { + let ark = Certificate::from_pem(ARK_GENOA_CERT_PEM).expect("could not parse cert"); + let ask = Certificate::from_pem(ASK_MILAN_CERT_PEM).expect("could not parse cert"); + assert!(verify_cert_signature(&ark, &ask).is_err()); +} + +// Negative test just to double check. +#[test] +fn milan_ark_does_not_sign_vcek() { + let ark = Certificate::from_pem(ARK_MILAN_CERT_PEM).expect("could not parse cert"); + let vcek = Certificate::from_pem(VCEK_MILAN_CERT_PEM).expect("could not parse cert"); + assert!(verify_cert_signature(&ark, &vcek).is_err()); +} + +#[test] +fn validate_milan() { + let ark = Certificate::from_pem(ARK_MILAN_CERT_PEM).expect("could not parse cert"); + let ask = Certificate::from_pem(ASK_MILAN_CERT_PEM).expect("could not parse cert"); + assert!(validate_ark_ask_certs(&ark, &ask).is_ok()); +} + +#[test] +fn validate_genoa() { + let ark = Certificate::from_pem(ARK_GENOA_CERT_PEM).expect("could not parse cert"); + let ask = Certificate::from_pem(ASK_GENOA_CERT_PEM).expect("could not parse cert"); + assert!(validate_ark_ask_certs(&ark, &ask).is_ok()); +} diff --git a/oak_attestation_verification/tests/verifier_tests.rs b/oak_attestation_verification/tests/verifier_tests.rs index af00340cdb2..01005203b19 100644 --- a/oak_attestation_verification/tests/verifier_tests.rs +++ b/oak_attestation_verification/tests/verifier_tests.rs @@ -33,6 +33,7 @@ use prost::Message; const ENDORSEMENT_PATH: &str = "testdata/endorsement.json"; const SIGNATURE_PATH: &str = "testdata/endorsement.json.sig"; const LOG_ENTRY_PATH: &str = "testdata/logentry.json"; +const VCEK_MILAN_CERT_DER: &str = "testdata/vcek_milan.der"; const ENDORSER_PUBLIC_KEY_PATH: &str = "testdata/oak-development.pem"; const REKOR_PUBLIC_KEY_PATH: &str = "testdata/rekor_public_key.pem"; const EVIDENCE_PATH: &str = "testdata/evidence.binarypb"; @@ -51,6 +52,7 @@ fn create_endorsements() -> Endorsements { let endorsement = fs::read(ENDORSEMENT_PATH).expect("couldn't read endorsement"); let signature = fs::read(SIGNATURE_PATH).expect("couldn't read signature"); let log_entry = fs::read(LOG_ENTRY_PATH).expect("couldn't read log entry"); + let vcek_milan_cert = fs::read(VCEK_MILAN_CERT_DER).expect("couldn't read TEE cert"); // Use this for all binaries. let tre = TransparentReleaseEndorsement { @@ -60,7 +62,7 @@ fn create_endorsements() -> Endorsements { }; let root_layer = RootLayerEndorsements { - tee_certificate: b"T O D O".to_vec(), + tee_certificate: vcek_milan_cert, stage0: Some(tre.clone()), }; let kernel_layer = KernelLayerEndorsements { diff --git a/oak_ml_transparency/runner/Cargo.lock b/oak_ml_transparency/runner/Cargo.lock index 16c25430a8b..e77dfb41ffe 100644 --- a/oak_ml_transparency/runner/Cargo.lock +++ b/oak_ml_transparency/runner/Cargo.lock @@ -261,10 +261,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fffa369a668c8af7dbf8b5e56c9f744fbd399949ed171606040001947de40b1c" dependencies = [ "const-oid", + "der_derive", + "flagset", "pem-rfc7468", "zeroize", ] +[[package]] +name = "der_derive" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "deranged" version = "0.3.10" @@ -378,6 +391,12 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" +[[package]] +name = "flagset" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d52a7e408202050813e6f1d9addadcaafef3dca7530c7ddfb005d4081cce6779" + [[package]] name = "generic-array" version = "0.14.7" @@ -510,12 +529,27 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +dependencies = [ + "spin", +] + [[package]] name = "libc" version = "0.2.150" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +[[package]] +name = "libm" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" + [[package]] name = "linux-raw-sys" version = "0.4.12" @@ -550,6 +584,54 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ce46fe64a9d73be07dcbe690a38ce1b293be448fd8ce1e6c1b8062c9f72c6a" +[[package]] +name = "num-bigint-dig" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc84195820f291c7697304f3cbdadd1cb7199c0efc917ff5eafd71225c136151" +dependencies = [ + "byteorder", + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand", + "smallvec", + "zeroize", +] + +[[package]] +name = "num-integer" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9" +dependencies = [ + "autocfg", + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +dependencies = [ + "autocfg", + "libm", +] + [[package]] name = "oak_attestation_verification" version = "0.1.0" @@ -564,10 +646,13 @@ dependencies = [ "oak_proto_rust", "oak_sev_guest", "p256", + "p384", + "rsa", "serde", "serde_json", "sha2", "time", + "x509-cert", "zerocopy", ] @@ -629,6 +714,18 @@ dependencies = [ "sha2", ] +[[package]] +name = "p384" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70786f51bcc69f6a4c0360e063a4cac5419ef7c5cd5b3c99ad70f3be5ba79209" +dependencies = [ + "ecdsa", + "elliptic-curve", + "primeorder", + "sha2", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -648,6 +745,17 @@ dependencies = [ "indexmap", ] +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -664,6 +772,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +[[package]] +name = "ppv-lite86" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" + [[package]] name = "prettyplease" version = "0.2.15" @@ -755,6 +869,26 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + [[package]] name = "rand_core" version = "0.6.4" @@ -812,6 +946,26 @@ dependencies = [ "subtle", ] +[[package]] +name = "rsa" +version = "0.9.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e5124fcb30e76a7e79bfee683a2746db83784b86289f6251b54b7950a0dfc" +dependencies = [ + "const-oid", + "digest", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "runner" version = "0.1.0" @@ -926,6 +1080,12 @@ dependencies = [ "rand_core", ] +[[package]] +name = "smallvec" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" + [[package]] name = "snafu" version = "0.8.0" @@ -947,6 +1107,12 @@ dependencies = [ "syn", ] +[[package]] +name = "spin" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" + [[package]] name = "spinning_top" version = "0.3.0" @@ -1279,6 +1445,17 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" +[[package]] +name = "x509-cert" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" +dependencies = [ + "const-oid", + "der", + "spki", +] + [[package]] name = "x86_64" version = "0.14.11" From 704f81ed6310dfed448670b9fcdeb7ab6cfc78d1 Mon Sep 17 00:00:00 2001 From: Brett McLarnon Date: Wed, 24 Jan 2024 18:44:03 -0800 Subject: [PATCH 30/42] Fix oci_runtime_bundle dependence on build environment. (#4707) This fixes 3 ways that the OCI runtime bundle tarball could vary when built on different systems: * The uid and gid mappings contained the host user's uid. * umoci chooses different mount options for /etc/resolv.conf when running as root. * The timestamp used for the tarball depended on the host's timezone. --- FORCE_CI | 2 +- bazel/private/oci_runtime_bundle.sh.tpl | 14 +++++++++++--- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/FORCE_CI b/FORCE_CI index f64f5d8d85a..9902f17848a 100644 --- a/FORCE_CI +++ b/FORCE_CI @@ -1 +1 @@ -27 +28 diff --git a/bazel/private/oci_runtime_bundle.sh.tpl b/bazel/private/oci_runtime_bundle.sh.tpl index cf54ea2479a..e122f54faed 100644 --- a/bazel/private/oci_runtime_bundle.sh.tpl +++ b/bazel/private/oci_runtime_bundle.sh.tpl @@ -32,10 +32,18 @@ readonly BUNDLE_DIR=$(mktemp -d) trap 'rm -rf -- "${BUNDLE_DIR}"' EXIT "${UMOCI}" unpack --rootless --image "${IMAGE_DIR}" "${BUNDLE_DIR}" -# Sort mount options, which umoci emits in a non-deterministic order. -"${YQ}" -i -o=json 'with(.mounts ; .[] | select(.options != null) | .options |= sort)' "${BUNDLE_DIR}/config.json" +# Patch the OCI runtime bundle config to remove non-determinism: +# * uid and gid mappings depend on the ids of the user running umoci. +# * /etc/resolv.conf mount options change if run as root. +# * mount options are emitted in a non-deterministic order. +"${YQ}" -i -o=json ' + with(.linux.uidMappings ; .[] | .hostID = 0) | + with(.linux.gidMappings ; .[] | .hostID = 0) | + with(.mounts ; .[] | select(.destination == "/etc/resolv.conf") | .options = ["rbind", "ro"]) | + with(.mounts ; .[] | select(.options != null) | .options |= sort) + ' "${BUNDLE_DIR}/config.json" # Pack the runtime bundle into a (reproducible) tar, excluding unnecessary files # added by umoci. -tar --create --sort=name --mtime="2000-01-01" --owner=0 --group=0 --numeric-owner \ +tar --create --sort=name --mtime="2000-01-01Z" --owner=0 --group=0 --numeric-owner \ --file="$2" --directory="${BUNDLE_DIR}" --exclude=./umoci.json --exclude='./sha256_*.mtree' . From 80756a7f24ba4eef93f7b0884aa769f53205c9e9 Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Thu, 25 Jan 2024 09:27:46 +0000 Subject: [PATCH 31/42] Remove OakLogger generics (#4713) Ref #4712 --- oak_functions_sdk/tests/integration_test.rs | 20 +++--- oak_functions_service/src/instance.rs | 6 +- oak_functions_service/src/logger.rs | 5 +- oak_functions_service/src/lookup.rs | 39 +++++------ oak_functions_service/src/wasm/api.rs | 10 +-- oak_functions_service/src/wasm/mod.rs | 71 +++++++++------------ oak_functions_service/src/wasm/tests.rs | 6 +- 7 files changed, 71 insertions(+), 86 deletions(-) diff --git a/oak_functions_sdk/tests/integration_test.rs b/oak_functions_sdk/tests/integration_test.rs index 3656807d4a4..847c783b5c9 100644 --- a/oak_functions_sdk/tests/integration_test.rs +++ b/oak_functions_sdk/tests/integration_test.rs @@ -50,7 +50,7 @@ lazy_static! { #[tokio::test] async fn test_read_write() { - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, @@ -73,7 +73,7 @@ async fn test_read_write() { #[tokio::test] async fn test_double_read() { - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, @@ -96,7 +96,7 @@ async fn test_double_read() { #[tokio::test] async fn test_double_write() { - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, @@ -119,7 +119,7 @@ async fn test_double_write() { #[tokio::test] async fn test_write_log() { - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, @@ -147,7 +147,7 @@ async fn test_storage_get_item() { b"StorageGetResponse".to_vec().into(), )]); - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(entries, logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, @@ -173,7 +173,7 @@ async fn test_storage_get_item_not_found() { // empty lookup data, no key will be found let entries = HashMap::new(); - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(entries, logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, @@ -200,7 +200,7 @@ async fn test_storage_get_item_huge_key() { let bytes: Vec = vec![42u8; 1 << 20]; let entries = HashMap::from_iter([(bytes.clone().into(), bytes.clone().into())]); - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(entries, logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, @@ -224,7 +224,7 @@ async fn test_storage_get_item_huge_key() { #[tokio::test] async fn test_echo() { - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let message_to_echo = "ECHO"; let lookup_data_manager = @@ -254,7 +254,7 @@ async fn test_blackhole() { // Keep in sync with // `workspace/oak_functions/sdk/oak_functions/tests/testing_module/src/lib.rs`. - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let message_to_blackhole = "BLACKHOLE"; let lookup_data_manager = @@ -285,7 +285,7 @@ async fn test_huge_response() { // Keep in sync with // `workspace/oak_functions/sdk/oak_functions/tests/testing_module/src/lib.rs`. - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(Data::default(), logger.clone())); diff --git a/oak_functions_service/src/instance.rs b/oak_functions_service/src/instance.rs index e3c5f49c120..a8a17867c1c 100644 --- a/oak_functions_service/src/instance.rs +++ b/oak_functions_service/src/instance.rs @@ -32,8 +32,8 @@ use crate::{ }; pub struct OakFunctionsInstance { - lookup_data_manager: Arc>, - wasm_handler: wasm::WasmHandler, + lookup_data_manager: Arc, + wasm_handler: wasm::WasmHandler, } impl OakFunctionsInstance { @@ -43,7 +43,7 @@ impl OakFunctionsInstance { observer: Option>, ) -> Result { let lookup_data_manager = - Arc::new(LookupDataManager::new_empty(StandaloneLogger::default())); + Arc::new(LookupDataManager::new_empty(Arc::new(StandaloneLogger))); let wasm_handler = wasm::new_wasm_handler(&request.wasm_module, lookup_data_manager.clone(), observer) .map_err(|err| { diff --git a/oak_functions_service/src/logger.rs b/oak_functions_service/src/logger.rs index cc7980965dd..ea5edab90fa 100644 --- a/oak_functions_service/src/logger.rs +++ b/oak_functions_service/src/logger.rs @@ -16,7 +16,7 @@ pub use log::Level; -pub trait OakLogger: Send + Sync + Clone { +pub trait OakLogger: Send + Sync { /// Logs the message, which might contain sensitive information, at the specified `Level`. /// /// Only insecure debug-only implementations may provide a non-empty implementation. Production @@ -33,8 +33,7 @@ pub trait OakLogger: Send + Sync + Clone { /// Temporary OakLogger implementation using the `log` crate. /// /// TODO(#2783): Replace with redesigned logger implementation. -#[derive(Clone, Default)] -pub struct StandaloneLogger {} +pub struct StandaloneLogger; // TODO(#2783): Implement a logger that differentiates between public and sensitive loges. impl OakLogger for StandaloneLogger { diff --git a/oak_functions_service/src/lookup.rs b/oak_functions_service/src/lookup.rs index 014b0c59149..9d7b95afaf6 100644 --- a/oak_functions_service/src/lookup.rs +++ b/oak_functions_service/src/lookup.rs @@ -83,20 +83,17 @@ impl DataBuilder { /// idiom `Arc>` we have `Spinlock>`. /// /// In the future we may replace both the mutex and the hash map with something like RCU. -pub struct LookupDataManager { +pub struct LookupDataManager { data: Spinlock>, // Behind a lock, because we have multiple references to LookupDataManager and need to mutate // data builder. data_builder: Spinlock, - logger: L, + logger: Arc, } -impl LookupDataManager -where - L: OakLogger + Clone, -{ +impl LookupDataManager { /// Creates a new instance with empty backing data. - pub fn new_empty(logger: L) -> Self { + pub fn new_empty(logger: Arc) -> Self { Self { data: Spinlock::new(Arc::new(Data::new())), // Incrementally builds the backing data that will be used by new `LookupData` @@ -107,7 +104,7 @@ where } /// Creates an instance of LookupData populated with the given entries. - pub fn for_test(data: Data, logger: L) -> Self { + pub fn for_test(data: Data, logger: Arc) -> Self { let test_manager = Self::new_empty(logger); *test_manager.data.lock() = Arc::new(data); test_manager @@ -161,7 +158,7 @@ where } /// Creates a new `LookupData` instance with a reference to the current backing data. - pub fn create_lookup_data(&self) -> LookupData { + pub fn create_lookup_data(&self) -> LookupData { let keys; let data = { let data = self.data.lock().clone(); @@ -175,16 +172,13 @@ where /// Provides access to shared lookup data. #[derive(Clone)] -pub struct LookupData { +pub struct LookupData { data: Arc, - logger: L, + logger: Arc, } -impl LookupData -where - L: OakLogger + Clone, -{ - fn new(data: Arc, logger: L) -> Self { +impl LookupData { + fn new(data: Arc, logger: Arc) -> Self { Self { data, logger } } @@ -236,7 +230,8 @@ mod tests { use super::*; #[derive(Clone)] - struct TestLogger {} + struct TestLogger; + impl OakLogger for TestLogger { fn log_sensitive(&self, _level: Level, _message: &str) {} fn log_public(&self, _level: Level, _message: &str) {} @@ -246,7 +241,7 @@ mod tests { fn test_lookup_data_instance_consistency() { // Ensure that the data for a specific lookup data instance remains consistent even if the // data in the manager has been updated. - let manager = LookupDataManager::new_empty(TestLogger {}); + let manager = LookupDataManager::new_empty(Arc::new(TestLogger)); let lookup_data_0 = manager.create_lookup_data(); assert_eq!(lookup_data_0.len(), 0); @@ -268,7 +263,7 @@ mod tests { #[test] fn test_update_lookup_data_one_chunk() { - let manager = LookupDataManager::new_empty(TestLogger {}); + let manager = LookupDataManager::new_empty(Arc::new(TestLogger)); manager.extend_next_lookup_data(create_test_data(0, 2)); manager.finish_next_lookup_data(); let lookup_data = manager.create_lookup_data(); @@ -277,7 +272,7 @@ mod tests { #[test] fn test_update_lookup_data_two_chunks() { - let manager = LookupDataManager::new_empty(TestLogger {}); + let manager = LookupDataManager::new_empty(Arc::new(TestLogger)); let lookup_data_0 = manager.create_lookup_data(); manager.extend_next_lookup_data(create_test_data(0, 2)); @@ -294,7 +289,7 @@ mod tests { #[test] fn test_update_lookup_four_chunks() { - let manager = LookupDataManager::new_empty(TestLogger {}); + let manager = LookupDataManager::new_empty(Arc::new(TestLogger)); manager.extend_next_lookup_data(create_test_data(0, 2)); manager.extend_next_lookup_data(create_test_data(2, 3)); @@ -310,7 +305,7 @@ mod tests { #[test] fn test_update_lookup_data_abort_by_sender() { - let manager = LookupDataManager::new_empty(TestLogger {}); + let manager = LookupDataManager::new_empty(Arc::new(TestLogger)); let lookup_data_0 = manager.create_lookup_data(); manager.extend_next_lookup_data(create_test_data(0, 2)); diff --git a/oak_functions_service/src/wasm/api.rs b/oak_functions_service/src/wasm/api.rs index bf06cddd7df..d93f8ca8a4c 100644 --- a/oak_functions_service/src/wasm/api.rs +++ b/oak_functions_service/src/wasm/api.rs @@ -34,10 +34,10 @@ use crate::{ /// [`StdWasmApiImpl`] for each incoming gRPC request, with an immutable snapshot of the /// current lookup data. pub struct StdWasmApiFactory { - pub lookup_data_manager: Arc>, + pub lookup_data_manager: Arc, } -impl WasmApiFactory for StdWasmApiFactory { +impl WasmApiFactory for StdWasmApiFactory { fn create_wasm_api( &self, request: Vec, @@ -45,7 +45,7 @@ impl WasmApiFactory for StdWasmApiFactory { ) -> Box { Box::new(StdWasmApiImpl { lookup_data: self.lookup_data_manager.create_lookup_data(), - logger: StandaloneLogger::default(), + logger: Arc::new(StandaloneLogger), request, response, }) @@ -58,8 +58,8 @@ impl WasmApiFactory for StdWasmApiFactory { /// future. #[derive(Clone)] pub struct StdWasmApiImpl { - lookup_data: LookupData, - logger: StandaloneLogger, + lookup_data: LookupData, + logger: Arc, /// Current request, as received from the client. request: Vec, /// Current response, as received from the Wasm module. diff --git a/oak_functions_service/src/wasm/mod.rs b/oak_functions_service/src/wasm/mod.rs index 15928478462..2c1d8fdafaf 100644 --- a/oak_functions_service/src/wasm/mod.rs +++ b/oak_functions_service/src/wasm/mod.rs @@ -59,9 +59,9 @@ pub type AbiPointerOffset = u32; /// `UserState` holds the user request bytes and response bytes for a particular execution of an Oak /// Wasm module. The `UserState` also holds a reference to the logger and the enabled extensions. -pub struct UserState { +pub struct UserState { wasm_api_transport: Box>, - logger: L, + logger: Arc, } /// Stubs a Wasm imported function in the provided linker. @@ -73,7 +73,7 @@ macro_rules! stub_wasm_function { $linker.func_wrap( stringify!($function_mod), stringify!($function_name), - |caller: wasmi::Caller<'_, UserState>, $(_: $t),*| { + |caller: wasmi::Caller<'_, UserState>, $(_: $t),*| { caller .data() .log_error(concat!("called stubbed ", stringify!($function_mod), ".", stringify!($function_name))); @@ -86,14 +86,14 @@ macro_rules! stub_wasm_function { }; } -impl UserState -where - L: OakLogger, -{ +impl UserState { /// Stores the user request bytes, extensions, and logger. The response bytes are initialized /// with the empty response because every request needs to have a response and we fixed the /// empty response as the default response. - fn new(wasm_api_transport: Box>, logger: L) -> Self { + fn new( + wasm_api_transport: Box>, + logger: Arc, + ) -> Self { UserState { wasm_api_transport, logger, @@ -111,16 +111,13 @@ where /// the [`OakCaller`]) to provide `alloc` for allocating memory. The [`OakLinker`] checks that the /// Wasm module provides `alloc` and `main`, which every Oak Wasm module must provide, and defines /// the memory which the [`OakCaller`] uses. -struct OakLinker { - linker: wasmi::Linker>, +struct OakLinker { + linker: wasmi::Linker, } -impl OakLinker -where - L: OakLogger, -{ +impl OakLinker { fn new(engine: &wasmi::Engine) -> Self { - let mut linker: wasmi::Linker> = wasmi::Linker::new(engine); + let mut linker: wasmi::Linker = wasmi::Linker::new(engine); linker .func_wrap( @@ -129,7 +126,7 @@ where "invoke", // The types in the signatures correspond to the parameters from // oak_functions_abi/src/lib.rs. - |caller: wasmi::Caller<'_, UserState>, + |caller: wasmi::Caller<'_, UserState>, request_ptr: AbiPointer, request_len: AbiPointerOffset, response_ptr_ptr: AbiPointer, @@ -209,7 +206,7 @@ where /// memory is attached. fn instantiate( &self, - mut store: &mut Store>, + mut store: &mut Store, module: Arc, ) -> Result { let instance = self @@ -266,17 +263,14 @@ where /// Provides functionality for reading from the Wasm memory, as well as allocating and writing to /// the Wasm memory. The Wasm memory is defined by the [`OakLinker`]. [`OakCaller`] /// relies on `alloc`, which every Oak Wasm module must provide. -struct OakCaller<'a, L: OakLogger> { - caller: wasmi::Caller<'a, UserState>, +struct OakCaller<'a> { + caller: wasmi::Caller<'a, UserState>, alloc: wasmi::TypedFunc, memory: wasmi::Memory, } -impl<'a, L> OakCaller<'a, L> -where - L: OakLogger, -{ - fn new(caller: wasmi::Caller<'a, UserState>) -> Result { +impl<'a> OakCaller<'a> { + fn new(caller: wasmi::Caller<'a, UserState>) -> Result { // Get typed `alloc` to store. let ext = caller.get_export(ALLOC_FUNCTION_NAME).ok_or_else(|| { caller @@ -400,21 +394,21 @@ where }) } - fn data_mut(&mut self) -> &mut UserState { + fn data_mut(&mut self) -> &mut UserState { self.caller.data_mut() } - fn data(&mut self) -> &UserState { + fn data(&mut self) -> &UserState { self.caller.data() } } // A request handler with a Wasm module for handling multiple requests. -pub struct WasmHandler { +pub struct WasmHandler { wasm_module: Arc, - linker: OakLinker, - wasm_api_factory: Arc + Send + Sync>, - logger: L, + linker: OakLinker, + wasm_api_factory: Arc, + logger: Arc, #[cfg_attr(not(feature = "std"), allow(dead_code))] observer: Option>, } @@ -424,7 +418,7 @@ pub struct WasmHandler { /// An instance of [`WasmApiFactory`] is expected to live for the lifetime of the server, while /// each instance of the created [`WasmApi`] is expected to live for the lifetime of a single /// request. -pub trait WasmApiFactory { +pub trait WasmApiFactory { fn create_wasm_api( &self, request: Vec, @@ -439,14 +433,11 @@ pub trait WasmApi { fn transport(&mut self) -> Box>; } -impl WasmHandler -where - L: OakLogger, -{ +impl WasmHandler { pub fn create( wasm_module_bytes: &[u8], - wasm_api_factory: Arc + Send + Sync>, - logger: L, + wasm_api_factory: Arc, + logger: Arc, observer: Option>, ) -> anyhow::Result { let engine = wasmi::Engine::default(); @@ -535,10 +526,10 @@ fn from_status_code(result: Result<(), StatusCode>) -> Result>, + lookup_data_manager: Arc, observer: Option>, -) -> anyhow::Result> { - let logger = StandaloneLogger::default(); +) -> anyhow::Result { + let logger = Arc::new(StandaloneLogger); let wasm_api_factory = StdWasmApiFactory { lookup_data_manager, }; diff --git a/oak_functions_service/src/wasm/tests.rs b/oak_functions_service/src/wasm/tests.rs index 4d7ea5a83e0..56fafb9dc2a 100644 --- a/oak_functions_service/src/wasm/tests.rs +++ b/oak_functions_service/src/wasm/tests.rs @@ -179,13 +179,13 @@ fn bench_invoke(bencher: &mut Bencher) { struct TestState { instance: wasmi::Instance, - store: wasmi::Store>, - wasm_handler: WasmHandler, + store: wasmi::Store, + wasm_handler: WasmHandler, request: Vec, } fn create_test_state() -> TestState { - let logger = StandaloneLogger {}; + let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); let api_factory = Arc::new(StdWasmApiFactory { lookup_data_manager, From 5e72b25dce9f85dc8a2095cfc92064be96187a84 Mon Sep 17 00:00:00 2001 From: Ernesto Ocampo Date: Thu, 25 Jan 2024 12:54:34 +0000 Subject: [PATCH 32/42] Move SEV-SNP attestation report structure to own crate. (#4710) Motivation is to make it easier to use from other crates by reducing transitive dependencies. b/322156740 --- Cargo.lock | 36 +- Cargo.toml | 2 + oak_attestation_verification/Cargo.toml | 2 +- oak_attestation_verification/src/amd.rs | 2 +- oak_attestation_verification/src/verifier.rs | 17 +- .../configs/6.1.33/minimal.config | 4 +- oak_ml_transparency/runner/Cargo.lock | 82 +--- oak_restricted_kernel_bin/Cargo.lock | 11 + oak_sev_guest/Cargo.toml | 1 + oak_sev_guest/src/guest.rs | 305 +-------------- oak_sev_snp_attestation_report/Cargo.toml | 12 + oak_sev_snp_attestation_report/src/lib.rs | 349 ++++++++++++++++++ stage0/Cargo.toml | 1 + stage0/src/dice_attestation.rs | 5 +- stage0_bin/Cargo.lock | 14 +- stage0_dice/Cargo.toml | 2 +- stage0_dice/src/lib.rs | 9 +- testing/sev_snp_hello_world_kernel/Cargo.lock | 11 + 18 files changed, 453 insertions(+), 412 deletions(-) create mode 100644 oak_sev_snp_attestation_report/Cargo.toml create mode 100644 oak_sev_snp_attestation_report/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 62017b6331b..20c0159e09a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1933,7 +1933,7 @@ dependencies = [ "hex", "oak_dice", "oak_proto_rust", - "oak_sev_guest", + "oak_sev_snp_attestation_report", "p256", "p384", "prost", @@ -2598,6 +2598,7 @@ dependencies = [ "aes-gcm", "bitflags 2.4.1", "lock_api", + "oak_sev_snp_attestation_report", "snafu", "spinning_top 0.3.0", "static_assertions", @@ -2606,6 +2607,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "oak_sev_snp_attestation_report" +version = "0.0.0" +dependencies = [ + "bitflags 1.3.2", + "static_assertions", + "strum 0.24.1", + "zerocopy", +] + [[package]] name = "oak_simple_io" version = "0.1.0" @@ -2629,6 +2640,7 @@ dependencies = [ "oak_dice", "oak_linux_boot_params", "oak_sev_guest", + "oak_sev_snp_attestation_report", "oak_stage0_dice", "p256", "rand_core", @@ -2650,7 +2662,7 @@ dependencies = [ "hex", "hkdf", "oak_dice", - "oak_sev_guest", + "oak_sev_snp_attestation_report", "p256", "rand_core", "sha2", @@ -3781,6 +3793,9 @@ name = "strum" version = "0.24.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f" +dependencies = [ + "strum_macros 0.24.3", +] [[package]] name = "strum" @@ -3788,7 +3803,20 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" dependencies = [ - "strum_macros", + "strum_macros 0.25.3", +] + +[[package]] +name = "strum_macros" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 1.0.109", ] [[package]] @@ -4747,7 +4775,7 @@ dependencies = [ "serde", "serde_yaml", "strum 0.24.1", - "strum_macros", + "strum_macros 0.25.3", "tokio", "toml", "walkdir", diff --git a/Cargo.toml b/Cargo.toml index 4cbd0496bc3..71d7d09974c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -49,6 +49,7 @@ members = [ "oak_restricted_kernel_interface", "oak_restricted_kernel_sdk", "oak_sev_guest", + "oak_sev_snp_attestation_report", "oak_simple_io", "oak_tdx_guest", "oak_virtio", @@ -110,6 +111,7 @@ oak_restricted_kernel_sdk = { path = "./oak_restricted_kernel_sdk" } oak_restricted_kernel_sdk_proc_macro = { path = "./oak_restricted_kernel_sdk_proc_macro" } oak_restricted_kernel_interface = { path = "./oak_restricted_kernel_interface" } oak_sev_guest = { path = "./oak_sev_guest", default-features = false } +oak_sev_snp_attestation_report = { path = "./oak_sev_snp_attestation_report" } oak_stage0_dice = { path = "./stage0_dice" } oak_simple_io = { path = "./oak_simple_io" } oak_tdx_guest = { path = "./oak_tdx_guest" } diff --git a/oak_attestation_verification/Cargo.toml b/oak_attestation_verification/Cargo.toml index fc17f94e60a..e0a2a1196bf 100644 --- a/oak_attestation_verification/Cargo.toml +++ b/oak_attestation_verification/Cargo.toml @@ -21,7 +21,7 @@ getrandom = { version = "*", default-features = false, features = [ hex = { version = "*", default-features = false } oak_dice = { workspace = true } oak_proto_rust = { workspace = true } -oak_sev_guest = { workspace = true } +oak_sev_snp_attestation_report = { workspace = true } p256 = { version = "*", default-features = false, features = [ "alloc", "ecdsa-core", diff --git a/oak_attestation_verification/src/amd.rs b/oak_attestation_verification/src/amd.rs index 20606a36a4e..9526e0fbe8a 100644 --- a/oak_attestation_verification/src/amd.rs +++ b/oak_attestation_verification/src/amd.rs @@ -18,7 +18,7 @@ use alloc::string::String; -use oak_sev_guest::guest::{AttestationReport, TcbVersion}; +use oak_sev_snp_attestation_report::{AttestationReport, TcbVersion}; use p256::pkcs8::ObjectIdentifier; use rsa::{pss::Signature, signature::Verifier, RsaPublicKey}; use sha2::Sha384; diff --git a/oak_attestation_verification/src/verifier.rs b/oak_attestation_verification/src/verifier.rs index e4929bf6df9..683640acdd8 100644 --- a/oak_attestation_verification/src/verifier.rs +++ b/oak_attestation_verification/src/verifier.rs @@ -42,7 +42,7 @@ use oak_proto_rust::oak::{ }, RawDigest, }; -use oak_sev_guest::guest::{AttestationReport, PolicyFlags}; +use oak_sev_snp_attestation_report::AttestationReport; use x509_cert::{ der::{Decode, DecodePem}, Certificate, @@ -343,17 +343,12 @@ fn verify_amd_sev_attestation_report( // We demand that the attestation report is signed by the VCEK public key. verify_attestation_report_signature(vcek, parsed)?; - let data = &parsed.data; - // Verify that we are not in debug mode. - if !reference_values.allow_debug { - let policy_flags = data - .policy - .get_flags() - .ok_or_else(|| anyhow::anyhow!("failed to parse flags"))?; - if policy_flags.bits() & PolicyFlags::DEBUG.bits() != 0 { - anyhow::bail!("debug mode not allowed"); - } + let has_debug: bool = parsed + .has_debug_flag() + .map_err(|error| anyhow::anyhow!(error))?; + if !reference_values.allow_debug && has_debug { + anyhow::bail!("debug mode not allowed"); } Ok(()) diff --git a/oak_containers_kernel/configs/6.1.33/minimal.config b/oak_containers_kernel/configs/6.1.33/minimal.config index 60036b97be5..444ae2ca70f 100644 --- a/oak_containers_kernel/configs/6.1.33/minimal.config +++ b/oak_containers_kernel/configs/6.1.33/minimal.config @@ -2,9 +2,9 @@ # Automatically generated file; DO NOT EDIT. # Linux/x86 6.1.33 Kernel Configuration # -CONFIG_CC_VERSION_TEXT="gcc (GCC) 12.3.0" +CONFIG_CC_VERSION_TEXT="gcc (GCC) 13.2.0" CONFIG_CC_IS_GCC=y -CONFIG_GCC_VERSION=120300 +CONFIG_GCC_VERSION=130200 CONFIG_CLANG_VERSION=0 CONFIG_AS_IS_GNU=y CONFIG_AS_VERSION=24000 diff --git a/oak_ml_transparency/runner/Cargo.lock b/oak_ml_transparency/runner/Cargo.lock index e77dfb41ffe..bbbbcdcacbd 100644 --- a/oak_ml_transparency/runner/Cargo.lock +++ b/oak_ml_transparency/runner/Cargo.lock @@ -89,12 +89,6 @@ version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" -[[package]] -name = "bit_field" -version = "0.10.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" - [[package]] name = "bitflags" version = "1.3.2" @@ -556,16 +550,6 @@ version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4cd1a83af159aa67994778be9070f0ae1bd732942279cabb14f86f986a21456" -[[package]] -name = "lock_api" -version = "0.4.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -dependencies = [ - "autocfg", - "scopeguard", -] - [[package]] name = "log" version = "0.4.20" @@ -644,7 +628,7 @@ dependencies = [ "hex", "oak_dice", "oak_proto_rust", - "oak_sev_guest", + "oak_sev_snp_attestation_report", "p256", "p384", "rsa", @@ -683,16 +667,12 @@ dependencies = [ ] [[package]] -name = "oak_sev_guest" -version = "0.1.0" +name = "oak_sev_snp_attestation_report" +version = "0.0.0" dependencies = [ - "bitflags 2.4.1", - "lock_api", - "snafu", - "spinning_top", + "bitflags 1.3.2", "static_assertions", "strum", - "x86_64", "zerocopy", ] @@ -1007,12 +987,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" -[[package]] -name = "scopeguard" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" - [[package]] name = "sec1" version = "0.7.3" @@ -1086,42 +1060,12 @@ version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6ecd384b10a64542d77071bd64bd7b231f4ed5940fba55e98c3de13824cf3d7" -[[package]] -name = "snafu" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d342c51730e54029130d7dc9fd735d28c4cd360f1368c01981d4f03ff207f096" -dependencies = [ - "snafu-derive", -] - -[[package]] -name = "snafu-derive" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "080c44971436b1af15d6f61ddd8b543995cf63ab8e677d46b00cc06f4ef267a0" -dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", -] - [[package]] name = "spin" version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" -[[package]] -name = "spinning_top" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d96d2d1d716fb500937168cc09353ffdc7a012be8475ac7308e1bdf0e3923300" -dependencies = [ - "lock_api", -] - [[package]] name = "spki" version = "0.7.3" @@ -1258,12 +1202,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "volatile" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793" - [[package]] name = "wasi" version = "0.11.0+wasi-snapshot-preview1" @@ -1456,18 +1394,6 @@ dependencies = [ "spki", ] -[[package]] -name = "x86_64" -version = "0.14.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b835097a84e4457323331ec5d6eb23d096066cbfb215d54096dcb4b2e85f500" -dependencies = [ - "bit_field", - "bitflags 2.4.1", - "rustversion", - "volatile", -] - [[package]] name = "zerocopy" version = "0.7.31" diff --git a/oak_restricted_kernel_bin/Cargo.lock b/oak_restricted_kernel_bin/Cargo.lock index ebfd672e724..9abeea27e98 100644 --- a/oak_restricted_kernel_bin/Cargo.lock +++ b/oak_restricted_kernel_bin/Cargo.lock @@ -801,6 +801,7 @@ dependencies = [ "aes-gcm", "bitflags 2.4.1", "lock_api", + "oak_sev_snp_attestation_report", "snafu", "spinning_top 0.3.0", "static_assertions", @@ -809,6 +810,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "oak_sev_snp_attestation_report" +version = "0.0.0" +dependencies = [ + "bitflags 1.3.2", + "static_assertions", + "strum", + "zerocopy", +] + [[package]] name = "oak_simple_io" version = "0.1.0" diff --git a/oak_sev_guest/Cargo.toml b/oak_sev_guest/Cargo.toml index 40ec094368a..d23694cf4fc 100644 --- a/oak_sev_guest/Cargo.toml +++ b/oak_sev_guest/Cargo.toml @@ -15,6 +15,7 @@ aes-gcm = { version = "*", optional = true, default-features = false, features = ] } bitflags = "*" lock_api = "*" +oak_sev_snp_attestation_report = { workspace = true } spinning_top = "*" static_assertions = "*" snafu = { version = "*", default-features = false } diff --git a/oak_sev_guest/src/guest.rs b/oak_sev_guest/src/guest.rs index 15b440d7d0b..bf4c57a11d6 100644 --- a/oak_sev_guest/src/guest.rs +++ b/oak_sev_guest/src/guest.rs @@ -22,6 +22,7 @@ use core::mem::size_of; use bitflags::bitflags; +use oak_sev_snp_attestation_report::AttestationReport; use strum::{EnumIter, FromRepr}; use zerocopy::{AsBytes, FromBytes, FromZeroes}; @@ -512,272 +513,6 @@ impl AttestationResponse { } } -/// A signed attestation report. -/// -/// See Table 22 in . -#[repr(C)] -#[derive(Debug, AsBytes, FromZeroes, FromBytes)] -pub struct AttestationReport { - /// The data contained in the report. - pub data: AttestationReportData, - /// The signature over the data. - pub signature: EcdsaSignature, -} - -static_assertions::assert_eq_size!(AttestationReport, [u8; 1184]); - -impl AttestationReport { - /// Checks that the report data is valid and the signature has the expected format. - pub fn validate(&self) -> Result<(), &'static str> { - self.data.validate()?; - self.signature.validate_format() - } -} - -/// The data contained in an attestation report. -/// -/// See Table 22 in . -#[repr(C)] -#[derive(Debug, AsBytes, FromZeroes, FromBytes)] -pub struct AttestationReportData { - /// The version of the attestation report format. - /// - /// This implementation is based on version 2. - pub version: u32, - /// The guest security version number. - pub guest_svn: u32, - /// The policy required by the guest VM to be launched. - pub policy: GuestPolicy, - /// The family ID provided at launch. - pub family_id: [u8; 16], - /// The image ID provided at launch. - pub image_id: [u8; 16], - /// The VMPL value that was passed in the request. - pub vmpl: u32, - /// The algorithm used to sign the report. - /// - /// Use `AttestationReportData::get_signature_algo` to try to convert this to a - /// `SigningAlgorithm` enum. - pub signature_algo: u32, - /// The current version of each of the components in the Trusted Computing Base (TCB). This - /// could be different from the committed value during provisional execution when firmware - /// is being updated. - pub current_tcb: TcbVersion, - /// Information about the platform. - /// - /// Use `AttestationReportData::get_platform_info` to try to convert this to a `PlatformInfo` - /// biflag representation. - pub platform_info: u64, - /// The least significant bit indicates Whether the digest of the author key is included in the - /// report, all other bits are reserved and must be zero. - /// - /// Use `AttestationReportData::get_author_key_en` to try to convert this to an `AuthorKey` - /// enum. - pub author_key_en: u64, - /// The custom data provided in the attestation request. - pub report_data: [u8; 64], - /// The measurement of the VM memory calculated at launch. - pub measurement: [u8; 48], - /// Custom data provided by the hypervisor at launch. - pub host_data: [u8; 32], - /// The SHA-384 digest of the ID public key used to sign the ID block that was provided in - /// SNP_LAUNCH_FINISH. - pub id_key_digest: [u8; 48], - /// The SHA-384 digest of the author public key used to certify the ID key, if the least - /// significant bit of `author_key_en` is 1, or all zeroes otherwise. - pub author_key_digest: [u8; 48], - /// The report ID of this guest. - pub report_id: [u8; 32], - /// The report ID of this guest's migration agent. - pub report_id_ma: [u8; 32], - /// The reported TCB version that was used to generate the versioned chip endorsement key - /// (VCEK) used to sign this report. - pub reported_tcb: TcbVersion, - /// Reserved, must be zero. - _reserved_0: [u8; 24], - /// Identifier unique to the chip, unless the ID has been masked in configuration in which case - /// it is all zeroes. - pub chip_id: [u8; 64], - /// The committed TCB version. - pub committed_tcb: TcbVersion, - /// The build number of the current secure firmware ABI version. - pub current_build: u8, - /// The minor number of the current secure firmware ABI version. - pub current_minor: u8, - /// The major number of the current secure firmware ABI version. - pub current_major: u8, - /// Reserved, must be zero. - _reserved_1: u8, - /// The build number of the committed secure firmware ABI version. - pub committed_build: u8, - /// The minor number of the committed secure firmware ABI version. - pub committed_minor: u8, - /// The major number of the committed secure firmware ABI version. - pub committed_major: u8, - /// Reserved, must be zero. - _reserved_2: u8, - /// The value of the current TCB version when the guest was launched or imported. - pub launch_tcb: TcbVersion, - /// Reserved, must be zero. - _reserved_3: [u8; 168], -} - -static_assertions::assert_eq_size!(AttestationReportData, [u8; 672]); - -impl AttestationReportData { - /// Gets the platform info field as a `PlatformInfo` representation if possible. - pub fn get_platform_info(&self) -> Option { - PlatformInfo::from_bits(self.platform_info) - } - - /// Gets the author key enabled field as an `AuthorKey` enum if possible. - pub fn get_author_key_en(&self) -> Option { - AuthorKey::from_repr(self.author_key_en) - } - - /// Gets the signing algorithm field as a `SigningAlgorithm` enum if possible. - pub fn get_signature_algo(&self) -> Option { - SigningAlgorithm::from_repr(self.signature_algo) - } - - /// Checks that fields with specific expected values or ranges are valid and the reserved bytes - /// are all zero. - pub fn validate(&self) -> Result<(), &'static str> { - self.policy.validate()?; - self.current_tcb.validate()?; - self.reported_tcb.validate()?; - self.committed_tcb.validate()?; - if self._reserved_0.iter().any(|&value| value != 0) { - return Err("nonzero value in _reserved_0"); - } - if self._reserved_1 != 0 { - return Err("nonzero value in _reserved_1"); - } - if self._reserved_2 != 0 { - return Err("nonzero value in _reserved_2"); - } - if self._reserved_3.iter().any(|&value| value != 0) { - return Err("nonzero value in _reserved_3"); - } - if self.signature_algo != SigningAlgorithm::EcdsaP384Sha384 as u32 { - return Err("invalid signature algorithm"); - } - if self.get_platform_info().is_none() { - return Err("invalid platform info"); - } - if self.get_author_key_en().is_none() { - return Err("invalid value for author_key_en"); - } - Ok(()) - } -} - -bitflags! { - /// Information on the platform configuration. - #[derive(Default)] - pub struct PlatformInfo: u64 { - /// Indicates that simulatneous multi-threading (SMT) is enabled. - const SMT_EN = (1 << 0); - /// Indicates that transparent secure memory encryption (TSME) is enabled. - const TSME_EN = (1 << 1); - } -} - -/// The required policy for a guest to run. -/// -/// See Table 9 in . -#[repr(C)] -#[derive(Debug, AsBytes, FromZeroes, FromBytes)] -pub struct GuestPolicy { - /// The minimum ABI minor version required to launch the guest. - pub abi_minor: u8, - /// The minimum ABI major version required to launch the guest. - pub abi_major: u8, - /// The allowed settings for the guest. - /// - /// Use `GuestPolicy::get_flags` to try to convert this to a `PolicyFlags` enum. - pub flags: u16, - /// Reserved, must be zero. - _reserved: u32, -} - -static_assertions::assert_eq_size!(GuestPolicy, u64); - -impl GuestPolicy { - /// Gets the flags field as a `PolicyFlags` representation if possible. - pub fn get_flags(&self) -> Option { - PolicyFlags::from_bits(self.flags) - } - - /// Checks that the flags are valid and the reserved bytes are all zero. - pub fn validate(&self) -> Result<(), &'static str> { - if self._reserved != 0 { - return Err("nonzero value in _reserved"); - } - if self.get_flags().is_none() { - return Err("invalid flags"); - } - Ok(()) - } -} - -/// The version of all the components in the Trusted Computing Base (TCB). -/// -/// See Table 3 in . -#[repr(C)] -#[derive(Debug, AsBytes, FromZeroes, FromBytes)] -pub struct TcbVersion { - /// The current security version number (SVN) of the secure processor (PSP) bootloader. - pub boot_loader: u8, - /// The current SVN of the PSP operating system. - pub tee: u8, - /// Reserved, must be zero. - _reserved: [u8; 4], - /// The current SVN of the SNP firmware. - pub snp: u8, - /// The lowest current patch level of all the CPU cores. - pub microcode: u8, -} - -static_assertions::assert_eq_size!(TcbVersion, u64); - -impl TcbVersion { - /// Checks that the reserved bytes are all zero. - pub fn validate(&self) -> Result<(), &'static str> { - if self._reserved.iter().any(|&value| value != 0) { - return Err("nonzero value in _reserved"); - } - Ok(()) - } -} - -bitflags! { - /// Flags indicating allowed policy options. - #[derive(Default)] - pub struct PolicyFlags: u16 { - /// Simulatneous multi-threading (SMT) is allowed. - const SMT = (1 << 0); - /// Reserved, must always be 1. - const RESERVED = (1 << 1); - /// The guest can be associated with a migration agent. - const MIGRATE_MA = (1 << 2); - /// Debugging the guest is allowed. - const DEBUG = (1 << 3); - /// The guest can only be activated on a single socket. - const SINGLE_SOCKET = (1 << 4); - } -} - -/// Whether the author key digest is included in the report. -#[derive(Debug, FromRepr)] -#[repr(u64)] -pub enum AuthorKey { - /// The author key digest is not present. - No = 0, - /// The author key digest is present. - Yes = 1, -} - /// The status of the report response. #[derive(Debug, FromRepr, PartialEq)] #[repr(u32)] @@ -788,32 +523,6 @@ pub enum ReportStatus { InvalidParams = 0x16, } -/// An ECDSA signature. -/// -/// See Table 119 in . -#[repr(C)] -#[derive(Debug, AsBytes, FromZeroes, FromBytes)] -pub struct EcdsaSignature { - /// The R component of this signature. The value is zero-extended and little-endian encoded. - pub r: [u8; 72], - /// The S component of this signature. The value is zero-extended and little-endian encoded. - pub s: [u8; 72], - /// Reserved, must be zero. - _reserved: [u8; 368], -} - -static_assertions::assert_eq_size!(EcdsaSignature, [u8; 512]); - -impl EcdsaSignature { - /// Checks that the reserved bytes are all zero. - pub fn validate_format(&self) -> Result<(), &'static str> { - if self._reserved.iter().any(|&value| value != 0) { - return Err("nonzero value in _reserved"); - } - Ok(()) - } -} - /// An ECDSA public key. /// /// See Table 120 in . @@ -832,18 +541,6 @@ pub struct EcdsaPublicKey { static_assertions::assert_eq_size!(EcdsaPublicKey, [u8; 1028]); -/// The signing algorithm used for the report signature. -/// -/// See Table 117 in . -#[derive(Debug, FromRepr)] -#[repr(u32)] -pub enum SigningAlgorithm { - /// Invalid. - Invalid = 0, - /// ECDSA using curve P-384 with SHA-384. - EcdsaP384Sha384 = 1, -} - /// The elliptic curve used. /// /// See Table 118 in . diff --git a/oak_sev_snp_attestation_report/Cargo.toml b/oak_sev_snp_attestation_report/Cargo.toml new file mode 100644 index 00000000000..aca7d3e3632 --- /dev/null +++ b/oak_sev_snp_attestation_report/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "oak_sev_snp_attestation_report" +version = "0.0.0" +authors = ["Ernesto Ocampo "] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +bitflags = "*" +static_assertions = "*" +strum = { version = "*", default-features = false, features = ["derive"] } +zerocopy = { version = "*", features = ["derive"] } diff --git a/oak_sev_snp_attestation_report/src/lib.rs b/oak_sev_snp_attestation_report/src/lib.rs new file mode 100644 index 00000000000..62940857fe8 --- /dev/null +++ b/oak_sev_snp_attestation_report/src/lib.rs @@ -0,0 +1,349 @@ +// Copyright 2024 The Project Oak Authors +// +// Licensed 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. +// + +//! AMD SEV-SNP data structures for attestation reports. + +// TODO(#3703): Remove when fixed. +#![allow(clippy::extra_unused_type_parameters)] +#![no_std] + +use bitflags::bitflags; +use strum::FromRepr; +use zerocopy::{AsBytes, FromBytes, FromZeroes}; + +/// A signed attestation report. +/// +/// See Table 22 in . +#[repr(C)] +#[derive(Debug, AsBytes, FromZeroes, FromBytes)] +pub struct AttestationReport { + /// The data contained in the report. + pub data: AttestationReportData, + /// The signature over the data. + pub signature: EcdsaSignature, +} +static_assertions::assert_eq_size!(AttestationReport, [u8; 1184]); + +impl AttestationReport { + /// Creates a new AttestationReport with all zeros and the provided value on the + /// data.report_data field. + pub fn from_report_data(report_data: [u8; REPORT_DATA_SIZE]) -> Self { + let mut result = AttestationReport::new_zeroed(); + result.data.report_data = report_data; + result + } + + /// Checks that the report data is valid and the signature has the expected format. + pub fn validate(&self) -> Result<(), &'static str> { + self.data.validate()?; + self.signature.validate_format() + } + + pub fn has_debug_flag(&self) -> Result { + let policy_flags: PolicyFlags = self + .data + .policy + .get_flags() + .ok_or("Failed to parse flags")?; + + Ok(policy_flags.bits() & PolicyFlags::DEBUG.bits() != 0) + } + + /// Gets the signing algorithm field as a `SigningAlgorithm` enum if possible. + pub fn get_signature_algo(&self) -> Option { + SigningAlgorithm::from_repr(self.data.signature_algo) + } +} + +/// The number of bytes of custom data that can be included in the attestation report. +/// +/// See Table 22 in . +pub const REPORT_DATA_SIZE: usize = 64; + +/// The data contained in an attestation report. +/// +/// See Table 22 in . +#[repr(C)] +#[derive(Debug, AsBytes, FromZeroes, FromBytes)] +pub struct AttestationReportData { + /// The version of the attestation report format. + /// + /// This implementation is based on version 2. + pub version: u32, + /// The guest security version number. + pub guest_svn: u32, + /// The policy required by the guest VM to be launched. + pub policy: GuestPolicy, + /// The family ID provided at launch. + pub family_id: [u8; 16], + /// The image ID provided at launch. + pub image_id: [u8; 16], + /// The VMPL value that was passed in the request. + pub vmpl: u32, + /// The algorithm used to sign the report. + /// + /// Use `AttestationReportData::get_signature_algo` to try to convert this to a + /// `SigningAlgorithm` enum. + pub signature_algo: u32, + /// The current version of each of the components in the Trusted Computing Base (TCB). This + /// could be different from the committed value during provisional execution when firmware + /// is being updated. + pub current_tcb: TcbVersion, + /// Information about the platform. + /// + /// Use `AttestationReportData::get_platform_info` to try to convert this to a `PlatformInfo` + /// biflag representation. + pub platform_info: u64, + /// The least significant bit indicates Whether the digest of the author key is included in the + /// report, all other bits are reserved and must be zero. + /// + /// Use `AttestationReportData::get_author_key_en` to try to convert this to an `AuthorKey` + /// enum. + pub author_key_en: u64, + /// Guest-provided data. The custom data provided in the attestation request. + pub report_data: [u8; REPORT_DATA_SIZE], + /// The measurement of the VM memory calculated at launch. + pub measurement: [u8; 48], + /// Custom data provided by the hypervisor at launch. + pub host_data: [u8; 32], + /// The SHA-384 digest of the ID public key used to sign the ID block that was provided in + /// SNP_LAUNCH_FINISH. + pub id_key_digest: [u8; 48], + /// The SHA-384 digest of the author public key used to certify the ID key, if the least + /// significant bit of `author_key_en` is 1, or all zeroes otherwise. + pub author_key_digest: [u8; 48], + /// The report ID of this guest. + pub report_id: [u8; 32], + /// The report ID of this guest's migration agent. + pub report_id_ma: [u8; 32], + /// The reported TCB version that was used to generate the versioned chip endorsement key + /// (VCEK) used to sign this report. + pub reported_tcb: TcbVersion, + /// Reserved, must be zero. + _reserved_0: [u8; 24], + /// Identifier unique to the chip, unless the ID has been masked in configuration in which case + /// it is all zeroes. + pub chip_id: [u8; 64], + /// The committed TCB version. + pub committed_tcb: TcbVersion, + /// The build number of the current secure firmware ABI version. + pub current_build: u8, + /// The minor number of the current secure firmware ABI version. + pub current_minor: u8, + /// The major number of the current secure firmware ABI version. + pub current_major: u8, + /// Reserved, must be zero. + _reserved_1: u8, + /// The build number of the committed secure firmware ABI version. + pub committed_build: u8, + /// The minor number of the committed secure firmware ABI version. + pub committed_minor: u8, + /// The major number of the committed secure firmware ABI version. + pub committed_major: u8, + /// Reserved, must be zero. + _reserved_2: u8, + /// The value of the current TCB version when the guest was launched or imported. + pub launch_tcb: TcbVersion, + /// Reserved, must be zero. + _reserved_3: [u8; 168], +} + +static_assertions::assert_eq_size!(AttestationReportData, [u8; 672]); + +impl AttestationReportData { + /// Gets the platform info field as a `PlatformInfo` representation if possible. + pub fn get_platform_info(&self) -> Option { + PlatformInfo::from_bits(self.platform_info) + } + + /// Gets the author key enabled field as an `AuthorKey` enum if possible. + pub fn get_author_key_en(&self) -> Option { + AuthorKey::from_repr(self.author_key_en) + } + + /// Checks that fields with specific expected values or ranges are valid and the reserved bytes + /// are all zero. + pub fn validate(&self) -> Result<(), &'static str> { + self.policy.validate()?; + self.current_tcb.validate()?; + self.reported_tcb.validate()?; + self.committed_tcb.validate()?; + if self._reserved_0.iter().any(|&value| value != 0) { + return Err("nonzero value in _reserved_0"); + } + if self._reserved_1 != 0 { + return Err("nonzero value in _reserved_1"); + } + if self._reserved_2 != 0 { + return Err("nonzero value in _reserved_2"); + } + if self._reserved_3.iter().any(|&value| value != 0) { + return Err("nonzero value in _reserved_3"); + } + if self.signature_algo != SigningAlgorithm::EcdsaP384Sha384 as u32 { + return Err("invalid signature algorithm"); + } + if self.get_platform_info().is_none() { + return Err("invalid platform info"); + } + if self.get_author_key_en().is_none() { + return Err("invalid value for author_key_en"); + } + Ok(()) + } +} + +bitflags! { + /// Information on the platform configuration. + #[derive(Default)] + pub struct PlatformInfo: u64 { + /// Indicates that simulatneous multi-threading (SMT) is enabled. + const SMT_EN = (1 << 0); + /// Indicates that transparent secure memory encryption (TSME) is enabled. + const TSME_EN = (1 << 1); + } +} + +/// The signing algorithm used for the report signature. +/// +/// See Table 117 in . +#[derive(Debug, FromRepr)] +#[repr(u32)] +pub enum SigningAlgorithm { + /// Invalid. + Invalid = 0, + /// ECDSA using curve P-384 with SHA-384. + EcdsaP384Sha384 = 1, +} + +/// The required policy for a guest to run. +/// +/// See Table 9 in . +#[repr(C)] +#[derive(Debug, AsBytes, FromZeroes, FromBytes)] +pub struct GuestPolicy { + /// The minimum ABI minor version required to launch the guest. + pub abi_minor: u8, + /// The minimum ABI major version required to launch the guest. + pub abi_major: u8, + /// The allowed settings for the guest. + /// + /// Use `GuestPolicy::get_flags` to try to convert this to a `PolicyFlags` enum. + pub flags: u16, + /// Reserved, must be zero. + _reserved: u32, +} + +static_assertions::assert_eq_size!(GuestPolicy, u64); + +impl GuestPolicy { + /// Gets the flags field as a `PolicyFlags` representation if possible. + pub fn get_flags(&self) -> Option { + PolicyFlags::from_bits(self.flags) + } + + /// Checks that the flags are valid and the reserved bytes are all zero. + pub fn validate(&self) -> Result<(), &'static str> { + if self._reserved != 0 { + return Err("nonzero value in _reserved"); + } + if self.get_flags().is_none() { + return Err("invalid flags"); + } + Ok(()) + } +} + +/// The version of all the components in the Trusted Computing Base (TCB). +/// +/// See Table 3 in . +#[repr(C)] +#[derive(Debug, AsBytes, FromZeroes, FromBytes)] +pub struct TcbVersion { + /// The current security version number (SVN) of the secure processor (PSP) bootloader. + pub boot_loader: u8, + /// The current SVN of the PSP operating system. + pub tee: u8, + /// Reserved, must be zero. + _reserved: [u8; 4], + /// The current SVN of the SNP firmware. + pub snp: u8, + /// The lowest current patch level of all the CPU cores. + pub microcode: u8, +} + +static_assertions::assert_eq_size!(TcbVersion, u64); + +impl TcbVersion { + /// Checks that the reserved bytes are all zero. + pub fn validate(&self) -> Result<(), &'static str> { + if self._reserved.iter().any(|&value| value != 0) { + return Err("nonzero value in _reserved"); + } + Ok(()) + } +} + +bitflags! { + /// Flags indicating allowed policy options. + #[derive(Default)] + pub struct PolicyFlags: u16 { + /// Simulatneous multi-threading (SMT) is allowed. + const SMT = (1 << 0); + /// Reserved, must always be 1. + const RESERVED = (1 << 1); + /// The guest can be associated with a migration agent. + const MIGRATE_MA = (1 << 2); + /// Debugging the guest is allowed. + const DEBUG = (1 << 3); + /// The guest can only be activated on a single socket. + const SINGLE_SOCKET = (1 << 4); + } +} + +/// Whether the author key digest is included in the report. +#[derive(Debug, FromRepr)] +#[repr(u64)] +pub enum AuthorKey { + /// The author key digest is not present. + No = 0, + /// The author key digest is present. + Yes = 1, +} + +/// An ECDSA signature. +/// +/// See Table 119 in . +#[repr(C)] +#[derive(Debug, AsBytes, FromZeroes, FromBytes)] +pub struct EcdsaSignature { + /// The R component of this signature. The value is zero-extended and little-endian encoded. + pub r: [u8; 72], + /// The S component of this signature. The value is zero-extended and little-endian encoded. + pub s: [u8; 72], + /// Reserved, must be zero. + _reserved: [u8; 368], +} +static_assertions::assert_eq_size!(EcdsaSignature, [u8; 512]); + +impl EcdsaSignature { + /// Checks that the reserved bytes are all zero. + pub fn validate_format(&self) -> Result<(), &'static str> { + if self._reserved.iter().any(|&value| value != 0) { + return Err("nonzero value in _reserved"); + } + Ok(()) + } +} diff --git a/stage0/Cargo.toml b/stage0/Cargo.toml index 113c210dc16..3dd7a747249 100644 --- a/stage0/Cargo.toml +++ b/stage0/Cargo.toml @@ -18,6 +18,7 @@ oak_dice = { workspace = true } oak_stage0_dice = { workspace = true } oak_linux_boot_params = { path = "../linux_boot_params" } oak_sev_guest = { workspace = true, features = ["rust-crypto"] } +oak_sev_snp_attestation_report = { workspace = true } p256 = { version = "*", default-features = false, features = ["ecdsa"] } rand_core = { version = "*", default-features = false, features = [ "getrandom", diff --git a/stage0/src/dice_attestation.rs b/stage0/src/dice_attestation.rs index 363d964e041..0ef55572404 100644 --- a/stage0/src/dice_attestation.rs +++ b/stage0/src/dice_attestation.rs @@ -16,11 +16,12 @@ use oak_sev_guest::{ guest::{ - AttestationReport, AttestationRequest, AttestationResponse, GuestFieldFlags, KeyRequest, - KeyResponse, ReportStatus, + AttestationRequest, AttestationResponse, GuestFieldFlags, KeyRequest, KeyResponse, + ReportStatus, }, msr::SevStatus, }; +use oak_sev_snp_attestation_report::AttestationReport; use crate::sev::send_guest_message_request; diff --git a/stage0_bin/Cargo.lock b/stage0_bin/Cargo.lock index b7973006261..d562052d637 100644 --- a/stage0_bin/Cargo.lock +++ b/stage0_bin/Cargo.lock @@ -421,6 +421,7 @@ dependencies = [ "aes-gcm", "bitflags 2.4.0", "lock_api", + "oak_sev_snp_attestation_report", "snafu", "spinning_top", "static_assertions", @@ -429,6 +430,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "oak_sev_snp_attestation_report" +version = "0.0.0" +dependencies = [ + "bitflags 1.3.2", + "static_assertions", + "strum", + "zerocopy", +] + [[package]] name = "oak_stage0" version = "0.1.0" @@ -444,6 +455,7 @@ dependencies = [ "oak_dice", "oak_linux_boot_params", "oak_sev_guest", + "oak_sev_snp_attestation_report", "oak_stage0_dice", "p256", "rand_core", @@ -472,7 +484,7 @@ dependencies = [ "hex", "hkdf", "oak_dice", - "oak_sev_guest", + "oak_sev_snp_attestation_report", "p256", "rand_core", "sha2", diff --git a/stage0_dice/Cargo.toml b/stage0_dice/Cargo.toml index 65c37334aaa..289a9cba64b 100644 --- a/stage0_dice/Cargo.toml +++ b/stage0_dice/Cargo.toml @@ -10,7 +10,7 @@ coset = { version = "*", default-features = false } hkdf = { version = "*", default-features = false } hex = { version = "*", default-features = false, features = ["alloc"] } oak_dice = { workspace = true } -oak_sev_guest = { workspace = true, features = ["rust-crypto"] } +oak_sev_snp_attestation_report = { workspace = true } p256 = { version = "*", default-features = false, features = ["ecdsa"] } rand_core = { version = "*", default-features = false, features = [ "getrandom", diff --git a/stage0_dice/src/lib.rs b/stage0_dice/src/lib.rs index 9347a9c8239..e0a86883a7b 100644 --- a/stage0_dice/src/lib.rs +++ b/stage0_dice/src/lib.rs @@ -36,16 +36,13 @@ use oak_dice::{ }, evidence::{Stage0DiceData, TeePlatform, STAGE0_MAGIC}, }; -use oak_sev_guest::guest::AttestationReport; +use oak_sev_snp_attestation_report::{AttestationReport, REPORT_DATA_SIZE}; use p256::ecdsa::SigningKey; use sha2::{Digest, Sha256}; use zerocopy::{AsBytes, FromZeroes}; type DerivedKey = [u8; 32]; -// The number of bytes of custom data that can be included in the attestation report. -pub const REPORT_DATA_SIZE: usize = 64; - /// Measurements of various components in Stage1. #[derive(Default)] pub struct Measurements { @@ -216,9 +213,7 @@ pub fn generate_dice_data< pub fn mock_attestation_report( report_data: [u8; REPORT_DATA_SIZE], ) -> Result { - let mut report = AttestationReport::new_zeroed(); - report.data.report_data = report_data; - Ok(report) + Ok(AttestationReport::from_report_data(report_data)) } /// Returns a fixed key filled with zeros. diff --git a/testing/sev_snp_hello_world_kernel/Cargo.lock b/testing/sev_snp_hello_world_kernel/Cargo.lock index 6a5460e56af..de7b31ffbc0 100644 --- a/testing/sev_snp_hello_world_kernel/Cargo.lock +++ b/testing/sev_snp_hello_world_kernel/Cargo.lock @@ -94,6 +94,7 @@ version = "0.1.0" dependencies = [ "bitflags 2.4.0", "lock_api", + "oak_sev_snp_attestation_report", "snafu", "spinning_top", "static_assertions", @@ -102,6 +103,16 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "oak_sev_snp_attestation_report" +version = "0.0.0" +dependencies = [ + "bitflags 1.3.2", + "static_assertions", + "strum", + "zerocopy", +] + [[package]] name = "proc-macro2" version = "1.0.69" From e54ea4fcbc14ed22c3cffc72ea152910e95b149d Mon Sep 17 00:00:00 2001 From: Tiziano Santoro Date: Thu, 25 Jan 2024 14:33:01 +0000 Subject: [PATCH 33/42] Create more solid Wasm benchmarks (#4709) Ref #4706 --- Cargo.lock | 149 +++++++++++++++++- oak_functions_service/Cargo.toml | 6 + .../benches/wasm_benchmark.rs | 128 +++++++++++++++ oak_functions_service/src/wasm/tests.rs | 38 +---- 4 files changed, 281 insertions(+), 40 deletions(-) create mode 100644 oak_functions_service/benches/wasm_benchmark.rs diff --git a/Cargo.lock b/Cargo.lock index 20c0159e09a..549312331ba 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -117,6 +117,12 @@ dependencies = [ "libc", ] +[[package]] +name = "anes" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" + [[package]] name = "anstream" version = "0.6.7" @@ -463,6 +469,12 @@ dependencies = [ "thiserror", ] +[[package]] +name = "cast" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" + [[package]] name = "cc" version = "1.0.83" @@ -694,6 +706,42 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "criterion" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" +dependencies = [ + "anes", + "cast", + "ciborium", + "clap", + "criterion-plot", + "is-terminal", + "itertools 0.10.5", + "num-traits", + "once_cell", + "oorandom", + "plotters", + "rayon", + "regex", + "serde", + "serde_derive", + "serde_json", + "tinytemplate", + "walkdir", +] + +[[package]] +name = "criterion-plot" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" +dependencies = [ + "cast", + "itertools 0.10.5", +] + [[package]] name = "crossbeam-channel" version = "0.5.11" @@ -703,6 +751,25 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.19" @@ -2401,6 +2468,7 @@ dependencies = [ "anyhow", "byteorder", "bytes", + "criterion", "env_logger", "hashbrown 0.14.3", "log", @@ -2416,6 +2484,7 @@ dependencies = [ "oak_proto_rust", "oak_restricted_kernel_sdk", "prost", + "rand", "spinning_top 0.3.0", "tokio", "wasmi", @@ -2588,7 +2657,7 @@ name = "oak_restricted_kernel_sdk_proc_macro" version = "0.1.0" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.48", ] [[package]] @@ -2719,6 +2788,12 @@ version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +[[package]] +name = "oorandom" +version = "11.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" + [[package]] name = "opaque-debug" version = "0.3.0" @@ -2978,6 +3053,34 @@ version = "3.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "626dec3cac7cc0e1577a2ec3fc496277ec2baa084bebad95bb6fdbfae235f84c" +[[package]] +name = "plotters" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" +dependencies = [ + "num-traits", + "plotters-backend", + "plotters-svg", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "plotters-backend" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" + +[[package]] +name = "plotters-svg" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" +dependencies = [ + "plotters-backend", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -3214,6 +3317,26 @@ dependencies = [ "bitflags 1.3.2", ] +[[package]] +name = "rayon" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.4.1" @@ -3992,6 +4115,16 @@ dependencies = [ "time-core", ] +[[package]] +name = "tinytemplate" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" +dependencies = [ + "serde", + "serde_json", +] + [[package]] name = "tokio" version = "1.35.1" @@ -4429,9 +4562,9 @@ checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" [[package]] name = "wasmi" -version = "0.31.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acfc1e384a36ca532d070a315925887247f3c7e23567e23e0ac9b1c5d6b8bf76" +checksum = "77a8281d1d660cdf54c76a3efa9ddd0c270cada1383a995db3ccb43d166456c7" dependencies = [ "smallvec", "spin 0.9.8", @@ -4486,6 +4619,16 @@ dependencies = [ "xtask", ] +[[package]] +name = "web-sys" +version = "0.3.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "which" version = "4.4.2" diff --git a/oak_functions_service/Cargo.toml b/oak_functions_service/Cargo.toml index c5125d1eb17..b31ace12d6f 100644 --- a/oak_functions_service/Cargo.toml +++ b/oak_functions_service/Cargo.toml @@ -11,6 +11,10 @@ default = ["deny_sensitive_logging"] deny_sensitive_logging = [] std = ["anyhow/std", "wasmi/std"] +[[bench]] +name = "wasm_benchmark" +harness = false + [dependencies] anyhow = { version = "*", default-features = false } byteorder = { version = "*", default-features = false } @@ -34,9 +38,11 @@ wasmi = { version = "*", default-features = false } micro_rpc_build = { workspace = true } [dev-dependencies] +criterion = "*" env_logger = { version = "*", default-features = false } oak_attestation = { workspace = true } oak_functions_test_utils = { workspace = true } +rand = "*" tokio = { workspace = true, features = ["rt", "macros"] } oak_restricted_kernel_sdk = { workspace = true, features = [ "mock_attestation" diff --git a/oak_functions_service/benches/wasm_benchmark.rs b/oak_functions_service/benches/wasm_benchmark.rs new file mode 100644 index 00000000000..810a845894a --- /dev/null +++ b/oak_functions_service/benches/wasm_benchmark.rs @@ -0,0 +1,128 @@ +// +// Copyright 2024 The Project Oak Authors +// +// Licensed 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. +// + +use std::sync::Arc; + +use bytes::Bytes; +use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput}; +use hashbrown::HashMap; +use oak_functions_abi::Request; +use oak_functions_service::{ + logger::StandaloneLogger, + lookup::{Data, LookupDataManager}, + wasm::WasmHandler, +}; +use rand::{rngs::SmallRng, Rng, SeedableRng}; + +fn bench_invoke_echo(c: &mut Criterion) { + let test_state = create_test_state_with_wasm_module_name("echo"); + + // Measure throughput for different data sizes. + let mut group = c.benchmark_group("echo wasm"); + for size in [0, 1, 10, 100, 1000].iter() { + group.throughput(Throughput::Bytes(*size as u64)); + group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| { + let data = vec![88u8; size]; + b.iter(|| { + let response = test_state + .wasm_handler + .handle_invoke(Request { + body: data.to_vec(), + }) + .unwrap(); + assert_eq!(response.body, data.to_vec()); + }) + }); + } + group.finish(); +} + +fn bench_invoke_lookup(c: &mut Criterion) { + let test_state = create_test_state_with_wasm_module_name("key_value_lookup"); + + let max_data_size = 1000; + + let test_data = create_test_data(0, max_data_size); + test_state + .lookup_data_manager + .extend_next_lookup_data(test_data.clone()); + test_state.lookup_data_manager.finish_next_lookup_data(); + + c.bench_function("lookup wasm", |b| { + let mut rng = SmallRng::seed_from_u64(0); + let i = rng.gen_range(0..max_data_size); + let request = format!("key{i}").into_bytes(); + let expected_response = format!("value{i}").into_bytes(); + b.iter(|| { + let response = test_state + .wasm_handler + .handle_invoke(Request { + body: request.to_vec(), + }) + .unwrap(); + assert_eq!(response.body, expected_response); + }) + }); + + // Baseline for comparison. + c.bench_function("lookup native", |b| { + let mut rng = SmallRng::seed_from_u64(0); + let i = rng.gen_range(0..max_data_size); + let request: Bytes = format!("key{i}").into_bytes().into(); + let expected_response = format!("value{i}").into_bytes(); + b.iter(|| { + let response = test_data.get(&request).unwrap(); + assert_eq!(response.as_ref(), expected_response); + }) + }); +} + +fn create_test_data(start: i32, end: i32) -> Data { + HashMap::from_iter((start..end).map(|i| { + ( + format!("key{}", i).into_bytes().into(), + format!("value{}", i).into_bytes().into(), + ) + })) +} + +struct TestState { + wasm_handler: WasmHandler, + lookup_data_manager: Arc, +} + +fn create_test_state_with_wasm_module_name(wasm_module_name: &str) -> TestState { + let logger = Arc::new(StandaloneLogger); + let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); + let wasm_module_path = + oak_functions_test_utils::build_rust_crate_wasm(wasm_module_name).unwrap(); + let wasm_module_bytes = std::fs::read(wasm_module_path).unwrap(); + + let wasm_handler = oak_functions_service::wasm::new_wasm_handler( + &wasm_module_bytes, + lookup_data_manager.clone(), + None, + ) + .unwrap(); + + TestState { + wasm_handler, + lookup_data_manager, + } +} + +criterion_group!(benches, bench_invoke_echo, bench_invoke_lookup); +criterion_main!(benches); diff --git a/oak_functions_service/src/wasm/tests.rs b/oak_functions_service/src/wasm/tests.rs index 56fafb9dc2a..5e87e31b6f4 100644 --- a/oak_functions_service/src/wasm/tests.rs +++ b/oak_functions_service/src/wasm/tests.rs @@ -17,13 +17,11 @@ extern crate test; use alloc::{sync::Arc, vec::Vec}; -use std::time::Duration; use byteorder::{ByteOrder, LittleEndian}; use hashbrown::HashMap; use oak_functions_abi::Request; use spinning_top::Spinlock; -use test::Bencher; use super::{ api::StdWasmApiFactory, OakLinker, UserState, WasmApiFactory, WasmHandler, ALLOC_FUNCTION_NAME, @@ -143,40 +141,6 @@ fn test_invoke() { assert_eq!(response.body, data.to_vec()); } -#[bench] -fn bench_invoke(bencher: &mut Bencher) { - let test_state = create_test_state(); - let data = b"Hello, world!"; - - let summary = bencher.bench(|bencher| { - bencher.iter(|| { - let response = test_state - .wasm_handler - .handle_invoke(Request { - body: data.to_vec(), - }) - .unwrap(); - assert_eq!(response.body, data.to_vec()); - }); - Ok(()) - }); - - // When running `cargo test` this benchmark test gets executed too, but `summary` will be `None` - // in that case. So, here we first check that `summary` is not empty. - if let Ok(Some(summary)) = summary { - // `summary.mean` is in nanoseconds, even though it is not explicitly documented in - // https://doc.rust-lang.org/test/stats/struct.Summary.html. - let elapsed = Duration::from_nanos(summary.mean as u64); - // We expect the `mean` time for loading the test Wasm module and running its main function - // to be less than a fixed threshold. - assert!( - elapsed < Duration::from_micros(100), - "elapsed time: {:.0?}", - elapsed - ); - } -} - struct TestState { instance: wasmi::Instance, store: wasmi::Store, @@ -188,7 +152,7 @@ fn create_test_state() -> TestState { let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); let api_factory = Arc::new(StdWasmApiFactory { - lookup_data_manager, + lookup_data_manager: lookup_data_manager.clone(), }); let wasm_module_path = oak_functions_test_utils::build_rust_crate_wasm("echo").unwrap(); From 737f22e494f6ffba2b123b3c1116d18ad364fb13 Mon Sep 17 00:00:00 2001 From: jul-sh Date: Thu, 25 Jan 2024 11:06:23 -0500 Subject: [PATCH 34/42] Use entrypoint macro for remaining enclave apps (#4714) * Apply entrypoint macro to remaining enclave apps * address comments * restore accidentially removed feature def --- enclave_apps/key_xor_test_app/src/main.rs | 27 ++----------------- enclave_apps/oak_echo_enclave_app/src/main.rs | 26 ++---------------- .../oak_echo_raw_enclave_app/src/main.rs | 26 ++---------------- .../src/lib.rs | 9 ++++++- 4 files changed, 14 insertions(+), 74 deletions(-) diff --git a/enclave_apps/key_xor_test_app/src/main.rs b/enclave_apps/key_xor_test_app/src/main.rs index 3bd50f6fe9d..e21520d195f 100644 --- a/enclave_apps/key_xor_test_app/src/main.rs +++ b/enclave_apps/key_xor_test_app/src/main.rs @@ -20,26 +20,13 @@ extern crate alloc; -use core::panic::PanicInfo; - -use log::info; use oak_channel::{Read, Write}; use oak_restricted_kernel_interface::{syscall::read, DERIVED_KEY_FD}; -use oak_restricted_kernel_sdk::FileDescriptorChannel; - -#[no_mangle] -fn _start() -> ! { - oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); - main(); -} - -fn main() -> ! { - info!("In main!"); - run_server() -} +use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel}; // Continuously reads single bytes from the communication channel, XORs them with a byte from the // derived key and sends them back. +#[entrypoint] fn run_server() -> ! { let mut key = [0u8; 32]; let mut byte = [0u8; 1]; @@ -54,13 +41,3 @@ fn run_server() -> ! { channel.write_all(&byte[..]).expect("couldn't write bytes"); } } - -#[alloc_error_handler] -fn out_of_memory(layout: ::core::alloc::Layout) -> ! { - oak_restricted_kernel_sdk::alloc_error_handler(layout); -} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - oak_restricted_kernel_sdk::panic_handler(info); -} diff --git a/enclave_apps/oak_echo_enclave_app/src/main.rs b/enclave_apps/oak_echo_enclave_app/src/main.rs index 188bd71df43..ed195643b27 100644 --- a/enclave_apps/oak_echo_enclave_app/src/main.rs +++ b/enclave_apps/oak_echo_enclave_app/src/main.rs @@ -21,25 +21,13 @@ extern crate alloc; use alloc::boxed::Box; -use core::panic::PanicInfo; -use log::info; use oak_core::samplestore::StaticSampleStore; -use oak_restricted_kernel_sdk::FileDescriptorChannel; - -#[no_mangle] -fn _start() -> ! { - oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); - main(); -} - -fn main() -> ! { - info!("In main!"); - start_echo_server() -} +use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel}; // Starts an echo server that uses the Oak communication channel: // https://github.com/project-oak/oak/blob/main/oak_channel/SPEC.md +#[entrypoint] fn start_echo_server() -> ! { let mut invocation_stats = StaticSampleStore::<1000>::new().unwrap(); let service = oak_echo_service::EchoService; @@ -51,13 +39,3 @@ fn start_echo_server() -> ! { ) .expect("server encountered an unrecoverable error"); } - -#[alloc_error_handler] -fn out_of_memory(layout: ::core::alloc::Layout) -> ! { - oak_restricted_kernel_sdk::alloc_error_handler(layout); -} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - oak_restricted_kernel_sdk::panic_handler(info); -} diff --git a/enclave_apps/oak_echo_raw_enclave_app/src/main.rs b/enclave_apps/oak_echo_raw_enclave_app/src/main.rs index 8cd98178944..df45a447744 100644 --- a/enclave_apps/oak_echo_raw_enclave_app/src/main.rs +++ b/enclave_apps/oak_echo_raw_enclave_app/src/main.rs @@ -21,27 +21,15 @@ extern crate alloc; use alloc::{vec, vec::Vec}; -use core::panic::PanicInfo; -use log::info; use oak_channel::{Read, Write}; -use oak_restricted_kernel_sdk::FileDescriptorChannel; +use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel}; const MESSAGE_SIZE: usize = 1; -#[no_mangle] -fn _start() -> ! { - oak_restricted_kernel_sdk::init(log::LevelFilter::Debug); - main(); -} - -fn main() -> ! { - info!("In main!"); - start_echo_server() -} - // Starts an echo server that reads single bytes from the channel and writes // them back. +#[entrypoint] fn start_echo_server() -> ! { let mut channel = FileDescriptorChannel::default(); loop { @@ -53,13 +41,3 @@ fn start_echo_server() -> ! { channel.write_all(&bytes).expect("couldn't write bytes"); } } - -#[alloc_error_handler] -fn out_of_memory(layout: ::core::alloc::Layout) -> ! { - oak_restricted_kernel_sdk::alloc_error_handler(layout); -} - -#[panic_handler] -fn panic(info: &PanicInfo) -> ! { - oak_restricted_kernel_sdk::panic_handler(info); -} diff --git a/oak_restricted_kernel_sdk_proc_macro/src/lib.rs b/oak_restricted_kernel_sdk_proc_macro/src/lib.rs index 8cb93ea6988..eb27c95e58a 100644 --- a/oak_restricted_kernel_sdk_proc_macro/src/lib.rs +++ b/oak_restricted_kernel_sdk_proc_macro/src/lib.rs @@ -36,9 +36,16 @@ fn validate_type_signature(entry_item: Item) -> Result { )), }?; + if !entry_fn.sig.inputs.is_empty() { + return Err(syn::Error::new( + syn::spanned::Spanned::span(&entry_fn.sig.inputs), + "the entrypoint function must not take arguments", + )); + } + let return_type_error = syn::Error::new( syn::spanned::Spanned::span(&entry_fn.sig.output), - "the entrypoint function should have return type !", + "the entrypoint function must have return type !", ); match &entry_fn.sig.output { syn::ReturnType::Default => Err(return_type_error), From 2b297487b31a5aa2365439057470c12a6ff9a40a Mon Sep 17 00:00:00 2001 From: jul-sh Date: Thu, 25 Jan 2024 14:22:46 -0500 Subject: [PATCH 35/42] Add simple testing launcher for enclave apps (#4711) * Add simple testing launcher for enclave apps Currently we only have a very specific launcher used for oak functions. The idea is to use this simpler launcher to aid in the development of the restricted kernel orchestrator. * improve readme * move out of the testing dir * Add option to specify initial ramdisk * Revert "Add option to specify initial ramdisk" This reverts commit c7b447894ee349c812cfa8422f645902a7e138c0. * Add option to specify initial ramdisk * improve comments --- Cargo.lock | 13 +++++ Cargo.toml | 1 + .../benches/integration_benches.rs | 1 + .../tests/integration_test.rs | 2 + oak_launcher_utils/src/launcher.rs | 11 ++++ oak_restricted_kernel_launcher/Cargo.toml | 22 ++++++++ oak_restricted_kernel_launcher/README.md | 35 +++++++++++++ oak_restricted_kernel_launcher/src/lib.rs | 32 ++++++++++++ oak_restricted_kernel_launcher/src/main.rs | 52 +++++++++++++++++++ 9 files changed, 169 insertions(+) create mode 100644 oak_restricted_kernel_launcher/Cargo.toml create mode 100644 oak_restricted_kernel_launcher/README.md create mode 100644 oak_restricted_kernel_launcher/src/lib.rs create mode 100644 oak_restricted_kernel_launcher/src/main.rs diff --git a/Cargo.lock b/Cargo.lock index 549312331ba..e1bb2319cc7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2632,6 +2632,19 @@ dependencies = [ "strum 0.25.0", ] +[[package]] +name = "oak_restricted_kernel_launcher" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "env_logger", + "log", + "oak_channel", + "oak_launcher_utils", + "tokio", +] + [[package]] name = "oak_restricted_kernel_sdk" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 71d7d09974c..3b32acdaf51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,7 @@ members = [ "oak_restricted_kernel_dice", "oak_restricted_kernel_interface", "oak_restricted_kernel_sdk", + "oak_restricted_kernel_launcher", "oak_sev_guest", "oak_sev_snp_attestation_report", "oak_simple_io", diff --git a/oak_functions_launcher/benches/integration_benches.rs b/oak_functions_launcher/benches/integration_benches.rs index 20f776d2bc0..132c36b4710 100644 --- a/oak_functions_launcher/benches/integration_benches.rs +++ b/oak_functions_launcher/benches/integration_benches.rs @@ -80,6 +80,7 @@ fn run_bench(b: &mut Bencher, config: &OakFunctionsTestConfig) { "oak_stage0.bin", ]), gdb: None, + initrd: None, memory_size: Some("256M".to_string()), }; log::debug!("launcher params: {:?}", params); diff --git a/oak_functions_launcher/tests/integration_test.rs b/oak_functions_launcher/tests/integration_test.rs index 25bb838de41..a5c17ac3cff 100644 --- a/oak_functions_launcher/tests/integration_test.rs +++ b/oak_functions_launcher/tests/integration_test.rs @@ -204,6 +204,7 @@ async fn test_load_large_lookup_data() { "oak_stage0.bin", ]), gdb: None, + initrd: None, memory_size: Some("256M".to_string()), }; log::debug!("launcher params: {:?}", params); @@ -299,6 +300,7 @@ async fn test_load_two_gib_lookup_data() { "oak_stage0.bin", ]), gdb: None, + initrd: None, memory_size: Some("256M".to_string()), }; log::debug!("launcher params: {:?}", params); diff --git a/oak_launcher_utils/src/launcher.rs b/oak_launcher_utils/src/launcher.rs index a8ba8b00ccc..f4922158402 100644 --- a/oak_launcher_utils/src/launcher.rs +++ b/oak_launcher_utils/src/launcher.rs @@ -64,6 +64,10 @@ pub struct Params { /// Gigabyte). #[arg(long)] pub memory_size: Option, + + /// Path to the initrd image to use. + #[arg(long, value_parser = path_exists)] + pub initrd: Option, } /// Checks if file with a given path exists. @@ -167,6 +171,13 @@ impl Instance { cmd.arg("-S"); } + if let Some(initrd) = params.initrd { + cmd.args([ + "-initrd", + initrd.into_os_string().into_string().unwrap().as_str(), + ]); + } + info!("executing: {:?}", cmd); let instance = cmd.spawn()?; diff --git a/oak_restricted_kernel_launcher/Cargo.toml b/oak_restricted_kernel_launcher/Cargo.toml new file mode 100644 index 00000000000..dde9f0bf229 --- /dev/null +++ b/oak_restricted_kernel_launcher/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "oak_restricted_kernel_launcher" +version = "0.1.0" +authors = ["Juliette Pluto "] +edition = "2021" +license = "Apache-2.0" + +[dependencies] +anyhow = "*" +clap = { version = "*", features = ["derive"] } +log = "*" +env_logger = "*" +tokio = { version = "*", features = [ + "rt-multi-thread", + "macros", + "net", + "process", + "signal", + "sync", +] } +oak_launcher_utils = { workspace = true } +oak_channel = { workspace = true, features = ["client"] } diff --git a/oak_restricted_kernel_launcher/README.md b/oak_restricted_kernel_launcher/README.md new file mode 100644 index 00000000000..41ab423588f --- /dev/null +++ b/oak_restricted_kernel_launcher/README.md @@ -0,0 +1,35 @@ +# oak_restricted_kernel_launcher + +Simple launcher used to launch an instance of the restricted kernel in a VM. + +Documentation is available via: + +```shell +cargo run --package=oak_restricted_kernel_launcher -- --help +``` + +The instructions below for building the required dependencies and running an app +using this launcher are provided on a best effort basis. + +First the dependencies used to run launch an instance of the restricted kernel +must be built. + +(instructions gained from inspecting xtask, may change in the future) + +Stage0, the restricted kernel, and an enclave app may be built like so: + +```shell +just stage0_bin oak_restricted_kernel_bin oak_echo_raw_enclave_app +``` + +After building dependencies, an enclave app may be run like so: + +```shell +RUST_LOG=DEBUG \ +cargo run --package=oak_restricted_kernel_launcher -- \ +--enclave-binary=oak_restricted_kernel_bin/target/x86_64-unknown-none/debug/oak_restricted_kernel_bin \ +--vmm-binary=$(which qemu-system-x86_64) \ +--memory-size=256M \ +--bios-binary=stage0_bin/target/x86_64-unknown-none/release/oak_stage0.bin \ +--app-binary=enclave_apps/target/x86_64-unknown-none/debug/oak_echo_raw_enclave_app +``` diff --git a/oak_restricted_kernel_launcher/src/lib.rs b/oak_restricted_kernel_launcher/src/lib.rs new file mode 100644 index 00000000000..deb995d9436 --- /dev/null +++ b/oak_restricted_kernel_launcher/src/lib.rs @@ -0,0 +1,32 @@ +// +// Copyright 2024 The Project Oak Authors +// +// Licensed 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. +// + +#![feature(never_type)] +#![feature(result_flattening)] +#![feature(array_chunks)] + +use oak_launcher_utils::{ + channel::{self}, + launcher, +}; + +pub async fn create( + params: launcher::Params, +) -> Result<(Box, channel::ConnectorHandle), Box> +{ + log::info!("creating guest instance"); + launcher::launch(params).await +} diff --git a/oak_restricted_kernel_launcher/src/main.rs b/oak_restricted_kernel_launcher/src/main.rs new file mode 100644 index 00000000000..17fb4e25122 --- /dev/null +++ b/oak_restricted_kernel_launcher/src/main.rs @@ -0,0 +1,52 @@ +// +// Copyright 2024 The Project Oak Authors +// +// Licensed 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. +// + +#![feature(never_type)] +#![feature(result_flattening)] +#![feature(array_chunks)] + +use clap::Parser; +use tokio::signal; + +#[derive(Parser, Debug)] +pub struct Args { + /// Launcher params. + #[clap(flatten)] + launcher_params: oak_launcher_utils::launcher::Params, +} + +#[tokio::main] +async fn main() -> Result<(), Box> { + let cli = Args::parse(); + env_logger::init(); + log::info!("Restricted Kernel Launcher args: {:?}", cli); + + let (mut launched_instance, _connector_handle) = + oak_restricted_kernel_launcher::create(cli.launcher_params).await?; + + // Wait until something dies or we get a signal to terminate. + tokio::select! { + _ = signal::ctrl_c() => { + log::info!("Ctrl-C received, terminating VMM"); + launched_instance.kill().await?; + }, + val = launched_instance.wait() => { + log::error!("Unexpected VMM exit, status: {:?}", val); + }, + } + + Ok(()) +} From c47a2eb905160a5a957e92abc1ed56431d0c139f Mon Sep 17 00:00:00 2001 From: jul-sh Date: Thu, 25 Jan 2024 14:44:16 -0500 Subject: [PATCH 36/42] Expose oak channel in the SDK & update enclave apps (#4715) * Expose oak channel in the SDK & update enclave apps Also removes a whole set of other unused dependencies along the way. * don't proxy export oak core * fix merge conflict --- enclave_apps/Cargo.lock | 12 ------------ enclave_apps/Cargo.toml | 6 +----- enclave_apps/key_xor_test_app/Cargo.toml | 2 -- enclave_apps/key_xor_test_app/src/main.rs | 3 +-- enclave_apps/oak_echo_enclave_app/Cargo.toml | 4 +--- enclave_apps/oak_echo_enclave_app/src/main.rs | 4 ++-- enclave_apps/oak_echo_raw_enclave_app/Cargo.toml | 2 -- enclave_apps/oak_echo_raw_enclave_app/src/main.rs | 3 +-- enclave_apps/oak_functions_enclave_app/Cargo.toml | 8 +------- enclave_apps/oak_functions_enclave_app/src/main.rs | 4 ++-- oak_restricted_kernel_sdk/src/channel.rs | 2 +- oak_restricted_kernel_sdk/src/lib.rs | 2 +- 12 files changed, 11 insertions(+), 41 deletions(-) diff --git a/enclave_apps/Cargo.lock b/enclave_apps/Cargo.lock index 6b29b87513f..89d646e8d7e 100644 --- a/enclave_apps/Cargo.lock +++ b/enclave_apps/Cargo.lock @@ -586,10 +586,8 @@ version = "0.1.0" dependencies = [ "log", "micro_rpc", - "oak_channel", "oak_restricted_kernel_interface", "oak_restricted_kernel_sdk", - "static_assertions", ] [[package]] @@ -752,11 +750,9 @@ version = "0.1.0" dependencies = [ "log", "micro_rpc", - "oak_channel", "oak_core", "oak_echo_service", "oak_restricted_kernel_sdk", - "static_assertions", ] [[package]] @@ -765,9 +761,7 @@ version = "0.1.0" dependencies = [ "log", "micro_rpc", - "oak_channel", "oak_restricted_kernel_sdk", - "static_assertions", ] [[package]] @@ -807,15 +801,9 @@ version = "0.1.0" dependencies = [ "log", "micro_rpc", - "oak_attestation", - "oak_channel", "oak_core", - "oak_crypto", - "oak_dice", "oak_functions_service", "oak_restricted_kernel_sdk", - "static_assertions", - "zerocopy", ] [[package]] diff --git a/enclave_apps/Cargo.toml b/enclave_apps/Cargo.toml index c05c196e1f5..7e654a21640 100644 --- a/enclave_apps/Cargo.toml +++ b/enclave_apps/Cargo.toml @@ -9,10 +9,6 @@ members = [ [workspace.dependencies] micro_rpc = { path = "../micro_rpc" } -oak_attestation = { path = "../oak_attestation" } -oak_channel = { path = "../oak_channel" } -oak_core = { path = "../oak_core" } -oak_crypto = { path = "../oak_crypto" } -oak_dice = { path = "../oak_dice" } oak_restricted_kernel_sdk = { path = "../oak_restricted_kernel_sdk" } oak_restricted_kernel_interface = { path = "../oak_restricted_kernel_interface" } +oak_core = { path = "../oak_core" } diff --git a/enclave_apps/key_xor_test_app/Cargo.toml b/enclave_apps/key_xor_test_app/Cargo.toml index 193f8dbb790..7207bf46d4d 100644 --- a/enclave_apps/key_xor_test_app/Cargo.toml +++ b/enclave_apps/key_xor_test_app/Cargo.toml @@ -8,10 +8,8 @@ license = "Apache-2.0" [dependencies] log = "*" micro_rpc = { workspace = true } -oak_channel = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } oak_restricted_kernel_interface = { workspace = true } -static_assertions = "*" [[bin]] name = "key_xor_test_app" diff --git a/enclave_apps/key_xor_test_app/src/main.rs b/enclave_apps/key_xor_test_app/src/main.rs index e21520d195f..28e393a387a 100644 --- a/enclave_apps/key_xor_test_app/src/main.rs +++ b/enclave_apps/key_xor_test_app/src/main.rs @@ -20,9 +20,8 @@ extern crate alloc; -use oak_channel::{Read, Write}; use oak_restricted_kernel_interface::{syscall::read, DERIVED_KEY_FD}; -use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel}; +use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel, Read, Write}; // Continuously reads single bytes from the communication channel, XORs them with a byte from the // derived key and sends them back. diff --git a/enclave_apps/oak_echo_enclave_app/Cargo.toml b/enclave_apps/oak_echo_enclave_app/Cargo.toml index ddf67d5c138..84a52efece6 100644 --- a/enclave_apps/oak_echo_enclave_app/Cargo.toml +++ b/enclave_apps/oak_echo_enclave_app/Cargo.toml @@ -9,10 +9,8 @@ license = "Apache-2.0" oak_echo_service = { path = "../../testing/oak_echo_service" } log = "*" micro_rpc = { workspace = true } -oak_channel = { workspace = true } -oak_core = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } -static_assertions = "*" +oak_core = { workspace = true } [[bin]] name = "oak_echo_enclave_app" diff --git a/enclave_apps/oak_echo_enclave_app/src/main.rs b/enclave_apps/oak_echo_enclave_app/src/main.rs index ed195643b27..3e464e974f3 100644 --- a/enclave_apps/oak_echo_enclave_app/src/main.rs +++ b/enclave_apps/oak_echo_enclave_app/src/main.rs @@ -23,7 +23,7 @@ extern crate alloc; use alloc::boxed::Box; use oak_core::samplestore::StaticSampleStore; -use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel}; +use oak_restricted_kernel_sdk::{entrypoint, start_blocking_server, FileDescriptorChannel}; // Starts an echo server that uses the Oak communication channel: // https://github.com/project-oak/oak/blob/main/oak_channel/SPEC.md @@ -32,7 +32,7 @@ fn start_echo_server() -> ! { let mut invocation_stats = StaticSampleStore::<1000>::new().unwrap(); let service = oak_echo_service::EchoService; let server = oak_echo_service::proto::EchoServer::new(service); - oak_channel::server::start_blocking_server( + start_blocking_server( Box::::default(), server, &mut invocation_stats, diff --git a/enclave_apps/oak_echo_raw_enclave_app/Cargo.toml b/enclave_apps/oak_echo_raw_enclave_app/Cargo.toml index fa4beba224a..ad3947d7c61 100644 --- a/enclave_apps/oak_echo_raw_enclave_app/Cargo.toml +++ b/enclave_apps/oak_echo_raw_enclave_app/Cargo.toml @@ -8,9 +8,7 @@ license = "Apache-2.0" [dependencies] log = "*" micro_rpc = { workspace = true } -oak_channel = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } -static_assertions = "*" [[bin]] name = "oak_echo_raw_enclave_app" diff --git a/enclave_apps/oak_echo_raw_enclave_app/src/main.rs b/enclave_apps/oak_echo_raw_enclave_app/src/main.rs index df45a447744..0c7cd02a6d1 100644 --- a/enclave_apps/oak_echo_raw_enclave_app/src/main.rs +++ b/enclave_apps/oak_echo_raw_enclave_app/src/main.rs @@ -22,8 +22,7 @@ extern crate alloc; use alloc::{vec, vec::Vec}; -use oak_channel::{Read, Write}; -use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel}; +use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel, Read, Write}; const MESSAGE_SIZE: usize = 1; diff --git a/enclave_apps/oak_functions_enclave_app/Cargo.toml b/enclave_apps/oak_functions_enclave_app/Cargo.toml index a755a74343f..74228c970f2 100644 --- a/enclave_apps/oak_functions_enclave_app/Cargo.toml +++ b/enclave_apps/oak_functions_enclave_app/Cargo.toml @@ -17,14 +17,8 @@ allow_sensitive_logging = [] oak_functions_service = { path = "../../oak_functions_service", default-features = false } log = "*" micro_rpc = { workspace = true } -oak_attestation = { workspace = true } -oak_core = { workspace = true } -oak_channel = { workspace = true } -oak_crypto = { workspace = true } -oak_dice = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } -static_assertions = "*" -zerocopy = "*" +oak_core = { workspace = true } [[bin]] name = "oak_functions_enclave_app" diff --git a/enclave_apps/oak_functions_enclave_app/src/main.rs b/enclave_apps/oak_functions_enclave_app/src/main.rs index 018411ceee7..cb1716e212e 100644 --- a/enclave_apps/oak_functions_enclave_app/src/main.rs +++ b/enclave_apps/oak_functions_enclave_app/src/main.rs @@ -23,7 +23,7 @@ extern crate alloc; use alloc::{boxed::Box, sync::Arc}; use oak_core::samplestore::StaticSampleStore; -use oak_restricted_kernel_sdk::{entrypoint, FileDescriptorChannel}; +use oak_restricted_kernel_sdk::{entrypoint, start_blocking_server, FileDescriptorChannel}; #[entrypoint] fn main() -> ! { @@ -45,7 +45,7 @@ fn main() -> ! { None, ); let server = oak_functions_service::proto::oak::functions::OakFunctionsServer::new(service); - oak_channel::server::start_blocking_server( + start_blocking_server( Box::::default(), server, &mut invocation_stats, diff --git a/oak_restricted_kernel_sdk/src/channel.rs b/oak_restricted_kernel_sdk/src/channel.rs index a1f2ba9e82e..134fd938eee 100644 --- a/oak_restricted_kernel_sdk/src/channel.rs +++ b/oak_restricted_kernel_sdk/src/channel.rs @@ -15,7 +15,7 @@ // use anyhow::anyhow; -use oak_channel::{Read, Write}; +pub use oak_channel::{server::start_blocking_server, Read, Write}; use oak_restricted_kernel_interface::OAK_CHANNEL_FD; /// Channel that communicates over a file descriptor. diff --git a/oak_restricted_kernel_sdk/src/lib.rs b/oak_restricted_kernel_sdk/src/lib.rs index e5c58eca829..b1124ae7f09 100644 --- a/oak_restricted_kernel_sdk/src/lib.rs +++ b/oak_restricted_kernel_sdk/src/lib.rs @@ -22,7 +22,7 @@ mod channel; mod dice; mod logging; -pub use channel::FileDescriptorChannel; +pub use channel::*; pub use dice::*; pub use logging::StderrLogger; use logging::STDERR_LOGGER; From c47762e81740183b3d2c4670eb2126282e25b9f6 Mon Sep 17 00:00:00 2001 From: Andri Saar Date: Thu, 25 Jan 2024 19:32:46 +0000 Subject: [PATCH 37/42] Change the default CPU target to `x86-64-v3` --- .cargo/config.toml | 4 ++++ enclave_apps/.cargo/config.toml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.cargo/config.toml b/.cargo/config.toml index a211cd31170..2710164180c 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -8,6 +8,8 @@ rustflags = [ "-C", "link-args=-Xlinker --build-id=none", + "-C", + "target-cpu=x86-64-v3", # Enable `tokio_unstable` so that we can access the Tokio runtime metrics. "--cfg", "tokio_unstable" @@ -23,6 +25,8 @@ rustflags = [ rustflags = [ "-C", "link-args=-Xlinker --build-id=none", + "-C", + "target-cpu=x86-64-v3", # Enable `tokio_unstable` so that we can access the Tokio runtime metrics. "--cfg", "tokio_unstable" diff --git a/enclave_apps/.cargo/config.toml b/enclave_apps/.cargo/config.toml index 6104b0b15f3..2e29d710a69 100644 --- a/enclave_apps/.cargo/config.toml +++ b/enclave_apps/.cargo/config.toml @@ -2,4 +2,4 @@ target = "x86_64-unknown-none" [target.x86_64-unknown-none] -rustflags = "-C relocation-model=static -C code-model=small -C target-feature=+sse,+sse2,+ssse3,+sse4.1,+sse4.2,+avx,+avx2,+rdrand,-soft-float" +rustflags = "-C relocation-model=static -C code-model=small -C target-feature=+sse,+sse2,+ssse3,+sse4.1,+sse4.2,+avx,+avx2,+rdrand,-soft-float -C target-cpu=x86-64-v3" From 40e4f58fffcbd64554207903516d20ae7dcb6661 Mon Sep 17 00:00:00 2001 From: Andri Saar Date: Tue, 23 Jan 2024 18:33:02 +0000 Subject: [PATCH 38/42] Create a fake EFI System Table in stage0. --- Cargo.lock | 16 +++++ stage0/Cargo.toml | 5 ++ stage0/src/efi.rs | 139 ++++++++++++++++++++++++++++++++++++++++ stage0/src/lib.rs | 10 +++ stage0/src/zero_page.rs | 6 ++ stage0_bin/Cargo.lock | 16 +++++ 6 files changed, 192 insertions(+) create mode 100644 stage0/src/efi.rs diff --git a/Cargo.lock b/Cargo.lock index e1bb2319cc7..4c3025d4403 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -697,6 +697,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crc32fast" version = "1.3.2" @@ -2713,6 +2728,7 @@ version = "0.1.0" dependencies = [ "bitflags 2.4.1", "coset", + "crc", "elf", "hex", "hkdf", diff --git a/stage0/Cargo.toml b/stage0/Cargo.toml index 3dd7a747249..a37d8cf234c 100644 --- a/stage0/Cargo.toml +++ b/stage0/Cargo.toml @@ -5,8 +5,13 @@ authors = ["Andri Saar "] edition = "2021" license = "Apache-2.0" +[features] +default = ["efi"] +efi = ["dep:crc"] + [dependencies] bitflags = "*" +crc = { version = "*", optional = true } coset = { version = "*", default-features = false } elf = { version = "*", default-features = false } hex = { version = "*", default-features = false, features = ["alloc"] } diff --git a/stage0/src/efi.rs b/stage0/src/efi.rs new file mode 100644 index 00000000000..2edb66ac266 --- /dev/null +++ b/stage0/src/efi.rs @@ -0,0 +1,139 @@ +// +// Copyright 2023 The Project Oak Authors +// +// Licensed 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. +// + +//! Basic data structures for EFI. +//! +//! IMPORTANT: We do not claim any compatibility with EFI. These data structures are here just in +//! hopes that we'll be able to fool Linux enough not to scan a non-validated range of memory for +//! DMI data when booting under SEV-SNP. +//! +//! We do not plan to support any EFI services in stage0. + +use alloc::boxed::Box; +use core::mem; + +use zerocopy::AsBytes; + +use crate::{BootAllocator, BOOT_ALLOC}; + +type EfiHandle = usize; + +/// EFI table header. +/// +/// See Section 4.2.1. EFI_TABLE_HEADER in the UEFI Specification 2.10 for more details. +#[repr(C)] +#[derive(AsBytes, Default)] +pub struct EfiTableHeader { + /// Identifies the type of table that follows. + pub signature: u64, + + /// Revsion of the EFI Specification to which this table conforms. + pub revision: u32, + + /// The size, in bytes, of the entire table, including the header. + pub header_size: u32, + + /// 32-bit CRC of the entire table. + pub crc32: u32, + + /// Reserved, must be zero. + pub reserved: u32, +} + +impl EfiTableHeader { + const EFI_2_100_SYSTEM_TABLE_REVISION: u32 = ((2 << 16) | (100)); +} + +/// EFI System Table. +/// +/// See Section 4.3.1. EFI_SYSTEM_TABLE in the UEFI Specification 2.10 for more details. +#[repr(C)] +#[derive(AsBytes)] +pub struct EfiSystemTable { + /// Table header for the EFI System Table. + pub header: EfiTableHeader, + + /// Pointer to a null-terminated string that identifies the vendor of the firmware. + pub firmware_vendor: usize, + + /// Vendor-specific value that identifies the revision of the firmware. + pub firmware_revision: u32, + + // Padding to placate `zerocopy::AsBytes.` This does not change the size of the data structure, + // as we're `repr(C)` and the padding would be added implicitly anyway. (Verified by the static + // assertion just after the data structure.) + _pad: u32, + + pub console_in_handle: EfiHandle, + pub con_in: usize, + pub console_out_handle: EfiHandle, + pub con_out: usize, + pub std_err_handle: EfiHandle, + pub std_err: usize, + pub runtime_services: usize, + pub boot_services: usize, + + /// Number of system configuration tables in the `config_table`. + pub num_entries: usize, + + /// Pointer to the system configuration tables. + pub config_table: usize, +} +static_assertions::assert_eq_size!(EfiSystemTable, [u8; 120usize]); + +impl EfiSystemTable { + const SIGNATURE: u64 = 0x5453595320494249; + + const CCITT32_CRC: crc::Crc = crc::Crc::::new(&crc::CRC_32_CKSUM); + + pub fn checksum(&self) -> u32 { + Self::CCITT32_CRC.checksum(self.as_bytes()) + } +} + +impl Default for EfiSystemTable { + fn default() -> Self { + let mut table = Self { + header: EfiTableHeader { + signature: Self::SIGNATURE, + revision: EfiTableHeader::EFI_2_100_SYSTEM_TABLE_REVISION, + header_size: mem::size_of::().try_into().unwrap(), + ..EfiTableHeader::default() + }, + firmware_vendor: 0, + firmware_revision: 0, + _pad: 0, + console_in_handle: 0, + con_in: 0, + console_out_handle: 0, + con_out: 0, + std_err_handle: 0, + std_err: 0, + runtime_services: 0, + boot_services: 0, + num_entries: 0, + config_table: 0, + }; + table.header.crc32 = table.checksum(); + table + } +} + +/// Create a fake placeholder EFI System Table and put a pointer to that in the zero page. +pub fn create_efi_skeleton() -> Box { + // We don't set anything in the table itself. As said, it's a fake table. + Box::new_in(EfiSystemTable::default(), &BOOT_ALLOC) +} diff --git a/stage0/src/lib.rs b/stage0/src/lib.rs index c0b4511521e..037b2f29d9e 100644 --- a/stage0/src/lib.rs +++ b/stage0/src/lib.rs @@ -50,6 +50,8 @@ mod allocator; mod apic; mod cmos; mod dice_attestation; +#[cfg(feature = "efi")] +mod efi; mod fw_cfg; mod initramfs; mod kernel; @@ -363,6 +365,14 @@ pub fn rust64_start(encrypted: u64) -> ! { }; zero_page.set_cmdline(cmdline); + // If required, create a fake EFI system table. + if cfg!(feature = "efi") { + let system_table = efi::create_efi_skeleton(); + zero_page.set_efi_system_table(PhysAddr::new( + VirtAddr::from_ptr(Box::leak(system_table)).as_u64(), + )) + } + log::info!("jumping to kernel at {:#018x}", entry.as_u64()); // Clean-ups we need to do just before we jump to the kernel proper: clean up the early GHCB and diff --git a/stage0/src/zero_page.rs b/stage0/src/zero_page.rs index 9c548c7b461..98b80b5619a 100644 --- a/stage0/src/zero_page.rs +++ b/stage0/src/zero_page.rs @@ -300,6 +300,12 @@ impl ZeroPage { // of RAM. self.inner.hdr.ramdisk_size = ram_disk.len() as u32; } + + /// Sets the address of the EFI system table. + pub fn set_efi_system_table(&mut self, efi_info: PhysAddr) { + self.inner.efi_info.efi_systab = efi_info.as_u64() as u32; + self.inner.efi_info.efi_systab_hi = (efi_info.as_u64() >> 32) as u32; + } } /// Builds an E820 table by reading the low and high memory amount from CMOS. diff --git a/stage0_bin/Cargo.lock b/stage0_bin/Cargo.lock index d562052d637..ba4430cad0b 100644 --- a/stage0_bin/Cargo.lock +++ b/stage0_bin/Cargo.lock @@ -150,6 +150,21 @@ dependencies = [ "libc", ] +[[package]] +name = "crc" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + [[package]] name = "crypto-bigint" version = "0.5.3" @@ -446,6 +461,7 @@ version = "0.1.0" dependencies = [ "bitflags 2.4.0", "coset", + "crc", "elf", "hex", "hkdf", From 4bfdb0f190e4156792be60ffb22948d713e0a5eb Mon Sep 17 00:00:00 2001 From: jul-sh Date: Thu, 25 Jan 2024 20:53:13 -0500 Subject: [PATCH 39/42] Extract restricted kernel specific logic from oak_functions_service (#4708) * Extract restricted kernel specific logic from oak_functions_service This extracts logic specific to the restricted kernel from the `oak_functions_service` crate, similiarly to what we do for oak container's in oak_functions_containers_app/src/lib.rs. It fixes an issue encountered in the oak container's version, which depends on a submodule of the `oak_functions_service` crate, and inherited it's dependency on the `oak_restricted_kernel_sdk. * remove unused deps * resolve type errors caused by dependency upgrades * fix merge error --- Cargo.lock | 41 +++- Cargo.toml | 2 + enclave_apps/Cargo.lock | 19 +- .../oak_functions_enclave_app/Cargo.toml | 6 +- .../oak_functions_enclave_app/src/main.rs | 5 +- oak_functions_enclave_service/Cargo.toml | 32 +++ oak_functions_enclave_service/src/lib.rs | 224 ++++++++++++++++++ .../tests/integration_test.rs | 16 +- oak_functions_sdk/Cargo.toml | 1 - oak_functions_sdk/tests/integration_test.rs | 19 +- oak_functions_service/Cargo.toml | 6 - oak_functions_service/src/lib.rs | 201 ---------------- 12 files changed, 336 insertions(+), 236 deletions(-) create mode 100644 oak_functions_enclave_service/Cargo.toml create mode 100644 oak_functions_enclave_service/src/lib.rs rename {oak_functions_service => oak_functions_enclave_service}/tests/integration_test.rs (98%) diff --git a/Cargo.lock b/Cargo.lock index 4c3025d4403..9541a6d1e83 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -62,6 +62,17 @@ dependencies = [ "subtle", ] +[[package]] +name = "ahash" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a824f2aa7e75a0c98c5a504fceb80649e9c35265d44525b5f94de4771a395cd" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + [[package]] name = "ahash" version = "0.8.7" @@ -1336,6 +1347,9 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.7", +] [[package]] name = "hashbrown" @@ -1343,7 +1357,7 @@ version = "0.14.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" dependencies = [ - "ahash", + "ahash 0.8.7", "allocator-api2", ] @@ -2393,6 +2407,25 @@ dependencies = [ "xtask", ] +[[package]] +name = "oak_functions_enclave_service" +version = "0.1.0" +dependencies = [ + "anyhow", + "env_logger", + "log", + "micro_rpc", + "oak_attestation", + "oak_core", + "oak_crypto", + "oak_dice", + "oak_functions_service", + "oak_functions_test_utils", + "oak_proto_rust", + "oak_restricted_kernel_sdk", + "prost", +] + [[package]] name = "oak_functions_launcher" version = "0.1.0" @@ -2446,7 +2479,6 @@ dependencies = [ name = "oak_functions_sdk" version = "0.1.0" dependencies = [ - "hashbrown 0.14.3", "lazy_static", "micro_rpc", "micro_rpc_build", @@ -2484,20 +2516,17 @@ dependencies = [ "byteorder", "bytes", "criterion", - "env_logger", - "hashbrown 0.14.3", + "hashbrown 0.12.3", "log", "micro_rpc", "micro_rpc_build", "oak_attestation", - "oak_core", "oak_crypto", "oak_dice", "oak_functions_abi", "oak_functions_sdk", "oak_functions_test_utils", "oak_proto_rust", - "oak_restricted_kernel_sdk", "prost", "rand", "spinning_top 0.3.0", diff --git a/Cargo.toml b/Cargo.toml index 3b32acdaf51..cf7ec11e5e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -40,6 +40,7 @@ members = [ "oak_functions_sdk/tests/lookup_module", "oak_functions_sdk/tests/testing_module", "oak_functions_service", + "oak_functions_enclave_service", "oak_functions_test_utils", "oak_hello_world_linux_init", "oak_launcher_utils", @@ -100,6 +101,7 @@ oak_functions_launcher = { path = "./oak_functions_launcher" } oak_functions_lookup = { path = "./oak_functions/lookup" } oak_functions_sdk = { path = "./oak_functions_sdk" } oak_functions_service = { path = "./oak_functions_service", default-features = false } +oak_functions_enclave_service = { path = "./oak_functions_enclave_service", default-features = false } oak_functions_test_utils = { path = "./oak_functions_test_utils" } oak_functions_wasm = { path = "./oak_functions/wasm" } oak_grpc_utils = { path = "./oak_grpc_utils" } diff --git a/enclave_apps/Cargo.lock b/enclave_apps/Cargo.lock index 89d646e8d7e..3f2f88ced7e 100644 --- a/enclave_apps/Cargo.lock +++ b/enclave_apps/Cargo.lock @@ -802,8 +802,25 @@ dependencies = [ "log", "micro_rpc", "oak_core", + "oak_functions_enclave_service", + "oak_restricted_kernel_sdk", +] + +[[package]] +name = "oak_functions_enclave_service" +version = "0.1.0" +dependencies = [ + "anyhow", + "log", + "micro_rpc", + "oak_attestation", + "oak_core", + "oak_crypto", + "oak_dice", "oak_functions_service", + "oak_proto_rust", "oak_restricted_kernel_sdk", + "prost", ] [[package]] @@ -828,13 +845,11 @@ dependencies = [ "micro_rpc", "micro_rpc_build", "oak_attestation", - "oak_core", "oak_crypto", "oak_dice", "oak_functions_abi", "oak_functions_sdk", "oak_proto_rust", - "oak_restricted_kernel_sdk", "prost", "spinning_top", "wasmi", diff --git a/enclave_apps/oak_functions_enclave_app/Cargo.toml b/enclave_apps/oak_functions_enclave_app/Cargo.toml index 74228c970f2..07123e4cd58 100644 --- a/enclave_apps/oak_functions_enclave_app/Cargo.toml +++ b/enclave_apps/oak_functions_enclave_app/Cargo.toml @@ -8,13 +8,15 @@ license = "Apache-2.0" [features] default = ["deny_sensitive_logging"] # Disable sensitive logging. -deny_sensitive_logging = ["oak_functions_service/deny_sensitive_logging"] +deny_sensitive_logging = [ + "oak_functions_enclave_service/deny_sensitive_logging" +] # Feature allow_sensitive_logging is not actually used in the code. It is only used as a # required feature to differentiate between the two binaries. allow_sensitive_logging = [] [dependencies] -oak_functions_service = { path = "../../oak_functions_service", default-features = false } +oak_functions_enclave_service = { path = "../../oak_functions_enclave_service", default-features = false } log = "*" micro_rpc = { workspace = true } oak_restricted_kernel_sdk = { workspace = true } diff --git a/enclave_apps/oak_functions_enclave_app/src/main.rs b/enclave_apps/oak_functions_enclave_app/src/main.rs index cb1716e212e..dbbc2cf6722 100644 --- a/enclave_apps/oak_functions_enclave_app/src/main.rs +++ b/enclave_apps/oak_functions_enclave_app/src/main.rs @@ -39,12 +39,13 @@ fn main() -> ! { .expect("couldn't encryption key"); let evidencer = oak_restricted_kernel_sdk::InstanceEvidenceProvider::create() .expect("couldn't get evidence"); - let service = oak_functions_service::OakFunctionsService::new( + let service = oak_functions_enclave_service::OakFunctionsService::new( evidencer, Arc::new(encryption_key_handle), None, ); - let server = oak_functions_service::proto::oak::functions::OakFunctionsServer::new(service); + let server = + oak_functions_enclave_service::proto::oak::functions::OakFunctionsServer::new(service); start_blocking_server( Box::::default(), server, diff --git a/oak_functions_enclave_service/Cargo.toml b/oak_functions_enclave_service/Cargo.toml new file mode 100644 index 00000000000..4f11945c471 --- /dev/null +++ b/oak_functions_enclave_service/Cargo.toml @@ -0,0 +1,32 @@ +[package] +name = "oak_functions_enclave_service" +version = "0.1.0" +authors = ["Andri Saar "] +edition = "2021" +license = "Apache-2.0" + +[features] +default = ["deny_sensitive_logging"] +# Disable sensitive logging. +deny_sensitive_logging = ["oak_functions_service/deny_sensitive_logging"] + +[dependencies] +anyhow = { version = "*", default-features = false } +prost = { workspace = true } +micro_rpc = { workspace = true } +oak_attestation = { workspace = true } +oak_core = { workspace = true } +oak_crypto = { workspace = true } +oak_dice = { workspace = true } +oak_restricted_kernel_sdk = { workspace = true } +oak_functions_service = { workspace = true } +oak_proto_rust = { workspace = true } +log = "*" + +[dev-dependencies] +env_logger = { version = "*", default-features = false } +oak_attestation = { workspace = true } +oak_functions_test_utils = { workspace = true } +oak_restricted_kernel_sdk = { workspace = true, features = [ + "mock_attestation" +] } diff --git a/oak_functions_enclave_service/src/lib.rs b/oak_functions_enclave_service/src/lib.rs new file mode 100644 index 00000000000..e9bbbd48dba --- /dev/null +++ b/oak_functions_enclave_service/src/lib.rs @@ -0,0 +1,224 @@ +// +// Copyright 2022 The Project Oak Authors +// +// Licensed 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. +// + +#![no_std] + +extern crate alloc; + +use alloc::{format, string::ToString, sync::Arc, vec::Vec}; + +use oak_attestation::handler::EncryptionHandler; +use oak_core::sync::OnceCell; +use oak_crypto::encryptor::EncryptionKeyHandle; +pub use oak_functions_service::proto; +use oak_functions_service::{ + instance::OakFunctionsInstance, + proto::oak::functions::{ + AbortNextLookupDataResponse, Empty, ExtendNextLookupDataRequest, + ExtendNextLookupDataResponse, FinishNextLookupDataRequest, FinishNextLookupDataResponse, + InitializeRequest, InitializeResponse, InvokeRequest, InvokeResponse, LookupDataChunk, + OakFunctions, PublicKeyInfo, ReserveRequest, ReserveResponse, + }, + Observer, +}; +use prost::Message; + +pub struct OakFunctionsService< + EKH: EncryptionKeyHandle + 'static, + EP: oak_restricted_kernel_sdk::EvidenceProvider, +> { + evidence_provider: EP, + encryption_key_handle: Arc, + instance: OnceCell, + observer: Option>, +} + +impl + OakFunctionsService +{ + pub fn new( + evidence_provider: EP, + encryption_key_handle: Arc, + observer: Option>, + ) -> Self { + Self { + evidence_provider, + encryption_key_handle, + instance: OnceCell::new(), + observer, + } + } + fn get_instance(&self) -> Result<&OakFunctionsInstance, micro_rpc::Status> { + self.instance.get().ok_or_else(|| { + micro_rpc::Status::new_with_message( + micro_rpc::StatusCode::FailedPrecondition, + "not initialized", + ) + }) + } + /// Extract the public key without verifying. This function is intended as a temporary + /// fix to allow the application to start using the oak SDK, while it still + /// requires the ability to extract the attestation public key, and to send it + /// directly to the client. + // TODO(#4571): Remove this function once the client has been updated to use + // the verification library. At this point the evidence will just be directly + // sent to the client, which extracts the public key from it upon succesful + // verification. + fn get_encryption_public_key(&self) -> Result, anyhow::Error> { + let encryption_claims = self + .evidence_provider + .get_evidence() + .application_keys + .claims() + .map_err(anyhow::Error::msg)?; + let encryption_cose_key = + oak_dice::cert::get_public_key_from_claims_set(&encryption_claims) + .map_err(anyhow::Error::msg)?; + oak_dice::cert::cose_key_to_hpke_public_key(&encryption_cose_key) + .map_err(anyhow::Error::msg) + } +} + +impl + OakFunctions for OakFunctionsService +{ + fn initialize( + &self, + request: InitializeRequest, + ) -> Result { + log::debug!( + "called initialize (Wasm module size: {} bytes)", + request.wasm_module.len() + ); + match self.instance.get() { + Some(_) => Err(micro_rpc::Status::new_with_message( + micro_rpc::StatusCode::FailedPrecondition, + "already initialized", + )), + None => { + let instance = OakFunctionsInstance::new(&request, self.observer.clone())?; + if self.instance.set(instance).is_err() { + return Err(micro_rpc::Status::new_with_message( + micro_rpc::StatusCode::FailedPrecondition, + "already initialized", + )); + } + let public_key = self.get_encryption_public_key().map_err(|err| { + micro_rpc::Status::new_with_message( + micro_rpc::StatusCode::Internal, + format!("failed to get encryption public key: {err}"), + ) + })?; + let evidence = oak_attestation::proto::oak::attestation::v1::Evidence::try_from( + self.evidence_provider.get_evidence().clone(), + ) + .map_err(|err| { + micro_rpc::Status::new_with_message( + micro_rpc::StatusCode::Internal, + format!("failed to convert evidence to proto: {err}"), + ) + })?; + #[allow(deprecated)] + Ok(InitializeResponse { + public_key_info: Some(PublicKeyInfo { + attestation: Vec::new(), + public_key, + }), + evidence: Some(evidence), + }) + } + } + } + + fn handle_user_request( + &self, + request: InvokeRequest, + ) -> Result { + log::debug!("called handle_user_request"); + let encryption_key_handle = self.encryption_key_handle.clone(); + let instance = self.get_instance()?; + + let encrypted_request = request.encrypted_request.ok_or_else(|| { + micro_rpc::Status::new_with_message( + micro_rpc::StatusCode::InvalidArgument, + "InvokeRequest doesn't contain an encrypted request".to_string(), + ) + })?; + + EncryptionHandler::create(encryption_key_handle, |r| { + // Wrap the invocation result (which may be an Error) into a micro RPC Response + // wrapper protobuf, and encode that as bytes. + let response_result: Result, micro_rpc::Status> = + instance.handle_user_request(r); + let response: micro_rpc::ResponseWrapper = response_result.into(); + response.encode_to_vec() + }) + .invoke(&encrypted_request) + .map( + #[allow(clippy::needless_update)] + |encrypted_response| InvokeResponse { + encrypted_response: Some(encrypted_response), + ..Default::default() + }, + ) + .map_err(|err| { + micro_rpc::Status::new_with_message( + micro_rpc::StatusCode::Internal, + format!("couldn't call handle_user_request handler: {:?}", err), + ) + }) + } + + fn extend_next_lookup_data( + &self, + request: ExtendNextLookupDataRequest, + ) -> Result { + log::debug!( + "called extend_next_lookup_data (items: {})", + request.chunk.as_ref().map(|c| c.items.len()).unwrap_or(0) + ); + self.get_instance()?.extend_next_lookup_data(request) + } + + fn finish_next_lookup_data( + &self, + request: FinishNextLookupDataRequest, + ) -> Result { + log::debug!("called finish_next_lookup_data"); + self.get_instance()?.finish_next_lookup_data(request) + } + + fn abort_next_lookup_data( + &self, + request: Empty, + ) -> Result { + log::debug!("called abort_next_lookup_data"); + self.get_instance()?.abort_next_lookup_data(request) + } + + fn stream_lookup_data( + &self, + request: LookupDataChunk, + ) -> Result { + let instance = self.get_instance()?; + instance.extend_lookup_data_chunk(request); + instance.finish_next_lookup_data(FinishNextLookupDataRequest {}) + } + + fn reserve(&self, request: ReserveRequest) -> Result { + self.get_instance()?.reserve(request) + } +} diff --git a/oak_functions_service/tests/integration_test.rs b/oak_functions_enclave_service/tests/integration_test.rs similarity index 98% rename from oak_functions_service/tests/integration_test.rs rename to oak_functions_enclave_service/tests/integration_test.rs index ba6e6b0edfa..5887d82f10e 100644 --- a/oak_functions_service/tests/integration_test.rs +++ b/oak_functions_enclave_service/tests/integration_test.rs @@ -19,11 +19,11 @@ extern crate alloc; +use alloc::sync::Arc; use core::assert_matches::assert_matches; -use std::sync::Arc; use oak_crypto::{encryptor::ClientEncryptor, proto::oak::crypto::v1::EncryptedRequest}; -use oak_functions_service::{ +use oak_functions_enclave_service::{ proto::oak::functions::{ ExtendNextLookupDataRequest, FinishNextLookupDataRequest, InitializeRequest, InvokeRequest, LookupDataChunk, LookupDataEntry, OakFunctionsClient, OakFunctionsServer, @@ -147,8 +147,8 @@ fn it_should_only_initialize_once() { ); } -#[tokio::test] -async fn it_should_error_on_invalid_wasm_module() { +#[test] +fn it_should_error_on_invalid_wasm_module() { init(); let service = new_service_for_testing(); let mut client = OakFunctionsClient::new(OakFunctionsServer::new(service)); @@ -221,8 +221,8 @@ async fn it_should_error_on_invalid_wasm_module() { ); } -#[tokio::test] -async fn it_should_support_lookup_data() { +#[test] +fn it_should_support_lookup_data() { init(); let service = new_service_for_testing(); let mut client = OakFunctionsClient::new(OakFunctionsServer::new(service)); @@ -301,8 +301,8 @@ async fn it_should_support_lookup_data() { assert_eq!(LOOKUP_TEST_VALUE, response_result.unwrap()); } -#[tokio::test] -async fn it_should_handle_wasm_panic() { +#[test] +fn it_should_handle_wasm_panic() { init(); let service = new_service_for_testing(); let mut client = OakFunctionsClient::new(OakFunctionsServer::new(service)); diff --git a/oak_functions_sdk/Cargo.toml b/oak_functions_sdk/Cargo.toml index 22ee48d19ac..990faf66d02 100644 --- a/oak_functions_sdk/Cargo.toml +++ b/oak_functions_sdk/Cargo.toml @@ -14,7 +14,6 @@ prost = { workspace = true } micro_rpc_build = { workspace = true } [dev-dependencies] -hashbrown = "*" lazy_static = "*" oak_functions_test_utils = { workspace = true } oak_functions_service = { workspace = true } diff --git a/oak_functions_sdk/tests/integration_test.rs b/oak_functions_sdk/tests/integration_test.rs index 847c783b5c9..02efce7658d 100644 --- a/oak_functions_sdk/tests/integration_test.rs +++ b/oak_functions_sdk/tests/integration_test.rs @@ -16,7 +16,6 @@ use std::{path::PathBuf, sync::Arc}; -use hashbrown::HashMap; use lazy_static::lazy_static; use oak_functions_abi::{Request, Response}; use oak_functions_service::{ @@ -51,7 +50,8 @@ lazy_static! { #[tokio::test] async fn test_read_write() { let logger = Arc::new(StandaloneLogger); - let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); + let lookup_data_manager = + Arc::new(LookupDataManager::for_test(Data::default(), logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, }; @@ -74,7 +74,8 @@ async fn test_read_write() { #[tokio::test] async fn test_double_read() { let logger = Arc::new(StandaloneLogger); - let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); + let lookup_data_manager = + Arc::new(LookupDataManager::for_test(Data::default(), logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, }; @@ -97,7 +98,8 @@ async fn test_double_read() { #[tokio::test] async fn test_double_write() { let logger = Arc::new(StandaloneLogger); - let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); + let lookup_data_manager = + Arc::new(LookupDataManager::for_test(Data::default(), logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, }; @@ -120,7 +122,8 @@ async fn test_double_write() { #[tokio::test] async fn test_write_log() { let logger = Arc::new(StandaloneLogger); - let lookup_data_manager = Arc::new(LookupDataManager::for_test(HashMap::new(), logger.clone())); + let lookup_data_manager = + Arc::new(LookupDataManager::for_test(Data::default(), logger.clone())); let api_factory = StdWasmApiFactory { lookup_data_manager, }; @@ -142,7 +145,7 @@ async fn test_write_log() { #[tokio::test] async fn test_storage_get_item() { - let entries = HashMap::from_iter([( + let entries = Data::from_iter([( b"StorageGet".to_vec().into(), b"StorageGetResponse".to_vec().into(), )]); @@ -171,7 +174,7 @@ async fn test_storage_get_item() { #[tokio::test] async fn test_storage_get_item_not_found() { // empty lookup data, no key will be found - let entries = HashMap::new(); + let entries = Data::new(); let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(entries, logger.clone())); @@ -198,7 +201,7 @@ async fn test_storage_get_item_not_found() { #[ignore] async fn test_storage_get_item_huge_key() { let bytes: Vec = vec![42u8; 1 << 20]; - let entries = HashMap::from_iter([(bytes.clone().into(), bytes.clone().into())]); + let entries = Data::from_iter([(bytes.clone().into(), bytes.clone().into())]); let logger = Arc::new(StandaloneLogger); let lookup_data_manager = Arc::new(LookupDataManager::for_test(entries, logger.clone())); diff --git a/oak_functions_service/Cargo.toml b/oak_functions_service/Cargo.toml index b31ace12d6f..f87b691a5e0 100644 --- a/oak_functions_service/Cargo.toml +++ b/oak_functions_service/Cargo.toml @@ -24,13 +24,11 @@ log = "*" prost = { workspace = true } micro_rpc = { workspace = true } oak_attestation = { workspace = true } -oak_core = { workspace = true } oak_crypto = { workspace = true } oak_dice = { workspace = true } oak_functions_abi = { workspace = true } oak_functions_sdk = { workspace = true } oak_proto_rust = { workspace = true } -oak_restricted_kernel_sdk = { workspace = true } spinning_top = "*" wasmi = { version = "*", default-features = false } @@ -39,11 +37,7 @@ micro_rpc_build = { workspace = true } [dev-dependencies] criterion = "*" -env_logger = { version = "*", default-features = false } oak_attestation = { workspace = true } oak_functions_test_utils = { workspace = true } rand = "*" tokio = { workspace = true, features = ["rt", "macros"] } -oak_restricted_kernel_sdk = { workspace = true, features = [ - "mock_attestation" -] } diff --git a/oak_functions_service/src/lib.rs b/oak_functions_service/src/lib.rs index 0b4ffb6b923..7ab1515e337 100644 --- a/oak_functions_service/src/lib.rs +++ b/oak_functions_service/src/lib.rs @@ -42,208 +42,7 @@ pub mod logger; pub mod lookup; pub mod wasm; -use alloc::{format, string::ToString, sync::Arc, vec::Vec}; - -use instance::OakFunctionsInstance; -use oak_attestation::handler::EncryptionHandler; -use oak_core::sync::OnceCell; -use oak_crypto::encryptor::EncryptionKeyHandle; -use prost::Message; -use proto::oak::functions::{ - AbortNextLookupDataResponse, Empty, ExtendNextLookupDataRequest, ExtendNextLookupDataResponse, - FinishNextLookupDataRequest, FinishNextLookupDataResponse, InitializeRequest, - InitializeResponse, InvokeRequest, InvokeResponse, LookupDataChunk, OakFunctions, - PublicKeyInfo, ReserveRequest, ReserveResponse, -}; - pub trait Observer { fn wasm_initialization(&self, duration: core::time::Duration); fn wasm_invocation(&self, duration: core::time::Duration); } - -pub struct OakFunctionsService< - EKH: EncryptionKeyHandle + 'static, - EP: oak_restricted_kernel_sdk::EvidenceProvider, -> { - evidence_provider: EP, - encryption_key_handle: Arc, - instance: OnceCell, - observer: Option>, -} - -impl - OakFunctionsService -{ - pub fn new( - evidence_provider: EP, - encryption_key_handle: Arc, - observer: Option>, - ) -> Self { - Self { - evidence_provider, - encryption_key_handle, - instance: OnceCell::new(), - observer, - } - } - fn get_instance(&self) -> Result<&OakFunctionsInstance, micro_rpc::Status> { - self.instance.get().ok_or_else(|| { - micro_rpc::Status::new_with_message( - micro_rpc::StatusCode::FailedPrecondition, - "not initialized", - ) - }) - } - /// Extract the public key without verifying. This function is intended as a temporary - /// fix to allow the application to start using the oak SDK, while it still - /// requires the ability to extract the attestation public key, and to send it - /// directly to the client. - // TODO(#4571): Remove this function once the client has been updated to use - // the verification library. At this point the evidence will just be directly - // sent to the client, which extracts the public key from it upon succesful - // verification. - fn get_encryption_public_key(&self) -> Result, anyhow::Error> { - let encryption_claims = self - .evidence_provider - .get_evidence() - .application_keys - .claims() - .map_err(anyhow::Error::msg)?; - let encryption_cose_key = - oak_dice::cert::get_public_key_from_claims_set(&encryption_claims) - .map_err(anyhow::Error::msg)?; - oak_dice::cert::cose_key_to_hpke_public_key(&encryption_cose_key) - .map_err(anyhow::Error::msg) - } -} - -impl - OakFunctions for OakFunctionsService -{ - fn initialize( - &self, - request: InitializeRequest, - ) -> Result { - log::debug!( - "called initialize (Wasm module size: {} bytes)", - request.wasm_module.len() - ); - match self.instance.get() { - Some(_) => Err(micro_rpc::Status::new_with_message( - micro_rpc::StatusCode::FailedPrecondition, - "already initialized", - )), - None => { - let instance = OakFunctionsInstance::new(&request, self.observer.clone())?; - if self.instance.set(instance).is_err() { - return Err(micro_rpc::Status::new_with_message( - micro_rpc::StatusCode::FailedPrecondition, - "already initialized", - )); - } - let public_key = self.get_encryption_public_key().map_err(|err| { - micro_rpc::Status::new_with_message( - micro_rpc::StatusCode::Internal, - format!("failed to get encryption public key: {err}"), - ) - })?; - let evidence = oak_attestation::proto::oak::attestation::v1::Evidence::try_from( - self.evidence_provider.get_evidence().clone(), - ) - .map_err(|err| { - micro_rpc::Status::new_with_message( - micro_rpc::StatusCode::Internal, - format!("failed to convert evidence to proto: {err}"), - ) - })?; - #[allow(deprecated)] - Ok(InitializeResponse { - public_key_info: Some(PublicKeyInfo { - attestation: Vec::new(), - public_key, - }), - evidence: Some(evidence), - }) - } - } - } - - fn handle_user_request( - &self, - request: InvokeRequest, - ) -> Result { - log::debug!("called handle_user_request"); - let encryption_key_handle = self.encryption_key_handle.clone(); - let instance = self.get_instance()?; - - let encrypted_request = request.encrypted_request.ok_or_else(|| { - micro_rpc::Status::new_with_message( - micro_rpc::StatusCode::InvalidArgument, - "InvokeRequest doesn't contain an encrypted request".to_string(), - ) - })?; - - EncryptionHandler::create(encryption_key_handle, |r| { - // Wrap the invocation result (which may be an Error) into a micro RPC Response - // wrapper protobuf, and encode that as bytes. - let response_result: Result, micro_rpc::Status> = - instance.handle_user_request(r); - let response: micro_rpc::ResponseWrapper = response_result.into(); - response.encode_to_vec() - }) - .invoke(&encrypted_request) - .map( - #[allow(clippy::needless_update)] - |encrypted_response| InvokeResponse { - encrypted_response: Some(encrypted_response), - ..Default::default() - }, - ) - .map_err(|err| { - micro_rpc::Status::new_with_message( - micro_rpc::StatusCode::Internal, - format!("couldn't call handle_user_request handler: {:?}", err), - ) - }) - } - - fn extend_next_lookup_data( - &self, - request: ExtendNextLookupDataRequest, - ) -> Result { - log::debug!( - "called extend_next_lookup_data (items: {})", - request.chunk.as_ref().map(|c| c.items.len()).unwrap_or(0) - ); - self.get_instance()?.extend_next_lookup_data(request) - } - - fn finish_next_lookup_data( - &self, - request: FinishNextLookupDataRequest, - ) -> Result { - log::debug!("called finish_next_lookup_data"); - self.get_instance()?.finish_next_lookup_data(request) - } - - fn abort_next_lookup_data( - &self, - request: Empty, - ) -> Result { - log::debug!("called abort_next_lookup_data"); - self.get_instance()?.abort_next_lookup_data(request) - } - - fn stream_lookup_data( - &self, - request: LookupDataChunk, - ) -> Result { - let instance = self.get_instance()?; - instance.extend_lookup_data_chunk(request); - instance.finish_next_lookup_data(FinishNextLookupDataRequest {}) - } - - fn reserve(&self, request: ReserveRequest) -> Result { - self.get_instance()?.reserve(request) - } -} From 64335f35581130a0c2b7e8d975f89b601aeffd0d Mon Sep 17 00:00:00 2001 From: jul-sh Date: Thu, 25 Jan 2024 20:53:57 -0500 Subject: [PATCH 40/42] Implement Signature verification in oak crypto (#4683) * Implement Signature verification in oak crypto * appease clippy * address style comments * don't add additional deps on oak_dice, as we want to eventually remove oak_crypto's dep on it --- oak_crypto/src/lib.rs | 1 + oak_crypto/src/signer.rs | 2 -- oak_crypto/src/tests.rs | 31 ++++++++++++++++++++++++++++ oak_crypto/src/verifier.rs | 42 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 oak_crypto/src/verifier.rs diff --git a/oak_crypto/src/lib.rs b/oak_crypto/src/lib.rs index ba3fc9987d3..024272152c3 100644 --- a/oak_crypto/src/lib.rs +++ b/oak_crypto/src/lib.rs @@ -37,3 +37,4 @@ pub mod hpke; pub mod signer; #[cfg(test)] mod tests; +pub mod verifier; diff --git a/oak_crypto/src/signer.rs b/oak_crypto/src/signer.rs index 155d6e195f7..60901e2084d 100644 --- a/oak_crypto/src/signer.rs +++ b/oak_crypto/src/signer.rs @@ -16,8 +16,6 @@ pub use crate::proto::oak::crypto::v1::Signature; -// TODO(#3836): Implement signature verification. - pub trait Signer { fn sign(&self, message: &[u8]) -> Signature; } diff --git a/oak_crypto/src/tests.rs b/oak_crypto/src/tests.rs index d32a734d923..656347936e1 100644 --- a/oak_crypto/src/tests.rs +++ b/oak_crypto/src/tests.rs @@ -265,3 +265,34 @@ async fn test_async_encryptor() { assert_eq!(TEST_RESPONSE_MESSAGE, decrypted_response); assert_eq!(TEST_RESPONSE_ASSOCIATED_DATA, response_associated_data); } + +const TEST_SIGNATURE_MESSAGE_ONE: &[u8] = b"Dogs are the best"; +const TEST_SIGNATURE_MESSAGE_TWO: &[u8] = b"Cats are even better"; + +use crate::{signer::Signer, verifier::Verifier}; + +#[test] +fn test_signer_and_verifier() { + let signing_key_one = p256::ecdsa::SigningKey::random(&mut rand_core::OsRng); + + let signature = signing_key_one.sign(TEST_SIGNATURE_MESSAGE_ONE); + + let verifying_key_one = p256::ecdsa::VerifyingKey::from(signing_key_one); + + assert!(verifying_key_one + .verify(TEST_SIGNATURE_MESSAGE_ONE, &signature) + .is_ok()); + + assert!(verifying_key_one + .verify(TEST_SIGNATURE_MESSAGE_TWO, &signature) + .is_err()); + + let verifying_key_two = { + let signing_key_two = p256::ecdsa::SigningKey::random(&mut rand_core::OsRng); + p256::ecdsa::VerifyingKey::from(signing_key_two) + }; + + assert!(verifying_key_two + .verify(TEST_SIGNATURE_MESSAGE_TWO, &signature) + .is_err()); +} diff --git a/oak_crypto/src/verifier.rs b/oak_crypto/src/verifier.rs new file mode 100644 index 00000000000..53ff02d9721 --- /dev/null +++ b/oak_crypto/src/verifier.rs @@ -0,0 +1,42 @@ +// +// Copyright 2023 The Project Oak Authors +// +// Licensed 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. +// + +use anyhow::Context; + +pub trait Verifier { + fn verify(&self, message: &[u8], signature: &crate::signer::Signature) -> anyhow::Result<()>; +} + +impl Verifier for p256::ecdsa::VerifyingKey { + fn verify(&self, message: &[u8], signature: &crate::signer::Signature) -> anyhow::Result<()> { + let parsed_signature = p256::ecdsa::Signature::from_slice(&signature.signature) + .map_err(anyhow::Error::msg) + .context("could not parse signature")?; + >::verify(self, message, &parsed_signature).map_err(anyhow::Error::msg) + } +} + +struct VerifierKeyHandle { + inner: p256::ecdsa::VerifyingKey, +} + +impl Verifier for VerifierKeyHandle { + fn verify(&self, message: &[u8], signature: &crate::signer::Signature) -> anyhow::Result<()> { + self.inner.verify(message, signature) + } +} From 9c491aad300e4e7b1479edb89d5b4239503b440c Mon Sep 17 00:00:00 2001 From: jul-sh Date: Fri, 26 Jan 2024 10:24:20 -0500 Subject: [PATCH 41/42] Make oak_restricted_kernel_wrapper configurable (#4720) * Make oak_restricted_kernel_wrapper configurable One can now specify which kernel binary should be wrapped using feature flags. * ensure a default feature is set, so clippy does not panic * address comments * remove extra conditional * improve spelling * ensure the build does not fail if clippy enables all features * fix typo --- justfile | 5 ++- oak_restricted_kernel_wrapper/Cargo.toml | 5 +++ oak_restricted_kernel_wrapper/build.rs | 48 +++++++++++++++++------- 3 files changed, 44 insertions(+), 14 deletions(-) diff --git a/justfile b/justfile index 93b7f13b21e..3afb6be9f99 100644 --- a/justfile +++ b/justfile @@ -22,11 +22,14 @@ oak_functions_insecure_enclave_app: oak_restricted_kernel_bin: env --chdir=oak_restricted_kernel_bin cargo build --release --bin=oak_restricted_kernel_bin +oak_restricted_kernel_wrapper: oak_restricted_kernel_bin + env --chdir=oak_restricted_kernel_wrapper cargo objcopy --release --features=oak_restricted_kernel_bin -- --output-target=binary target/x86_64-unknown-none/release/oak_restricted_kernel_wrapper_bin + oak_restricted_kernel_simple_io_bin: env --chdir=oak_restricted_kernel_bin cargo build --release --no-default-features --features=simple_io_channel --bin=oak_restricted_kernel_simple_io_bin oak_restricted_kernel_simple_io_wrapper: oak_restricted_kernel_simple_io_bin - env --chdir=oak_restricted_kernel_wrapper cargo objcopy --release -- --output-target=binary target/x86_64-unknown-none/release/oak_restricted_kernel_simple_io_wrapper_bin + env --chdir=oak_restricted_kernel_wrapper cargo objcopy --release --no-default-features --features=oak_restricted_kernel_simple_io_bin -- --output-target=binary target/x86_64-unknown-none/release/oak_restricted_kernel_simple_io_wrapper_bin stage0_bin: env --chdir=stage0_bin cargo objcopy --release -- --output-target=binary target/x86_64-unknown-none/release/stage0_bin diff --git a/oak_restricted_kernel_wrapper/Cargo.toml b/oak_restricted_kernel_wrapper/Cargo.toml index 279037edfd7..df824e957dc 100644 --- a/oak_restricted_kernel_wrapper/Cargo.toml +++ b/oak_restricted_kernel_wrapper/Cargo.toml @@ -5,6 +5,11 @@ authors = ["Conrad Grobler "] edition = "2021" license = "Apache-2.0" +[features] +default = ["oak_restricted_kernel_bin"] +oak_restricted_kernel_simple_io_bin = [] +oak_restricted_kernel_bin = [] + [workspace] resolver = "2" members = ["."] diff --git a/oak_restricted_kernel_wrapper/build.rs b/oak_restricted_kernel_wrapper/build.rs index 75014a6add0..d555857f775 100644 --- a/oak_restricted_kernel_wrapper/build.rs +++ b/oak_restricted_kernel_wrapper/build.rs @@ -20,11 +20,15 @@ use std::{ path::PathBuf, }; -fn main() { - println!("cargo:rerun-if-changed=layout.ld"); - println!("cargo:rustc-link-arg=--script=layout.ld"); +// returns source_path if it can be constructed and if it points to a valid file +fn try_source_path() -> Result { let kernel_directory = "oak_restricted_kernel_bin"; - let file_name = "oak_restricted_kernel_simple_io_bin"; + let file_name = match (cfg!(feature = "oak_restricted_kernel_bin"), cfg!(feature = "oak_restricted_kernel_simple_io_bin")) { + (true, false) => Ok("oak_restricted_kernel_bin"), + (false, true) => Ok("oak_restricted_kernel_simple_io_bin"), + (true, true) => Err("Feature oak_restricted_kernel_simple_io_bin and feature oak_restricted_kernel_bin cannot be enabled at the same time. Only either version can be built."), + (false, false) => Err("One of feature oak_restricted_kernel_simple_io_bin or feature oak_restricted_kernel_bin must be enabled.") + }?; // The source file is the output from building "../oak_restricted_kernel_bin" in release mode. let mut source_path = PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); @@ -32,16 +36,34 @@ fn main() { source_path.push(kernel_directory); source_path.push("target/x86_64-unknown-none/release"); source_path.push(file_name); + match source_path.exists() { + true => Ok(source_path), + false => Err("contructed source_path does not exist"), + } +} + +fn main() { + println!("cargo:rerun-if-changed=layout.ld"); + println!("cargo:rustc-link-arg=--script=layout.ld"); let mut destination_path = PathBuf::from(std::env::var("OUT_DIR").unwrap()); - destination_path.push(file_name); - if source_path.exists() { - copy(&source_path, &destination_path).unwrap(); - } else { - // Create a fake file so cargo clippy doesn't break if the kernel was not built. - File::create(&destination_path) - .unwrap() - .write_all(b"invalid") - .unwrap(); + match try_source_path() { + Ok(source_path) => { + destination_path.push(source_path.file_name().unwrap()); + copy(&source_path, &destination_path).unwrap(); + } + Err(msg) => { + let msg = format!( + "Kernel could not be built! An error occured when building: {}", + msg + ); + println!("cargo:warning={}", msg.as_str()); + // Create a fake file so cargo clippy doesn't break if the kernel was not built. + destination_path.push("invalid_build"); + File::create(&destination_path) + .unwrap() + .write_all(msg.as_bytes()) + .unwrap(); + } } println!( From 2805871eb1a6178d465387ab92cc2fa8ea304886 Mon Sep 17 00:00:00 2001 From: jblebrun Date: Fri, 26 Jan 2024 17:05:50 +0000 Subject: [PATCH 42/42] Add buildconfigs for containers stage1 and kernel (#4692) It's been a little trickier to get the system image provenance build working, so that will be in a follow up PR. --- .github/workflows/provenance.yaml | 5 +++- .../oak_containers_kernel_bzimage.toml | 13 ++++++++++ buildconfigs/oak_containers_stage1_cpio.toml | 13 ++++++++++ flake.nix | 25 +++++++++++++++++++ 4 files changed, 55 insertions(+), 1 deletion(-) create mode 100644 buildconfigs/oak_containers_kernel_bzimage.toml create mode 100644 buildconfigs/oak_containers_stage1_cpio.toml diff --git a/.github/workflows/provenance.yaml b/.github/workflows/provenance.yaml index 00e29027123..53941ecbe43 100644 --- a/.github/workflows/provenance.yaml +++ b/.github/workflows/provenance.yaml @@ -19,17 +19,20 @@ jobs: if: | github.event_name == 'push' || contains(github.event.pull_request.labels.*.name, 'provenance:force-run') + # We use the same job template to generate provenances for multiple binaries. strategy: fail-fast: false matrix: buildconfig: - buildconfigs/key_xor_test_app.toml + - buildconfigs/oak_containers_kernel_bzimage.toml + - buildconfigs/oak_containers_stage1_cpio.toml - buildconfigs/oak_echo_enclave_app.toml - buildconfigs/oak_echo_raw_enclave_app.toml - - buildconfigs/oak_restricted_kernel_simple_io_wrapper.toml - buildconfigs/oak_functions_enclave_app.toml - buildconfigs/oak_functions_insecure_enclave_app.toml + - buildconfigs/oak_restricted_kernel_simple_io_wrapper.toml - buildconfigs/stage0_bin.toml permissions: diff --git a/buildconfigs/oak_containers_kernel_bzimage.toml b/buildconfigs/oak_containers_kernel_bzimage.toml new file mode 100644 index 00000000000..70f23ebd56d --- /dev/null +++ b/buildconfigs/oak_containers_kernel_bzimage.toml @@ -0,0 +1,13 @@ +# This is the static build configuration that we use with the docker-based SLSA3 generator for +# building the `oak_containers_kernel` binary, and its provenance. +# See https://github.com/slsa-framework/slsa-github-generator/tree/main/internal/builders/docker. +command = [ + "nix", + "develop", + ".#bzImageProvenance", + "--command", + "env", + "--chdir=oak_containers_kernel", + "make", +] +artifact_path = "./oak_containers_kernel/target/bzImage" diff --git a/buildconfigs/oak_containers_stage1_cpio.toml b/buildconfigs/oak_containers_stage1_cpio.toml new file mode 100644 index 00000000000..7208a5231e4 --- /dev/null +++ b/buildconfigs/oak_containers_stage1_cpio.toml @@ -0,0 +1,13 @@ +# This is the static build configuration that we use with the docker-based SLSA3 generator for +# building the `stage1` binary, and its provenance. +# See https://github.com/slsa-framework/slsa-github-generator/tree/main/internal/builders/docker. +command = [ + "nix", + "develop", + ".#stage1Provenance", + "--command", + "env", + "--chdir=oak_containers_stage1", + "make", +] +artifact_path = "./target/stage1.cpio" diff --git a/flake.nix b/flake.nix index 08420ac6842..1b4638c0cdc 100644 --- a/flake.nix +++ b/flake.nix @@ -173,6 +173,31 @@ umoci ]; }; + # Shell for container kernel image provenance workflow. + bzImageProvenance = with pkgs; mkShell { + inputsFrom = [ + rust + ]; + packages = [ + bc + bison + curl + elfutils + flex + libelf + ]; + }; + # Shell for container stage 1 image provenance workflow. + stage1Provenance = with pkgs; mkShell { + inputsFrom = [ + rust + ]; + packages = [ + cpio + glibc + glibc.static + ]; + }; # Shell for most CI steps (i.e. without contaniners support). ci = pkgs.mkShell { inputsFrom = [