diff --git a/WORKSPACE b/WORKSPACE index 56b1b76694e..ca1a2d9709c 100644 --- a/WORKSPACE +++ b/WORKSPACE @@ -18,6 +18,9 @@ workspace(name = "oak") load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +# The `name` argument in all `http_archive` rules should be equal to the +# WORKSPACE name of the corresponding library. + # Google Abseil. # https://github.com/abseil/abseil-cpp http_archive( @@ -33,7 +36,7 @@ http_archive( # BoringSSL. # https://github.com/google/boringssl http_archive( - name = "com_google_boringssl", + name = "boringssl", sha256 = "b6dd308895eea9e1f0d3f503b7210141f75ba6817c78b4057406ee8f0a042504", strip_prefix = "boringssl-44cc20b4a0227b8913dc5f9e063443cb05e4134d", urls = [ diff --git a/cc/client/BUILD b/cc/client/BUILD index 81b6af0689c..72eb3023ad2 100644 --- a/cc/client/BUILD +++ b/cc/client/BUILD @@ -26,6 +26,8 @@ cc_library( srcs = ["client.cc"], hdrs = ["client.h"], deps = [ + "//cc/crypto:client_encryptor", + "//cc/crypto:common", "//cc/remote_attestation:attestation_verifier", "//cc/transport", "//oak_remote_attestation/proto/v1:messages_cc_proto", @@ -35,6 +37,25 @@ cc_library( ], ) +# Tests + +cc_test( + name = "client_test", + size = "small", + srcs = ["client_test.cc"], + deps = [ + ":client", + "//cc/crypto:server_encryptor", + "//cc/crypto/hpke:recipient_context", + "//cc/remote_attestation:insecure_attestation_verifier", + "//cc/transport", + "//oak_remote_attestation/proto/v1:messages_cc_proto", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_main", + ], +) + # Binaries cc_binary( diff --git a/cc/client/client.cc b/cc/client/client.cc index 2c7d2e50fc6..a82a1f3df8f 100644 --- a/cc/client/client.cc +++ b/cc/client/client.cc @@ -21,19 +21,25 @@ #include "absl/status/statusor.h" #include "absl/strings/string_view.h" +#include "cc/crypto/client_encryptor.h" +#include "cc/crypto/common.h" #include "oak_remote_attestation/proto/v1/messages.pb.h" namespace oak::client { namespace { +using ::oak::crypto::ClientEncryptor; +using ::oak::crypto::DecryptionResult; using ::oak::remote_attestation::AttestationVerifier; +using ::oak::session::v1::AttestationBundle; using ::oak::transport::TransportWrapper; } // namespace +constexpr absl::string_view kEmptyAssociatedData = ""; + absl::StatusOr> OakClient::Create( std::unique_ptr transport, AttestationVerifier& verifier) { - absl::StatusOr<::oak::session::v1::AttestationBundle> endorsed_evidence = - transport->GetEvidence(); + absl::StatusOr endorsed_evidence = transport->GetEvidence(); if (!endorsed_evidence.ok()) { return endorsed_evidence.status(); } @@ -49,8 +55,33 @@ absl::StatusOr> OakClient::Create( } absl::StatusOr OakClient::Invoke(absl::string_view request_body) { - // TODO(#4069): Implement sending an encrypted request and decrypting the response. - return absl::OkStatus(); + // Create client encryptor. + absl::StatusOr> client_encryptor = + ClientEncryptor::Create(server_encryption_public_key_); + if (!client_encryptor.ok()) { + return client_encryptor.status(); + } + + // Encrypt request. + absl::StatusOr encrypted_request = + (*client_encryptor)->Encrypt(request_body, kEmptyAssociatedData); + if (!encrypted_request.ok()) { + return encrypted_request.status(); + } + + // Send request. + absl::StatusOr encrypted_response = transport_->Invoke(*encrypted_request); + if (!encrypted_response.ok()) { + return encrypted_response.status(); + } + + // Decrypt response. + absl::StatusOr response = (*client_encryptor)->Decrypt(*encrypted_response); + if (!response.ok()) { + return response.status(); + } + + return response->plaintext; } } // namespace oak::client diff --git a/cc/client/client_test.cc b/cc/client/client_test.cc new file mode 100644 index 00000000000..8a27302f287 --- /dev/null +++ b/cc/client/client_test.cc @@ -0,0 +1,95 @@ +/* + * 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. + */ + +#include "cc/client/client.h" + +#include + +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" +#include "cc/crypto/hpke/recipient_context.h" +#include "cc/crypto/server_encryptor.h" +#include "cc/remote_attestation/insecure_attestation_verifier.h" +#include "cc/transport/transport.h" +#include "gmock/gmock.h" +#include "gtest/gtest.h" +#include "oak_remote_attestation/proto/v1/messages.pb.h" + +namespace oak::client { +namespace { + +using ::oak::crypto::KeyPair; +using ::oak::crypto::ServerEncryptor; +using ::oak::remote_attestation::InsecureAttestationVerifier; +using ::oak::session::v1::AttestationBundle; +using ::oak::transport::TransportWrapper; +using ::testing::StrEq; + +constexpr absl::string_view kTestRequest = "Request"; +constexpr absl::string_view kTestResponse = "Response"; +constexpr absl::string_view kTestAssociatedData = ""; + +// Number of message exchanges done to test secure session handling. +constexpr uint8_t kTestSessionSize = 8; + +// TODO(#3641): Send test remote attestation report to the client and add corresponding tests. +class TestTransport : public TransportWrapper { + public: + TestTransport() : key_pair_(*KeyPair::Generate()) {} + + absl::StatusOr GetEvidence() override { + AttestationBundle endorsed_evidence; + endorsed_evidence.mutable_attestation_evidence()->set_encryption_public_key( + key_pair_.public_key); + return endorsed_evidence; + } + + absl::StatusOr Invoke(absl::string_view request_bytes) override { + ServerEncryptor server_encryptor = ServerEncryptor(key_pair_); + auto decrypted_request = server_encryptor.Decrypt(request_bytes); + if (!decrypted_request.ok()) { + return decrypted_request.status(); + } + + if (decrypted_request->plaintext != kTestRequest) { + return absl::InvalidArgumentError(std::string("incorrect request, expected: ") + + std::string(kTestRequest) + + ", got : " + decrypted_request->plaintext); + } + + return server_encryptor.Encrypt(kTestResponse, kTestAssociatedData); + } + + private: + KeyPair key_pair_; +}; + +// Client can process attestation evidence and invoke the backend. +TEST(EncryptorTest, ClientCreateAndInvokeSuccess) { + auto transport = std::make_unique(); + InsecureAttestationVerifier verifier = InsecureAttestationVerifier(); + auto oak_client = OakClient::Create(std::move(transport), verifier); + ASSERT_TRUE(oak_client.ok()); + + for (int i = 0; i < kTestSessionSize; i++) { + auto response = (*oak_client)->Invoke(kTestRequest); + ASSERT_TRUE(response.ok()); + EXPECT_THAT(*response, StrEq(kTestResponse)); + } +} + +} // namespace +} // namespace oak::client diff --git a/cc/client/grpc_client_cli.cc b/cc/client/grpc_client_cli.cc index e5b1ae8e4d9..aceec04a77b 100644 --- a/cc/client/grpc_client_cli.cc +++ b/cc/client/grpc_client_cli.cc @@ -14,13 +14,6 @@ * limitations under the License. */ -#include -#include -#include -#include -#include -#include - #include #include "absl/flags/flag.h" @@ -29,6 +22,11 @@ #include "cc/client/client.h" #include "cc/remote_attestation/insecure_attestation_verifier.h" #include "cc/transport/grpc_streaming_transport.h" +#include "grpcpp/channel.h" +#include "grpcpp/client_context.h" +#include "grpcpp/create_channel.h" +#include "grpcpp/grpcpp.h" +#include "grpcpp/security/credentials.h" #include "oak_remote_attestation/proto/v1/service_streaming.grpc.pb.h" using ::grpc::Channel; diff --git a/cc/crypto/hpke/BUILD b/cc/crypto/hpke/BUILD index a1a8a58bd28..0935cfd3cc5 100644 --- a/cc/crypto/hpke/BUILD +++ b/cc/crypto/hpke/BUILD @@ -27,9 +27,9 @@ cc_library( hdrs = ["recipient_context.h"], deps = [ ":utils", + "@boringssl//:crypto", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_boringssl//:crypto", ], ) @@ -39,9 +39,9 @@ cc_library( hdrs = ["sender_context.h"], deps = [ ":utils", + "@boringssl//:crypto", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_boringssl//:crypto", ], ) @@ -50,10 +50,10 @@ cc_library( srcs = ["utils.cc"], hdrs = ["utils.h"], deps = [ + "@boringssl//:crypto", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/status:statusor", "@com_google_absl//absl/strings", - "@com_google_boringssl//:crypto", ], ) @@ -66,8 +66,8 @@ cc_test( deps = [ ":recipient_context", ":sender_context", + "@boringssl//:crypto", "@com_google_absl//absl/status:statusor", - "@com_google_boringssl//:crypto", "@com_google_googletest//:gtest_main", ], ) diff --git a/cc/transport/grpc_streaming_transport.h b/cc/transport/grpc_streaming_transport.h index 253180949fd..31d7665496e 100644 --- a/cc/transport/grpc_streaming_transport.h +++ b/cc/transport/grpc_streaming_transport.h @@ -17,17 +17,15 @@ #ifndef CC_TRANSPORT_GRPC_STREAMING_TRANSPORT_H_ #define CC_TRANSPORT_GRPC_STREAMING_TRANSPORT_H_ -#include -#include -#include -#include -#include - #include #include "absl/status/statusor.h" #include "absl/strings/string_view.h" #include "cc/transport/transport.h" +#include "grpcpp/channel.h" +#include "grpcpp/client_context.h" +#include "grpcpp/create_channel.h" +#include "grpcpp/grpcpp.h" #include "oak_remote_attestation/proto/v1/messages.pb.h" #include "oak_remote_attestation/proto/v1/service_streaming.grpc.pb.h" #include "oak_remote_attestation/proto/v1/service_streaming.pb.h"