diff --git a/examples/aggregator/client/aggregator.cc b/examples/aggregator/client/aggregator.cc index 42e84b7e2ec..4a925c70e8d 100644 --- a/examples/aggregator/client/aggregator.cc +++ b/examples/aggregator/client/aggregator.cc @@ -78,7 +78,7 @@ int main(int argc, char** argv) { // // The particular value corresponds to the hash on the `aggregator.wasm` line in // https://github.com/project-oak/oak/blob/hashes/reproducibility_index. - oak::label::Label label = oak::WebAssemblyModuleLabel( + oak::label::Label label = oak::WebAssemblyModuleHashLabel( absl::HexStringToBytes("c995a15ab48aa7e091b65e8bb8d7b67c71f4a10c236b8eaa0599fe7439053530")); // Connect to the Oak Application. auto stub = Aggregator::NewStub(oak::ApplicationClient::CreateChannel( diff --git a/examples/private_set_intersection/client/private_set_intersection.cc b/examples/private_set_intersection/client/private_set_intersection.cc index a22c6f30127..1b846ac153b 100644 --- a/examples/private_set_intersection/client/private_set_intersection.cc +++ b/examples/private_set_intersection/client/private_set_intersection.cc @@ -25,22 +25,20 @@ ABSL_FLAG(std::string, address, "localhost:8080", "Address of the Oak application to connect to"); ABSL_FLAG(std::string, ca_cert, "", "Path to the PEM-encoded CA root certificate"); +ABSL_FLAG(std::string, public_key, "", "Path to the PEM-encoded public key used as a data label"); using ::oak::examples::private_set_intersection::GetIntersectionResponse; using ::oak::examples::private_set_intersection::PrivateSetIntersection; using ::oak::examples::private_set_intersection::SubmitSetRequest; -void SubmitSet(PrivateSetIntersection::Stub* stub, std::vector set) { +grpc::Status SubmitSet(PrivateSetIntersection::Stub* stub, std::vector set) { grpc::ClientContext context; SubmitSetRequest request; for (auto item : set) { request.add_values(item); } google::protobuf::Empty response; - grpc::Status status = stub->SubmitSet(&context, request, &response); - if (!status.ok()) { - LOG(FATAL) << "Could not submit set: " << status.error_code() << ": " << status.error_message(); - } + return stub->SubmitSet(&context, request, &response); } std::vector RetrieveIntersection(PrivateSetIntersection::Stub* stub) { @@ -68,7 +66,8 @@ int main(int argc, char** argv) { // TODO(#1066): Use a more restrictive Label, based on a bearer token shared between the two // clients. - oak::label::Label label = oak::PublicUntrustedLabel(); + std::string public_key = oak::ApplicationClient::LoadPublicKey(absl::GetFlag(FLAGS_public_key)); + oak::label::Label label = oak::WebAssemblyModuleSignatureLabel(public_key); auto stub_0 = PrivateSetIntersection::NewStub(oak::ApplicationClient::CreateChannel( address, oak::ApplicationClient::GetTlsChannelCredentials(ca_cert), label)); @@ -78,14 +77,37 @@ int main(int argc, char** argv) { // Submit sets from different clients. std::vector set_0{"a", "b", "c"}; - SubmitSet(stub_0.get(), set_0); + auto submit_status_0 = SubmitSet(stub_0.get(), set_0); + if (!submit_status_0.ok()) { + LOG(FATAL) << "Could not submit set: " << submit_status_0.error_code() << ": " + << submit_status_0.error_message(); + } std::vector set_1{"b", "c", "d"}; - SubmitSet(stub_1.get(), set_1); + auto submit_status_1 = SubmitSet(stub_1.get(), set_1); + if (!submit_status_1.ok()) { + LOG(FATAL) << "Could not submit set: " << submit_status_1.error_code() << ": " + << submit_status_1.error_message(); + } - std::set expected_set{"b", "c"}; + // Use an invalid public key. + std::string invalid_public_key_base64 = "vpxqTZOUq1FjcaB9uJYCuv4kAg+AsgMwubA6WE+2pmk="; + std::string invalid_public_key; + if (!absl::Base64Unescape(invalid_public_key_base64, &invalid_public_key)) { + LOG(FATAL) << "Could not decode public key: " << invalid_public_key_base64; + } + oak::label::Label invalid_label = oak::WebAssemblyModuleSignatureLabel(invalid_public_key); + auto invalid_stub = PrivateSetIntersection::NewStub(oak::ApplicationClient::CreateChannel( + address, oak::ApplicationClient::GetTlsChannelCredentials(ca_cert), invalid_label)); + std::vector set_2{"c", "d", "e"}; + auto submit_status_2 = SubmitSet(invalid_stub.get(), set_2); + // Error code `3` means `could not process gRPC request`. + if (submit_status_2.error_code() != 3) { + LOG(FATAL) << "Invalid public key was accepted"; + } // Retrieve intersection. + std::set expected_set{"b", "c"}; std::vector intersection_0 = RetrieveIntersection(stub_0.get()); LOG(INFO) << "client 0 intersection:"; for (auto item : intersection_0) { diff --git a/examples/private_set_intersection/example.toml b/examples/private_set_intersection/example.toml index 17dd1275a89..ab5e92e9c92 100644 --- a/examples/private_set_intersection/example.toml +++ b/examples/private_set_intersection/example.toml @@ -9,5 +9,12 @@ out = "examples/private_set_intersection/bin/private_set_intersection.oak" [applications.rust.modules] module = { Cargo = { cargo_manifest = "examples/private_set_intersection/module/rust/Cargo.toml" } } +[server] +additional_args = [ + "--signatures-manifest=examples/private_set_intersection/signatures.toml" +] + [clients] -cpp = { Bazel = { bazel_target = "//examples/private_set_intersection/client:client" } } +cpp = { Bazel = { bazel_target = "//examples/private_set_intersection/client:client" }, additional_args = [ + "--public_key=../../../../../../../../examples/keys/ed25519/test.pub", +] } diff --git a/examples/private_set_intersection/oak_app_manifest.toml b/examples/private_set_intersection/oak_app_manifest.toml index 9cfb15e2f71..24db8430e28 100644 --- a/examples/private_set_intersection/oak_app_manifest.toml +++ b/examples/private_set_intersection/oak_app_manifest.toml @@ -1,4 +1,5 @@ name = "private_set_intersection" [modules] -app = { path = "examples/private_set_intersection/bin/private_set_intersection.wasm" } +# TODO(865): Use locally built module once reproducibility is fixed. +app = { external = { url = "https://storage.googleapis.com/oak-modules/private_set_intersection/b6c9183aaa1d0a65e3056d7ff7a35eb039c6c8f661f833ce8041332e3d8493c1", sha256 = "b6c9183aaa1d0a65e3056d7ff7a35eb039c6c8f661f833ce8041332e3d8493c1" } } diff --git a/examples/trusted_information_retrieval/database_proxy.sign b/examples/private_set_intersection/private_set_intersection.sign similarity index 56% rename from examples/trusted_information_retrieval/database_proxy.sign rename to examples/private_set_intersection/private_set_intersection.sign index a823ff2642e..332311b45d3 100644 --- a/examples/trusted_information_retrieval/database_proxy.sign +++ b/examples/private_set_intersection/private_set_intersection.sign @@ -3,10 +3,10 @@ f41SClNtR4i46v2Tuh1fQLbt/ZqRr1lENajCW92jyP4= -----END PUBLIC KEY----- -----BEGIN SIGNATURE----- -XTMXZqNPeVO5mfT+GURXMBXU3GrjVtIVlpdrnACbkgePpszy+RuL7wzrok6bU+P5 -1JY5gTU4vSU8kljTOzJvDw== +SPZRIElc7+ayx35H1j63zSOD19Xi8JZQAMnU9HgBxGgLpyDcKE4tlJhBNTLC0HgU +dKLbbAQGLp7MjVmmJhfCDA== -----END SIGNATURE----- -----BEGIN HASH----- -NgaobZ+gHSI9w8sJLBrOueOv4O2XmaxdWyFLFhAgn48= +tskYOqodCmXjBW1/96NesDnGyPZh+DPOgEEzLj2Ek8E= -----END HASH----- diff --git a/examples/private_set_intersection/signatures.toml b/examples/private_set_intersection/signatures.toml new file mode 100644 index 00000000000..27b629e39a0 --- /dev/null +++ b/examples/private_set_intersection/signatures.toml @@ -0,0 +1,3 @@ +signatures = [ + { path = "examples/private_set_intersection/private_set_intersection.sign" }, +] diff --git a/examples/trusted_information_retrieval/client/trusted_information_retrieval.cc b/examples/trusted_information_retrieval/client/trusted_information_retrieval.cc index e8464e5bc7e..75dbc3ace53 100644 --- a/examples/trusted_information_retrieval/client/trusted_information_retrieval.cc +++ b/examples/trusted_information_retrieval/client/trusted_information_retrieval.cc @@ -59,7 +59,6 @@ int main(int argc, char** argv) { LOG(INFO) << "Connecting to Oak Application: " << address; // TODO(#1066): Use a more restrictive Label. - // TODO(#1344): Add Wasm module signature Label. oak::label::Label label = oak::PublicUntrustedLabel(); // Connect to the Oak Application. auto stub = TrustedInformationRetrieval::NewStub(oak::ApplicationClient::CreateChannel( diff --git a/examples/trusted_information_retrieval/example.toml b/examples/trusted_information_retrieval/example.toml index 0d0279ca14f..d546deafb01 100644 --- a/examples/trusted_information_retrieval/example.toml +++ b/examples/trusted_information_retrieval/example.toml @@ -17,7 +17,6 @@ module_1 = { Cargo = { cargo_manifest = "examples/trusted_information_retrieval/ [server] additional_args = [ "--config-files=config=examples/trusted_information_retrieval/database.toml", - "--signatures-manifest=examples/trusted_information_retrieval/signatures.toml" ] [clients] diff --git a/examples/trusted_information_retrieval/signatures.toml b/examples/trusted_information_retrieval/signatures.toml deleted file mode 100644 index c8d5d430056..00000000000 --- a/examples/trusted_information_retrieval/signatures.toml +++ /dev/null @@ -1,4 +0,0 @@ -signatures = [ - # database_proxy - { path = "examples/trusted_information_retrieval/database_proxy.sign" }, -] diff --git a/oak/client/application_client.h b/oak/client/application_client.h index f67e7495f10..40a23891b45 100644 --- a/oak/client/application_client.h +++ b/oak/client/application_client.h @@ -124,6 +124,27 @@ class ApplicationClient { } return utils::read_file(root_cert_path); } + + // Loads the PEM-encoded public key. + static std::string LoadPublicKey(std::string public_key_path) { + std::string public_key = utils::read_file(public_key_path); + + // Parse key file. + auto patterns = {"-----BEGIN PUBLIC KEY-----", "-----END PUBLIC KEY-----", "\r", "\n"}; + for (std::string pattern : patterns) { + size_t substring = public_key.find(pattern); + if (substring != std::string::npos) { + public_key.erase(substring, pattern.length()); + } + } + + // Decode Base64 key. + std::string decoded_public_key; + if (!absl::Base64Unescape(public_key, &decoded_public_key)) { + LOG(FATAL) << "Could not decode public key: " << public_key; + } + return decoded_public_key; + } }; } // namespace oak diff --git a/oak/common/label.cc b/oak/common/label.cc index ffa360cfdc7..3d6ee94abab 100644 --- a/oak/common/label.cc +++ b/oak/common/label.cc @@ -51,7 +51,7 @@ oak::label::Label AuthorizationBearerTokenLabel(const std::string& authorization return label; } -oak::label::Label WebAssemblyModuleLabel(const std::string& web_assembly_module_hash_sha_256) { +oak::label::Label WebAssemblyModuleHashLabel(const std::string& web_assembly_module_hash_sha_256) { oak::label::Label label; auto* confidentiality_tag = label.add_confidentiality_tags(); confidentiality_tag->mutable_web_assembly_module_tag()->set_web_assembly_module_hash_sha_256( @@ -59,6 +59,15 @@ oak::label::Label WebAssemblyModuleLabel(const std::string& web_assembly_module_ return label; } +oak::label::Label WebAssemblyModuleSignatureLabel( + const std::string& web_assembly_module_public_key) { + oak::label::Label label; + auto* confidentiality_tag = label.add_confidentiality_tags(); + confidentiality_tag->mutable_web_assembly_module_signature_tag()->set_public_key( + web_assembly_module_public_key); + return label; +} + oak::label::Label TlsEndpointLabel(const std::string& authority) { oak::label::Label label; auto* confidentiality_tag = label.add_confidentiality_tags(); diff --git a/oak/common/label.h b/oak/common/label.h index f43e7898341..1e22f800757 100644 --- a/oak/common/label.h +++ b/oak/common/label.h @@ -48,7 +48,12 @@ oak::label::Label DeserializeLabel(const std::string& label_bytes); oak::label::Label AuthorizationBearerTokenLabel(const std::string& authorization_token_hmac); // Creates a label having as principal the provided WebAssembly module SHA-256 hash. -oak::label::Label WebAssemblyModuleLabel(const std::string& web_asesemblymodule_hash_sha_256); +oak::label::Label WebAssemblyModuleHashLabel(const std::string& web_asesemblymodule_hash_sha_256); + +// Creates a label having as principal the provided WebAssembly module Ed25519 public key. +// https://ed25519.cr.yp.to +oak::label::Label WebAssemblyModuleSignatureLabel( + const std::string& web_assembly_module_public_key); // Creates a label having as principal the provided TLS authority (host:port). oak::label::Label TlsEndpointLabel(const std::string& authority); diff --git a/oak_runtime/src/lib.rs b/oak_runtime/src/lib.rs index f083bdecd13..ada5dfc362b 100644 --- a/oak_runtime/src/lib.rs +++ b/oak_runtime/src/lib.rs @@ -478,6 +478,7 @@ impl Runtime { pub(crate) fn verify_module_signatures(&self) -> Result<(), OakStatus> { for (name, module_bytes) in &self.application_configuration.wasm_modules { let module_hash = sha_256_hex(&module_bytes); + // Get signature by module name. if let Some(signatures) = self.signature_table.values.get(&module_hash) { for signature_item in signatures.iter() { diff --git a/oak_runtime/src/node/wasm/mod.rs b/oak_runtime/src/node/wasm/mod.rs index 14258608c98..b03a822c2cc 100644 --- a/oak_runtime/src/node/wasm/mod.rs +++ b/oak_runtime/src/node/wasm/mod.rs @@ -18,7 +18,7 @@ use crate::{ node::ConfigurationError, sha_256_hex, NodeMessage, NodePrivilege, NodeReadStatus, - RuntimeProxy, Signature, SignatureTable, + RuntimeProxy, SignatureTable, }; use byteorder::{ByteOrder, LittleEndian}; use log::{debug, error, info, trace, warn}; @@ -815,15 +815,9 @@ impl WasmNode { ConfigurationError::IncorrectWebAssemblyModuleName })?; - let signatures = signature_table - .values - .get(&node_configuration.wasm_module_name) - .cloned() - .unwrap_or_default(); - // We compute the node privilege once and for all at start and just store it, since it does // not change throughout the node execution. - let node_privilege = wasm_node_privilege(&wasm_module_bytes, signatures.as_ref()); + let node_privilege = wasm_node_privilege(&wasm_module_bytes, signature_table); Ok(Self { node_name: node_name.to_string(), @@ -836,21 +830,29 @@ impl WasmNode { /// Computes the [`NodePrivilege`] granted to a WebAssembly Node running the specified WebAssembly /// module. -/// Created [`NodePrivilege`] consists of Wasm module hash and signature. -fn wasm_node_privilege(wasm_module_bytes: &[u8], signatures: &[Signature]) -> NodePrivilege { - let module_hash = - hex::decode(sha_256_hex(&wasm_module_bytes)).expect("Couldn't decode SHA-256 hex value"); +/// Created [`NodePrivilege`] consists of Wasm module hash and any matching signatures. +fn wasm_node_privilege( + wasm_module_bytes: &[u8], + signature_table: &SignatureTable, +) -> NodePrivilege { + let module_hash = sha_256_hex(&wasm_module_bytes); debug!("Wasm module SHA-256 hash: {:?}", module_hash); - let hash_tag = oak_abi::label::web_assembly_module_tag(&module_hash); + + // Create hash tags. + let module_hash_bytes = hex::decode(&module_hash).expect("Couldn't decode SHA-256 hex value"); + let hash_tag = oak_abi::label::web_assembly_module_tag(&module_hash_bytes); let mut confidentiality_tags = hashset! { hash_tag.clone() }; let mut integrity_tags = hashset! { hash_tag }; - for signature_item in signatures.iter() { - let signature_tag = - oak_abi::label::web_assembly_module_signature_tag(&signature_item.public_key); - confidentiality_tags.insert(signature_tag.clone()); - integrity_tags.insert(signature_tag); + // Create signature tags. + if let Some(signatures) = signature_table.values.get(&module_hash) { + for signature_item in signatures.iter() { + let signature_tag = + oak_abi::label::web_assembly_module_signature_tag(&signature_item.public_key); + confidentiality_tags.insert(signature_tag.clone()); + integrity_tags.insert(signature_tag); + } } NodePrivilege::new(confidentiality_tags, integrity_tags) diff --git a/oak_runtime/src/node/wasm/tests.rs b/oak_runtime/src/node/wasm/tests.rs index 22506da4804..e40a94a7dc7 100644 --- a/oak_runtime/src/node/wasm/tests.rs +++ b/oak_runtime/src/node/wasm/tests.rs @@ -15,7 +15,7 @@ // use super::*; -use crate::{RuntimeProxy, SecureServerConfiguration}; +use crate::{RuntimeProxy, SecureServerConfiguration, Signature}; use maplit::hashmap; use oak_abi::{ label::Label,