diff --git a/WORKSPACE b/WORKSPACE
index 11d1a097d2..50c7a7a65b 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -61,6 +61,16 @@ http_archive(
     ],
 )
 
+# GoogleBenchmark
+# https://github.com/google/benchmark
+http_archive(
+    name = "com_github_google_benchmark",
+    strip_prefix = "benchmark-1.9.1",
+    urls = [
+        "https://github.com/google/benchmark/archive/refs/tags/v1.9.1.zip",
+    ],
+)
+
 # C++ gRPC support.
 # https://github.com/grpc/grpc
 http_archive(
diff --git a/cc/containers/hello_world_enclave_app/BUILD b/cc/containers/hello_world_enclave_app/BUILD
index 591067b3bc..c643e6b046 100644
--- a/cc/containers/hello_world_enclave_app/BUILD
+++ b/cc/containers/hello_world_enclave_app/BUILD
@@ -80,6 +80,27 @@ cc_test(
     ],
 )
 
+cc_binary(
+    name = "standalone_benchmark",
+    testonly = True,
+    srcs = ["standalone_benchmark.cc"],
+    deps = [
+        ":app_service",
+        "//cc/attestation/verification:insecure_attestation_verifier",
+        "//cc/client",
+        "//cc/client:session_client",
+        "//cc/containers/sdk/standalone:oak_standalone",
+        "//cc/oak_session:client_session",
+        "//cc/transport:grpc_streaming_transport",
+        "//cc/transport:grpc_sync_session_client_transport",
+        "//oak_containers/examples/hello_world/proto:hello_world_cc_grpc",
+        "@com_github_google_benchmark//:benchmark",
+        "@com_google_absl//absl/log:check",
+        "@com_google_absl//absl/status",
+        "@com_google_absl//absl/status:status_matchers",
+    ],
+)
+
 pkg_tar(
     name = "tar",
     srcs = [":main"],
diff --git a/cc/containers/hello_world_enclave_app/README.md b/cc/containers/hello_world_enclave_app/README.md
index 681e050cd6..8d0659a58d 100644
--- a/cc/containers/hello_world_enclave_app/README.md
+++ b/cc/containers/hello_world_enclave_app/README.md
@@ -10,3 +10,9 @@ C++ implementation of the enclave app part (inside the TEE) of the Oak
 Containers Hello World example application. This implementation parallels the
 Rust implementation in
 [`/oak_containers/examples/hello_world/enclave_app`](../../../oak_containers/examples/hello_world/enclave_app).
+
+## Benchmarks
+
+To get some basic benchmark statistics:
+
+bazel run cc/containers/hello_world_enclave_app:standalone_benchmark
diff --git a/cc/containers/hello_world_enclave_app/app_service.cc b/cc/containers/hello_world_enclave_app/app_service.cc
index 472067820e..8ca94a8520 100644
--- a/cc/containers/hello_world_enclave_app/app_service.cc
+++ b/cc/containers/hello_world_enclave_app/app_service.cc
@@ -33,6 +33,7 @@
 
 namespace oak::containers::hello_world_enclave_app {
 
+using ::oak::session::v1::PlaintextMessage;
 using ::oak::session::v1::RequestWrapper;
 using ::oak::session::v1::ResponseWrapper;
 using ::oak::session::v1::SessionRequest;
@@ -85,4 +86,17 @@ grpc::Status EnclaveApplicationImpl::OakSession(
   return grpc::Status();
 }
 
+grpc::Status EnclaveApplicationImpl::PlaintextSession(
+    grpc::ServerContext* context,
+    grpc::ServerReaderWriter<PlaintextMessage, PlaintextMessage>* stream) {
+  PlaintextMessage request;
+  while (stream->Read(&request)) {
+    std::string response_text = HandleRequest(request.plaintext());
+    PlaintextMessage response;
+    *(response.mutable_plaintext()) = response_text;
+    stream->Write(response);
+  }
+  return grpc::Status();
+}
+
 }  // namespace oak::containers::hello_world_enclave_app
diff --git a/cc/containers/hello_world_enclave_app/app_service.h b/cc/containers/hello_world_enclave_app/app_service.h
index 7fb53f1ab8..309c4c4b06 100644
--- a/cc/containers/hello_world_enclave_app/app_service.h
+++ b/cc/containers/hello_world_enclave_app/app_service.h
@@ -53,6 +53,12 @@ class EnclaveApplicationImpl
                                oak::session::v1::SessionRequest>* stream)
       override;
 
