mirror of
https://github.com/kidfromjupiter/nearby.git
synced 2026-09-15 07:06:11 -04:00
531 lines
20 KiB
C++
531 lines
20 KiB
C++
// Copyright 2020 Google LLC
|
|
//
|
|
// 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
|
|
//
|
|
// https://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 "connections/implementation/base_endpoint_channel.h"
|
|
|
|
#include <cstddef>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "securegcm/ukey2_handshake.h"
|
|
#include "gmock/gmock.h"
|
|
#include "protobuf-matchers/protocol-buffer-matchers.h"
|
|
#include "gtest/gtest.h"
|
|
#include "absl/strings/string_view.h"
|
|
#include "absl/synchronization/mutex.h"
|
|
#include "absl/time/clock.h"
|
|
#include "absl/time/time.h"
|
|
#include "connections/implementation/client_proxy.h"
|
|
#include "connections/implementation/encryption_runner.h"
|
|
#include "connections/implementation/endpoint_channel.h"
|
|
#include "connections/implementation/flags/nearby_connections_feature_flags.h"
|
|
#include "connections/implementation/offline_frames.h"
|
|
#include "internal/flags/nearby_flags.h"
|
|
#include "internal/platform/byte_array.h"
|
|
#include "internal/platform/count_down_latch.h"
|
|
#include "internal/platform/exception.h"
|
|
#include "internal/platform/input_stream.h"
|
|
#include "internal/platform/logging.h"
|
|
#include "internal/platform/multi_thread_executor.h"
|
|
#include "internal/platform/output_stream.h"
|
|
#include "internal/platform/pipe.h"
|
|
#include "proto/connections_enums.pb.h"
|
|
|
|
namespace nearby {
|
|
namespace connections {
|
|
namespace {
|
|
|
|
using ::location::nearby::proto::connections::DisconnectionReason;
|
|
using ::location::nearby::proto::connections::Medium;
|
|
using EncryptionContext = BaseEndpointChannel::EncryptionContext;
|
|
constexpr size_t kChunkSize = 64 * 1024;
|
|
|
|
class TestEndpointChannel : public BaseEndpointChannel {
|
|
public:
|
|
explicit TestEndpointChannel(InputStream* input, OutputStream* output)
|
|
: BaseEndpointChannel("service_id", "channel", input, output) {}
|
|
|
|
using BaseEndpointChannel::EncodeMessageForTests;
|
|
|
|
MOCK_METHOD(ExceptionOr<ByteArray>, DispatchPacket, (), (override));
|
|
MOCK_METHOD(Medium, GetMedium, (), (const, override));
|
|
MOCK_METHOD(void, CloseImpl, (), (override));
|
|
};
|
|
|
|
std::function<void()> MakeDataPump(
|
|
std::string label, InputStream* input, OutputStream* output,
|
|
std::function<void(const ByteArray&)> monitor = nullptr) {
|
|
return [label, input, output, monitor]() {
|
|
LOG(INFO) << "streaming data through '" << label << "'";
|
|
while (true) {
|
|
auto read_response = input->Read(kChunkSize);
|
|
if (!read_response.ok()) {
|
|
LOG(INFO) << "Peer reader closed on '" << label << "'";
|
|
output->Close();
|
|
break;
|
|
}
|
|
if (monitor) {
|
|
monitor(read_response.result());
|
|
}
|
|
auto write_response =
|
|
output->Write(read_response.result().AsStringView());
|
|
if (write_response.Raised()) {
|
|
LOG(INFO) << "Peer writer closed on '" << label << "'";
|
|
input->Close();
|
|
break;
|
|
}
|
|
}
|
|
LOG(INFO) << "streaming terminated on '" << label << "'";
|
|
};
|
|
}
|
|
|
|
std::function<void(const ByteArray&)> MakeDataMonitor(const std::string& label,
|
|
std::string* capture,
|
|
absl::Mutex* mutex) {
|
|
return [label, capture, mutex](const ByteArray& input) mutable {
|
|
std::string s = std::string(input);
|
|
{
|
|
absl::MutexLock lock(*mutex);
|
|
*capture += s;
|
|
}
|
|
LOG(INFO) << "source='" << label << "'"
|
|
<< "; message='" << s << "'";
|
|
};
|
|
}
|
|
|
|
std::pair<std::shared_ptr<EncryptionContext>,
|
|
std::shared_ptr<EncryptionContext>>
|
|
DoDhKeyExchange(BaseEndpointChannel* channel_a,
|
|
BaseEndpointChannel* channel_b) {
|
|
std::shared_ptr<EncryptionContext> context_a;
|
|
std::shared_ptr<EncryptionContext> context_b;
|
|
EncryptionRunner crypto_a;
|
|
EncryptionRunner crypto_b;
|
|
ClientProxy proxy_a;
|
|
ClientProxy proxy_b;
|
|
CountDownLatch latch(2);
|
|
crypto_a.StartClient(
|
|
&proxy_a, "endpoint_id", channel_a,
|
|
{
|
|
.on_success_cb =
|
|
[&latch, &context_a](
|
|
const std::string& endpoint_id,
|
|
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
|
|
const std::string& auth_token,
|
|
const ByteArray& raw_auth_token) {
|
|
LOG(INFO) << "client-A side key negotiation done";
|
|
EXPECT_TRUE(ukey2->VerifyHandshake());
|
|
auto context = ukey2->ToConnectionContext();
|
|
EXPECT_NE(context, nullptr);
|
|
context_a = std::move(context);
|
|
latch.CountDown();
|
|
},
|
|
.on_failure_cb =
|
|
[&latch](const std::string& endpoint_id,
|
|
EndpointChannel* channel) {
|
|
LOG(INFO) << "client-A side key negotiation failed";
|
|
latch.CountDown();
|
|
},
|
|
});
|
|
crypto_b.StartServer(
|
|
&proxy_b, "endpoint_id", channel_b,
|
|
{
|
|
.on_success_cb =
|
|
[&latch, &context_b](
|
|
const std::string& endpoint_id,
|
|
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
|
|
const std::string& auth_token,
|
|
const ByteArray& raw_auth_token) {
|
|
LOG(INFO) << "client-B side key negotiation done";
|
|
EXPECT_TRUE(ukey2->VerifyHandshake());
|
|
auto context = ukey2->ToConnectionContext();
|
|
EXPECT_NE(context, nullptr);
|
|
context_b = std::move(context);
|
|
latch.CountDown();
|
|
},
|
|
.on_failure_cb =
|
|
[&latch](const std::string& endpoint_id,
|
|
EndpointChannel* channel) {
|
|
LOG(INFO) << "client-B side key negotiation failed";
|
|
latch.CountDown();
|
|
},
|
|
});
|
|
EXPECT_TRUE(latch.Await(absl::Milliseconds(5000)).result());
|
|
return std::make_pair(std::move(context_a), std::move(context_b));
|
|
}
|
|
|
|
class BaseEndpointChannelTest : public ::testing::Test {
|
|
protected:
|
|
void TearDown() override {
|
|
// Restore any overridden flags after each test to ensure test isolation.
|
|
NearbyFlags::GetInstance().ResetOverridedValues();
|
|
}
|
|
|
|
const absl::string_view kTestData = "test_data";
|
|
};
|
|
|
|
TEST_F(BaseEndpointChannelTest, ReadSucceedsWhenFlagDisabled) {
|
|
NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
|
config_package_nearby::nearby_connections_feature::kRefactorBleL2cap,
|
|
false);
|
|
|
|
auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b.
|
|
auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a.
|
|
TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get());
|
|
TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get());
|
|
|
|
channel_a.Write(kTestData);
|
|
ByteArray rx_message = std::move(channel_b.Read().result());
|
|
EXPECT_EQ(rx_message.AsStringView(), kTestData);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, ReadCallsDispatchPacketWhenFlagEnabled) {
|
|
NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
|
config_package_nearby::nearby_connections_feature::kRefactorBleL2cap,
|
|
true);
|
|
|
|
auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b.
|
|
auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a.
|
|
TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get());
|
|
TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get());
|
|
|
|
EXPECT_CALL(channel_b, DispatchPacket)
|
|
.WillOnce(::testing::Return(
|
|
ExceptionOr<ByteArray>(ByteArray(std::string(kTestData)))));
|
|
|
|
channel_a.Write(kTestData);
|
|
|
|
auto read_byte = channel_b.Read();
|
|
EXPECT_TRUE(read_byte.ok());
|
|
EXPECT_EQ(read_byte.result().AsStringView(), kTestData);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest,
|
|
ReadPropagatesFailureFromDispatchPacketWhenFlagEnabled) {
|
|
NearbyFlags::GetInstance().OverrideBoolFlagValue(
|
|
config_package_nearby::nearby_connections_feature::kRefactorBleL2cap,
|
|
true);
|
|
|
|
auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b.
|
|
auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a.
|
|
TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get());
|
|
TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get());
|
|
|
|
EXPECT_CALL(channel_b, DispatchPacket)
|
|
.WillOnce(::testing::Return(ExceptionOr<ByteArray>(Exception::kIo)));
|
|
|
|
auto read_byte = channel_b.Read();
|
|
EXPECT_FALSE(read_byte.ok());
|
|
EXPECT_EQ(read_byte.GetException().value, Exception::kIo);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, ConstructorDestructorWorks) {
|
|
auto [input, output] = CreatePipe();
|
|
|
|
TestEndpointChannel test_channel(input.get(), output.get());
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, ReadWrite) {
|
|
// Direct not-encrypted IO.
|
|
auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b.
|
|
auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a.
|
|
TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get());
|
|
TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get());
|
|
absl::string_view tx_message = "data message";
|
|
channel_a.Write(tx_message);
|
|
ByteArray rx_message = std::move(channel_b.Read().result());
|
|
EXPECT_EQ(rx_message.AsStringView(), tx_message);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, ChannelUnencryptedByDefault) {
|
|
auto pipe = CreatePipe();
|
|
TestEndpointChannel channel(pipe.first.get(), pipe.second.get());
|
|
|
|
ExceptionOr<ByteArray> result = channel.TryDecrypt(ByteArray("message"));
|
|
|
|
EXPECT_FALSE(channel.IsEncrypted());
|
|
EXPECT_FALSE(result.ok());
|
|
EXPECT_EQ(result.exception(), Exception::kFailed);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, TryDecrypt) {
|
|
absl::string_view kMessage = "message";
|
|
auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b.
|
|
auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a.
|
|
TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get());
|
|
TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get());
|
|
auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b);
|
|
ASSERT_NE(context_a, nullptr);
|
|
ASSERT_NE(context_b, nullptr);
|
|
channel_a.EnableEncryption(context_a);
|
|
channel_b.EnableEncryption(context_b);
|
|
std::unique_ptr<std::string> encrypted_message =
|
|
channel_a.EncodeMessageForTests(kMessage);
|
|
|
|
ExceptionOr<ByteArray> decrypted_message =
|
|
channel_b.TryDecrypt(ByteArray(*encrypted_message));
|
|
|
|
EXPECT_TRUE(channel_b.IsEncrypted());
|
|
EXPECT_TRUE(decrypted_message.ok());
|
|
EXPECT_EQ(decrypted_message.result().AsStringView(), kMessage);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, TryDecryptFailsWhenDecryptionFails) {
|
|
auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b.
|
|
auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a.
|
|
TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get());
|
|
TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get());
|
|
auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b);
|
|
ASSERT_NE(context_a, nullptr);
|
|
channel_a.EnableEncryption(context_a);
|
|
|
|
ExceptionOr<ByteArray> result =
|
|
channel_a.TryDecrypt(ByteArray("invalid message"));
|
|
|
|
EXPECT_TRUE(channel_a.IsEncrypted());
|
|
EXPECT_FALSE(result.ok());
|
|
EXPECT_EQ(result.exception(), Exception::kExecution);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, NotEncryptedReadWriteCanBeIntercepted) {
|
|
// Not encrypted IO; MITM scenario.
|
|
|
|
// Setup test communication environment.
|
|
absl::Mutex mutex;
|
|
std::string capture_a;
|
|
std::string capture_b;
|
|
auto client_a =
|
|
CreatePipe(); // Channel "a" writes to client "a", reads from server "a".
|
|
auto client_b =
|
|
CreatePipe(); // Channel "b" writes to client "b", reads from server "b".
|
|
auto server_a = CreatePipe(); // Data pump "a" reads from client "a", writes
|
|
// to server "b".
|
|
auto server_b = CreatePipe(); // Data pump "b" reads from client "b", writes
|
|
// to server "a".
|
|
TestEndpointChannel channel_a(server_a.first.get(), client_a.second.get());
|
|
TestEndpointChannel channel_b(server_b.first.get(), client_b.second.get());
|
|
|
|
ON_CALL(channel_a, GetMedium).WillByDefault([]() { return Medium::BLE; });
|
|
ON_CALL(channel_b, GetMedium).WillByDefault([]() { return Medium::BLE; });
|
|
|
|
MultiThreadExecutor executor(2);
|
|
executor.Execute(
|
|
MakeDataPump("pump_a", client_a.first.get(), server_b.second.get(),
|
|
MakeDataMonitor("monitor_a", &capture_a, &mutex)));
|
|
executor.Execute(
|
|
MakeDataPump("pump_b", client_b.first.get(), server_a.second.get(),
|
|
MakeDataMonitor("monitor_b", &capture_b, &mutex)));
|
|
|
|
EXPECT_EQ(channel_a.GetType(), "BLE");
|
|
EXPECT_EQ(channel_b.GetType(), "BLE");
|
|
|
|
// Start data transfer
|
|
absl::string_view tx_message = "data message";
|
|
channel_a.Write(tx_message);
|
|
ByteArray rx_message = std::move(channel_b.Read().result());
|
|
|
|
// Verify expectations.
|
|
EXPECT_EQ(rx_message.AsStringView(), tx_message);
|
|
{
|
|
absl::MutexLock lock(mutex);
|
|
std::string message{tx_message};
|
|
EXPECT_TRUE(capture_a.find(message) != std::string::npos ||
|
|
capture_b.find(message) != std::string::npos);
|
|
}
|
|
|
|
// Shutdown test environment.
|
|
channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION);
|
|
channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, EncryptedReadWriteCanNotBeIntercepted) {
|
|
// Encrypted IO; MITM scenario.
|
|
|
|
// Setup test communication environment.
|
|
absl::Mutex mutex;
|
|
std::string capture_a;
|
|
std::string capture_b;
|
|
auto client_a =
|
|
CreatePipe(); // Channel "a" writes to client "a", reads from server "a".
|
|
auto client_b =
|
|
CreatePipe(); // Channel "b" writes to client "b", reads from server "b".
|
|
auto server_a = CreatePipe(); // Data pump "a" reads from client "a", writes
|
|
// to server "b".
|
|
auto server_b = CreatePipe(); // Data pump "b" reads from client "b", writes
|
|
// to server "a".
|
|
TestEndpointChannel channel_a(server_a.first.get(), client_a.second.get());
|
|
TestEndpointChannel channel_b(server_b.first.get(), client_b.second.get());
|
|
|
|
ON_CALL(channel_a, GetMedium).WillByDefault([]() {
|
|
return Medium::BLUETOOTH;
|
|
});
|
|
ON_CALL(channel_b, GetMedium).WillByDefault([]() {
|
|
return Medium::BLUETOOTH;
|
|
});
|
|
|
|
MultiThreadExecutor executor(2);
|
|
executor.Execute(
|
|
MakeDataPump("pump_a", client_a.first.get(), server_b.second.get(),
|
|
MakeDataMonitor("monitor_a", &capture_a, &mutex)));
|
|
executor.Execute(
|
|
MakeDataPump("pump_b", client_b.first.get(), server_a.second.get(),
|
|
MakeDataMonitor("monitor_b", &capture_b, &mutex)));
|
|
|
|
// Run DH key exchange; setup encryption contexts for channels.
|
|
auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b);
|
|
ASSERT_NE(context_a, nullptr);
|
|
ASSERT_NE(context_b, nullptr);
|
|
channel_a.EnableEncryption(context_a);
|
|
channel_b.EnableEncryption(context_b);
|
|
|
|
EXPECT_EQ(channel_a.GetType(), "ENCRYPTED_BLUETOOTH");
|
|
EXPECT_EQ(channel_b.GetType(), "ENCRYPTED_BLUETOOTH");
|
|
EXPECT_TRUE(channel_a.IsEncrypted());
|
|
EXPECT_TRUE(channel_b.IsEncrypted());
|
|
|
|
// Start data transfer
|
|
absl::string_view tx_message = "data message";
|
|
channel_a.Write(tx_message);
|
|
ByteArray rx_message = std::move(channel_b.Read().result());
|
|
|
|
// Verify expectations.
|
|
EXPECT_EQ(rx_message.AsStringView(), tx_message);
|
|
{
|
|
absl::MutexLock lock(mutex);
|
|
std::string message{tx_message};
|
|
EXPECT_TRUE(capture_a.find(message) == std::string::npos &&
|
|
capture_b.find(message) == std::string::npos);
|
|
}
|
|
|
|
// Shutdown test environment.
|
|
channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION);
|
|
channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, CanBesuspendedAndResumed) {
|
|
// Setup test communication environment.
|
|
auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b.
|
|
auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a.
|
|
TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get());
|
|
TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get());
|
|
|
|
ON_CALL(channel_a, GetMedium).WillByDefault([]() {
|
|
return Medium::WIFI_LAN;
|
|
});
|
|
ON_CALL(channel_b, GetMedium).WillByDefault([]() {
|
|
return Medium::WIFI_LAN;
|
|
});
|
|
|
|
EXPECT_EQ(channel_a.GetType(), "WIFI_LAN");
|
|
EXPECT_EQ(channel_b.GetType(), "WIFI_LAN");
|
|
|
|
// Start data transfer
|
|
absl::string_view tx_message = "data message";
|
|
absl::string_view more_message = "more data";
|
|
channel_a.Write(tx_message);
|
|
ByteArray rx_message = std::move(channel_b.Read().result());
|
|
|
|
// Pause and make sure reader blocks.
|
|
MultiThreadExecutor pause_resume_executor(2);
|
|
channel_a.Pause();
|
|
pause_resume_executor.Execute([&channel_a, &more_message]() {
|
|
// Write will block until channel is resumed, or closed.
|
|
EXPECT_TRUE(channel_a.Write(more_message).Ok());
|
|
});
|
|
CountDownLatch latch(1);
|
|
ByteArray read_more;
|
|
pause_resume_executor.Execute([&channel_b, &read_more, &latch]() {
|
|
// Read will block until channel is resumed, or closed.
|
|
auto response = channel_b.Read();
|
|
EXPECT_TRUE(response.ok());
|
|
read_more = std::move(response.result());
|
|
latch.CountDown();
|
|
});
|
|
absl::SleepFor(absl::Milliseconds(500));
|
|
EXPECT_TRUE(read_more.Empty());
|
|
|
|
// Resume; verify that data transfer comepleted.
|
|
channel_a.Resume();
|
|
EXPECT_TRUE(latch.Await(absl::Milliseconds(1000)).result());
|
|
EXPECT_EQ(read_more.AsStringView(), more_message);
|
|
|
|
// Shutdown test environment.
|
|
channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION);
|
|
channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, ReadAfterInputStreamClosed) {
|
|
auto [input, output] = CreatePipe();
|
|
|
|
TestEndpointChannel test_channel(input.get(), output.get());
|
|
|
|
// Close the output stream before trying to read from the input.
|
|
output->Close();
|
|
|
|
// Trying to read should fail gracefully with an kNoData error.
|
|
ExceptionOr<ByteArray> read_data = test_channel.Read();
|
|
|
|
ASSERT_FALSE(read_data.ok());
|
|
EXPECT_EQ(read_data.exception(), Exception::kNoData);
|
|
}
|
|
|
|
TEST_F(BaseEndpointChannelTest, ReadUnencryptedFrameOnEncryptedChannel) {
|
|
// Setup test communication environment.
|
|
auto pipe_a = CreatePipe(); // channel_a writes to pipe_a, reads from pipe_b.
|
|
auto pipe_b = CreatePipe(); // channel_b writes to pipe_b, reads from pipe_a.
|
|
TestEndpointChannel channel_a(pipe_b.first.get(), pipe_a.second.get());
|
|
TestEndpointChannel channel_b(pipe_a.first.get(), pipe_b.second.get());
|
|
|
|
ON_CALL(channel_a, GetMedium).WillByDefault([]() {
|
|
return Medium::BLUETOOTH;
|
|
});
|
|
ON_CALL(channel_b, GetMedium).WillByDefault([]() {
|
|
return Medium::BLUETOOTH;
|
|
});
|
|
|
|
// Run DH key exchange; setup encryption contexts for channels. But only
|
|
// encrypt |channel_b|.
|
|
auto [context_a, context_b] = DoDhKeyExchange(&channel_a, &channel_b);
|
|
ASSERT_NE(context_a, nullptr);
|
|
ASSERT_NE(context_b, nullptr);
|
|
channel_b.EnableEncryption(context_b);
|
|
|
|
EXPECT_EQ(channel_a.GetType(), "BLUETOOTH");
|
|
EXPECT_EQ(channel_b.GetType(), "ENCRYPTED_BLUETOOTH");
|
|
|
|
// An unencrypted KeepAlive should succeed.
|
|
std::string keep_alive_message = parser::ForKeepAlive();
|
|
channel_a.Write(keep_alive_message);
|
|
ExceptionOr<ByteArray> result = channel_b.Read();
|
|
EXPECT_TRUE(result.ok());
|
|
EXPECT_EQ(result.result().AsStringView(), keep_alive_message);
|
|
|
|
// An unencrypted data frame should fail.
|
|
absl::string_view tx_message = "data message";
|
|
channel_a.Write(tx_message);
|
|
result = channel_b.Read();
|
|
EXPECT_FALSE(result.ok());
|
|
EXPECT_EQ(result.exception(), Exception::kInvalidProtocolBuffer);
|
|
|
|
// Shutdown test environment.
|
|
channel_a.Close(DisconnectionReason::LOCAL_DISCONNECTION);
|
|
channel_b.Close(DisconnectionReason::REMOTE_DISCONNECTION);
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace connections
|
|
} // namespace nearby
|