+  grpc::Status PlaintextSession(
+      grpc::ServerContext* context,
+      grpc::ServerReaderWriter<oak::session::v1::PlaintextMessage,
+                               oak::session::v1::PlaintextMessage>* stream)
+      override;
+
  private:
   server::OakSessionServer session_server_;
   std::string HandleRequest(absl::string_view request);
diff --git a/cc/containers/hello_world_enclave_app/standalone_benchmark.cc b/cc/containers/hello_world_enclave_app/standalone_benchmark.cc
new file mode 100644
index 0000000000..558d164f34
--- /dev/null
+++ b/cc/containers/hello_world_enclave_app/standalone_benchmark.cc
@@ -0,0 +1,182 @@
+/*
+ * Copyright 2025 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 <cstdint>
+
+#include "absl/log/check.h"
+#include "absl/log/log.h"
+#include "absl/status/status_matchers.h"
+#include "benchmark/benchmark.h"
+#include "cc/attestation/verification/insecure_attestation_verifier.h"
+#include "cc/client/client.h"
+#include "cc/client/session_client.h"
+#include "cc/containers/hello_world_enclave_app/app_service.h"
+#include "cc/containers/sdk/standalone/oak_standalone.h"
+#include "cc/oak_session/channel/oak_session_channel.h"
+#include "cc/oak_session/client_session.h"
+#include "cc/transport/grpc_streaming_transport.h"
+#include "cc/transport/grpc_sync_session_client_transport.h"
+#include "grpcpp/server.h"
+#include "grpcpp/server_builder.h"
+#include "oak_containers/examples/hello_world/proto/hello_world.grpc.pb.h"
+
+using ::oak::attestation::v1::AttestationResults;
+using ::oak::attestation::verification::InsecureAttestationVerifier;
+using ::oak::client::OakClient;
+using ::oak::containers::example::EnclaveApplication;
+using ::oak::containers::hello_world_enclave_app::EnclaveApplicationImpl;
+using ::oak::crypto::EncryptionKeyProvider;
+using ::oak::crypto::KeyPair;
+using ::oak::session::v1::EndorsedEvidence;
+using ::oak::session::v1::PlaintextMessage;
+using ::oak::session::v1::SessionResponse;
+using ::oak::transport::GrpcStreamingTransport;
+
+namespace oak::containers::sdk::standalone {
+
+namespace {
+
+std::string TestMessage(int size) {
+  std::string message;
+  message.reserve(size);
+  for (int i = 0; i < size; i++) {
+    message += i % 255;
+  }
+  return message;
+}
+
+constexpr absl::string_view kApplicationConfig = "{}";
+
+class HelloWorldStandaloneBench : public benchmark::Fixture {
+ public:
+  void SetUp(benchmark::State& state) override {
+    // Set up our new Keypair and get an EndorsedEvidence from Rust.
+    absl::StatusOr<KeyPair> key_pair = KeyPair::Generate();
+    QCHECK_OK(key_pair);
+    absl::StatusOr<EndorsedEvidence> endorsed_evidence =
+        GetEndorsedEvidence(*key_pair);
+    QCHECK_OK(endorsed_evidence);
+
+    // Verify that the endorsed evidence is valid.
+    InsecureAttestationVerifier verifier;
+    absl::StatusOr<AttestationResults> attestation_results = verifier.Verify(
+        std::chrono::system_clock::now(), endorsed_evidence->evidence(),
+        endorsed_evidence->endorsements());
+    QCHECK_OK(attestation_results);
+
+    service_ = std::make_unique<EnclaveApplicationImpl>(
+        OakSessionContext(std::move(*endorsed_evidence),
+                          std::make_unique<EncryptionKeyProvider>(*key_pair)),
+        kApplicationConfig);
+
+    grpc::ServerBuilder builder;
+    builder.AddListeningPort("[::]:8080", grpc::InsecureServerCredentials());
+    builder.RegisterService(service_.get());
+    server_ = builder.BuildAndStart();
+
+    auto channel = grpc::CreateChannel("localhost:8080",
+                                       grpc::InsecureChannelCredentials());
+    stub_ = EnclaveApplication::NewStub(channel);
+  }
+
+ protected:
+  std::unique_ptr<EnclaveApplicationImpl> service_;
+  std::unique_ptr<grpc::Server> server_;
+  std::unique_ptr<EnclaveApplication::Stub> stub_;
+};
+
+BENCHMARK_DEFINE_F(HelloWorldStandaloneBench, HPKEInvocation)
+(benchmark::State& state) {
+  std::string test_message = TestMessage(state.range(0));
+  grpc::ClientContext context;
+  auto transport =
+      std::make_unique<GrpcStreamingTransport>(stub_->LegacySession(&context));
+  InsecureAttestationVerifier verifier;
+  auto client = OakClient::Create(std::move(transport), verifier);
+  QCHECK_OK(client);
+
+  for (auto iter : state) {
+    auto result = (*client)->Invoke(test_message);
+    QCHECK_OK(result);
+    QCHECK(*result ==
+           absl::Substitute("Hello from the enclave, $1! Btw, the app has a "
+                            "config with a length of $0 bytes.",
+                            kApplicationConfig.size(), test_message));
+  }
+  state.SetBytesProcessed(int64_t(state.iterations()) *
+                          int64_t(state.range(0)));
+}
+
+BENCHMARK_DEFINE_F(HelloWorldStandaloneBench, NoiseInvocation)
+(benchmark::State& state) {
+  std::string test_message = TestMessage(state.range(0));
+  client::OakSessionClient session_client;
+  grpc::ClientContext context;
+  auto channel = session_client.NewChannel(
+      std::make_unique<transport::GrpcSyncSessionClientTransport>(
+          stub_->OakSession(&context)));
+  QCHECK_OK(channel);
+
+  for (auto i : state) {
+    auto result = (*channel)->Send(test_message);
+    QCHECK_OK(result);
+    auto response = (*channel)->Receive();
+    QCHECK_OK(response);
+    QCHECK(*response ==
+           absl::Substitute("Hello from the enclave, $1! Btw, the app has a "
+                            "config with a length of $0 bytes.",
+                            kApplicationConfig.size(), test_message));
+  }
+  state.SetBytesProcessed(int64_t(state.iterations()) *
+                          int64_t(state.range(0)));
+}
+
+BENCHMARK_DEFINE_F(HelloWorldStandaloneBench, PlaintextInvocation)
+(benchmark::State& state) {
+  std::string test_message = TestMessage(state.range(0));
+  grpc::ClientContext context;
+  auto reader_writer = stub_->PlaintextSession(&context);
+
+  for (auto _ : state) {
+    PlaintextMessage request;
+    request.set_plaintext(test_message);
+    bool result = reader_writer->Write(request);
+    QCHECK(result);
+    PlaintextMessage response;
+    bool response_result = reader_writer->Read(&response);
+    QCHECK(response_result);
+    QCHECK(response.plaintext() ==
+           absl::Substitute("Hello from the enclave, $1! Btw, the app has a "
+                            "config with a length of $0 bytes.",
+                            kApplicationConfig.size(), test_message));
+  }
+  state.SetBytesProcessed(int64_t(state.iterations()) *
+                          int64_t(state.range(0)));
+}
+
+BENCHMARK_REGISTER_F(HelloWorldStandaloneBench, HPKEInvocation)
+    ->Range(2, 1 << 21);
+BENCHMARK_REGISTER_F(HelloWorldStandaloneBench, NoiseInvocation)
+    ->Range(2, 1 << 21);
+BENCHMARK_REGISTER_F(HelloWorldStandaloneBench, PlaintextInvocation)
+    ->Range(2, 1 << 21);
+
+}  // namespace
+
+}  // namespace oak::containers::sdk::standalone
+
+// Run the benchmark
+BENCHMARK_MAIN();
diff --git a/cc/oak_session/BUILD b/cc/oak_session/BUILD
index 54bbf23015..7f6ae699bc 100644
--- a/cc/oak_session/BUILD
+++ b/cc/oak_session/BUILD
@@ -90,6 +90,23 @@ cc_test(
     ],
 )
 
+cc_binary(
+    name = "client_server_session_benchmark",
+    testonly = True,
+    srcs = ["client_server_session_benchmark.cc"],
+    tags = ["asan"],
+    deps = [
+        ":client_session",
+        ":server_session",
+        "//proto/session:session_cc_proto",
+        "@com_github_google_benchmark//:benchmark",
+        "@com_github_grpc_grpc//:grpc++",
+        "@com_google_absl//absl/log",
+        "@com_google_absl//absl/log:check",
+        "@com_google_absl//absl/status:status_matchers",
+    ],
+)
+
 cc_library(
     name = "config",
     srcs = ["config.cc"],
diff --git a/cc/oak_session/client_server_session_benchmark.cc b/cc/oak_session/client_server_session_benchmark.cc
new file mode 100644
index 0000000000..caffc9b33e
--- /dev/null
+++ b/cc/oak_session/client_server_session_benchmark.cc
@@ -0,0 +1,106 @@
+/*
+ * Copyright 2025 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 <string>
+
+#include "absl/log/check.h"
+#include "absl/status/status_matchers.h"
+#include "benchmark/benchmark.h"
+#include "cc/oak_session/client_session.h"
+#include "cc/oak_session/server_session.h"
+#include "proto/session/session.pb.h"
+
+namespace oak::session {
+namespace {
+
+using ::oak::session::v1::SessionRequest;
+using ::oak::session::v1::SessionResponse;
+
+class ClientServerSessionBench : public benchmark::Fixture {};
+
+SessionConfig* TestConfig() {
+  return SessionConfigBuilder(AttestationType::kUnattested,
+                              HandshakeType::kNoiseNN)
+      .Build();
+}
+
+std::string TestMessage(int size) {
+  std::string message;
+  message.reserve(size);
+  for (int i = 0; i < size; i++) {
+    message += i % 255;
+  }
+  return message;
+}
+
+void DoHandshake(ClientSession& client_session, ServerSession& server_session) {
+  absl::StatusOr<std::optional<SessionRequest>> init =
+      client_session.GetOutgoingMessage();
+  QCHECK_OK(init);
+  QCHECK(*init != std::nullopt);
+  QCHECK_OK(server_session.PutIncomingMessage(**init));
+
+  absl::StatusOr<std::optional<SessionResponse>> init_resp =
+      server_session.GetOutgoingMessage();
+  QCHECK_OK(init_resp);
+  QCHECK(*init_resp != std::nullopt);
+  QCHECK_OK(client_session.PutIncomingMessage(**init_resp));
+
+  QCHECK(client_session.IsOpen());
+  QCHECK(server_session.IsOpen());
+}
+
+BENCHMARK_DEFINE_F(ClientServerSessionBench, ClientEncryptServerDecrypt)
+(benchmark::State& state) {
+  auto client_session = ClientSession::Create(TestConfig());
+  auto server_session = ServerSession::Create(TestConfig());
+
+  DoHandshake(**client_session, **server_session);
+
+  std::string test_message = TestMessage(state.range(0));
+  for (auto iter : state) {
+    v1::PlaintextMessage plaintext_message_request;
+    plaintext_message_request.set_plaintext(test_message);
+
+    QCHECK_OK((*client_session)->Write(plaintext_message_request));
+    absl::StatusOr<std::optional<SessionRequest>> request =
+        (*client_session)->GetOutgoingMessage();
+    QCHECK_OK(request);
+    QCHECK(*request != std::nullopt);
+
+    QCHECK_OK((*server_session)->PutIncomingMessage(**request));
+    absl::StatusOr<std::optional<v1::PlaintextMessage>> received_request =
+        (*server_session)->Read();
+    QCHECK_OK(received_request);
+    QCHECK(*received_request != std::nullopt);
+
+    QCHECK((**received_request).plaintext() ==
+           plaintext_message_request.plaintext());
+  }
+
+  state.SetBytesProcessed(int64_t(state.iterations()) *
+                          int64_t(state.range(0)));
+}
+
+BENCHMARK_REGISTER_F(ClientServerSessionBench, ClientEncryptServerDecrypt)
+    ->Range(2, 2 << 21);
+
+}  // namespace
+
+}  // namespace oak::session
+
+// Run the benchmark
+BENCHMARK_MAIN();
diff --git a/oak_containers/examples/hello_world/enclave_app/src/app_service.rs b/oak_containers/examples/hello_world/enclave_app/src/app_service.rs
index f602bbd7da..2bf48476d2 100644
--- a/oak_containers/examples/hello_world/enclave_app/src/app_service.rs
+++ b/oak_containers/examples/hello_world/enclave_app/src/app_service.rs
@@ -106,6 +106,8 @@ impl EnclaveApplication for EnclaveApplicationImplementation {
     type LegacySessionStream = oak_containers_sdk::tonic::OakSessionStream;
     type OakSessionStream =
         Pin<Box<dyn Stream<Item = Result<SessionResponse, tonic::Status>> + Send + 'static>>;
+    type PlaintextSessionStream =
+        Pin<Box<dyn Stream<Item = Result<PlaintextMessage, tonic::Status>> + Send + 'static>>;
 
     async fn legacy_session(
         &self,
@@ -145,6 +147,28 @@ impl EnclaveApplication for EnclaveApplicationImplementation {
         };
         Ok(tonic::Response::new(Box::pin(response_stream) as Self::OakSessionStream))
     }
+
+    async fn plaintext_session(
+        &self,
+        request: tonic::Request<tonic::Streaming<PlaintextMessage>>,
+    ) -> Result<tonic::Response<Self::PlaintextSessionStream>, tonic::Status> {
+        let mut request_stream = request.into_inner();
+        let application_handler = self.application_handler.clone();
+
+        let response_stream = async_stream::try_stream! {
+            while let Some(request) = request_stream.next().await {
+                let plaintext_message = request?;
+                    yield PlaintextMessage {
+                        plaintext: application_handler
+                            .handle(&plaintext_message.plaintext)
+                            .await
+                            .into_tonic_result("application failed")?
+                    };
+            }
+        };
+
+        Ok(tonic::Response::new(Box::pin(response_stream) as Self::PlaintextSessionStream))
+    }
 }
 
 pub async fn create(
diff --git a/oak_containers/examples/hello_world/proto/hello_world.proto b/oak_containers/examples/hello_world/proto/hello_world.proto
index c6d7ad81ef..648d3ac3f7 100644
--- a/oak_containers/examples/hello_world/proto/hello_world.proto
+++ b/oak_containers/examples/hello_world/proto/hello_world.proto
@@ -27,6 +27,9 @@ service EnclaveApplication {
 
   rpc OakSession(stream oak.session.v1.SessionRequest)
       returns (stream oak.session.v1.SessionResponse) {}
+
+  rpc PlaintextSession(stream oak.session.v1.PlaintextMessage)
+      returns (stream oak.session.v1.PlaintextMessage) {}
 }
 
 service HostApplication {