Roll forward to cl/338482889

Signed-off-by: Alexey Polyudov <apolyudov@google.com>
Change-Id: I9850950db8bd84f0904ea1a413151887f52098cf
This commit is contained in:
Alexey Polyudov
2020-10-22 10:47:34 -07:00
parent d68e53cf03
commit 2155b3ddeb
542 changed files with 15219 additions and 42295 deletions
+196 -265
View File
@@ -2,324 +2,269 @@
#include <cinttypes>
#include <cstdint>
#include <memory>
#include "platform/base64_utils.h"
#include "platform/byte_array.h"
#include "platform/cancelable_alarm.h"
#include "platform/exception.h"
#include "platform/logging.h"
#include "platform/base/base64_utils.h"
#include "platform/base/byte_array.h"
#include "platform/base/exception.h"
#include "platform/public/cancelable_alarm.h"
#include "platform/public/logging.h"
#include "securegcm/ukey2_handshake.h"
#include "absl/strings/ascii.h"
namespace {
std::int64_t kTimeoutMillis = 15 * 1000; // 15 seconds
std::int32_t kMaxUkey2VerificationStringLength = 32;
std::int32_t kTokenLength = 5;
securegcm::UKey2Handshake::HandshakeCipher kCipher =
securegcm::UKey2Handshake::HandshakeCipher::P256_SHA512;
} // namespace
#include "absl/time/clock.h"
#include "absl/time/time.h"
namespace location {
namespace nearby {
namespace connections {
namespace {
constexpr absl::Duration kTimeout = absl::Seconds(15);
constexpr std::int32_t kMaxUkey2VerificationStringLength = 32;
constexpr std::int32_t kTokenLength = 5;
constexpr securegcm::UKey2Handshake::HandshakeCipher kCipher =
securegcm::UKey2Handshake::HandshakeCipher::P256_SHA512;
// Transforms a raw UKEY2 token (which is a random ByteArray that's
// kMaxUkey2VerificationStringLength long) into a kTokenLength string that only
// uses A-Z0-9 for each character.
string toHumanReadableString(ConstPtr<ByteArray> token) {
string result = Base64Utils::encode(token).substr(0, kTokenLength);
// uses [A-Z], [0-9], '_', '-' for each character.
std::string ToHumanReadableString(const ByteArray& token) {
std::string result = Base64Utils::Encode(token).substr(0, kTokenLength);
absl::AsciiStrToUpper(&result);
return result;
}
template <typename Platform>
bool handleEncryptionSuccess(
const string& endpoint_id, Ptr<securegcm::UKey2Handshake> ukey2_handshake,
Ptr<typename EncryptionRunner<Platform>::ResultListener> result_listener) {
ScopedPtr<Ptr<securegcm::UKey2Handshake>> scoped_ukey2_handshake(
ukey2_handshake);
std::unique_ptr<string> verification_string =
scoped_ukey2_handshake->GetVerificationString(
kMaxUkey2VerificationStringLength);
bool HandleEncryptionSuccess(const std::string& endpoint_id,
std::unique_ptr<securegcm::UKey2Handshake> ukey2,
const EncryptionRunner::ResultListener& listener) {
std::unique_ptr<std::string> verification_string =
ukey2->GetVerificationString(kMaxUkey2VerificationStringLength);
if (verification_string == nullptr) {
return false;
}
ScopedPtr<ConstPtr<ByteArray>> raw_authentication_token(MakeConstPtr(
new ByteArray(verification_string->data(), verification_string->size())));
ByteArray raw_authentication_token(*verification_string);
result_listener->onEncryptionSuccess(
endpoint_id, scoped_ukey2_handshake.release(),
toHumanReadableString(raw_authentication_token.get()),
raw_authentication_token.release());
listener.on_success_cb(endpoint_id, std::move(ukey2),
ToHumanReadableString(raw_authentication_token),
raw_authentication_token);
return true;
}
template <typename Platform>
class CancelableAlarmRunnable : public Runnable {
void CancelableAlarmRunnable(ClientProxy* client,
const std::string& endpoint_id,
EndpointChannel* endpoint_channel) {
NEARBY_LOG(INFO,
"Timing out encryption for client %" PRId64
" to endpoint %s after %" PRId64 " ms",
client->GetClientId(), endpoint_id.c_str(),
static_cast<std::int64_t>(absl::ToInt64Milliseconds(kTimeout)));
endpoint_channel->Close();
}
class ServerRunnable final {
public:
CancelableAlarmRunnable(Ptr<ClientProxy<Platform>> client_proxy,
const string& endpoint_id,
Ptr<EndpointChannel> endpoint_channel)
: client_proxy_(client_proxy),
endpoint_id_(endpoint_id),
endpoint_channel_(endpoint_channel) {}
void run() override {
NEARBY_LOG(INFO,
"Timing out encryption for client %" PRId64
" to endpoint %s after %" PRId64 " ms",
client_proxy_->getClientId(), endpoint_id_.c_str(),
kTimeoutMillis);
endpoint_channel_->close();
}
private:
Ptr<ClientProxy<Platform>> client_proxy_;
const string endpoint_id_;
Ptr<EndpointChannel> endpoint_channel_;
};
template <typename Platform>
class ServerRunnable : public Runnable {
public:
ServerRunnable(Ptr<ClientProxy<Platform>> client_proxy,
Ptr<typename Platform::ScheduledExecutorType> alarm_executor,
const string& endpoint_id,
Ptr<EndpointChannel> endpoint_channel,
Ptr<typename EncryptionRunner<Platform>::ResultListener>
encryption_result_listener)
: client_proxy_(client_proxy),
ServerRunnable(ClientProxy* client, ScheduledExecutor* alarm_executor,
const std::string& endpoint_id, EndpointChannel* channel,
EncryptionRunner::ResultListener&& listener)
: client_(client),
alarm_executor_(alarm_executor),
endpoint_id_(endpoint_id),
endpoint_channel_(endpoint_channel),
encryption_result_listener_(encryption_result_listener) {}
channel_(channel),
listener_(std::move(listener)) {}
void run() override {
void operator()() const {
CancelableAlarm timeout_alarm(
"EncryptionRunner.startServer() timeout",
MakePtr(new CancelableAlarmRunnable<Platform>(
client_proxy_, endpoint_id_, endpoint_channel_)),
kTimeoutMillis, alarm_executor_);
"EncryptionRunner.StartServer() timeout",
[this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); },
kTimeout, alarm_executor_);
std::unique_ptr<securegcm::UKey2Handshake> server =
securegcm::UKey2Handshake::ForResponder(kCipher);
// Java code throws a HandshakeException.
if (server == nullptr) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
// Message 1 (Client Init)
ExceptionOr<ConstPtr<ByteArray>> client_init = endpoint_channel_->read();
ExceptionOr<ByteArray> client_init = channel_->Read();
if (!client_init.ok()) {
if (Exception::IO == client_init.exception()) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
return;
}
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
ScopedPtr<ConstPtr<ByteArray>> scoped_client_init(client_init.result());
securegcm::UKey2Handshake::ParseResult parse_result =
server->ParseHandshakeMessage(
string(scoped_client_init->getData(), scoped_client_init->size()));
server->ParseHandshakeMessage(std::string(client_init.result()));
// Java code throws a HandshakeException / AlertException.
if (!parse_result.success) {
logException();
LogException();
if (parse_result.alert_to_send != nullptr) {
handleAlertException(parse_result);
HandleAlertException(parse_result);
}
handleHandshakeOrIOException(&timeout_alarm);
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
NEARBY_LOG(INFO, "In startServer(), read UKEY2 Message 1 from endpoint %s",
NEARBY_LOG(INFO, "In StartServer(), read UKEY2 Message 1 from endpoint %s",
endpoint_id_.c_str());
// Message 2 (Server Init)
std::unique_ptr<string> server_init = server->GetNextHandshakeMessage();
std::unique_ptr<std::string> server_init =
server->GetNextHandshakeMessage();
// Java code throws a HandshakeException.
if (server_init == nullptr) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
Exception::Value write_exception = endpoint_channel_->write(
MakeConstPtr(new ByteArray(server_init->data(), server_init->size())));
if (Exception::NONE != write_exception) {
if (Exception::IO == write_exception) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
return;
}
Exception write_exception =
channel_->Write(ByteArray(std::move(*server_init)));
if (!write_exception.Ok()) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
NEARBY_LOG(INFO, "In startServer(), wrote UKEY2 Message 2 to endpoint %s",
NEARBY_LOG(INFO, "In StartServer(), wrote UKEY2 Message 2 to endpoint %s",
endpoint_id_.c_str());
// Message 3 (Client Finish)
ExceptionOr<ConstPtr<ByteArray>> client_finish = endpoint_channel_->read();
ExceptionOr<ByteArray> client_finish = channel_->Read();
if (!client_finish.ok()) {
if (Exception::IO == client_finish.exception()) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
return;
}
}
ScopedPtr<ConstPtr<ByteArray>> scoped_client_finish(client_finish.result());
parse_result = server->ParseHandshakeMessage(
string(scoped_client_finish->getData(), scoped_client_finish->size()));
// Java code throws an AlertException or a HandshakeException.
if (!parse_result.success) {
logException();
if (parse_result.alert_to_send != nullptr) {
handleAlertException(parse_result);
}
handleHandshakeOrIOException(&timeout_alarm);
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
NEARBY_LOG(INFO, "In startServer(), read UKEY2 Message 3 from endpoint %s",
parse_result =
server->ParseHandshakeMessage(std::string(client_finish.result()));
// Java code throws an AlertException or a HandshakeException.
if (!parse_result.success) {
LogException();
if (parse_result.alert_to_send != nullptr) {
HandleAlertException(parse_result);
}
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
NEARBY_LOG(INFO, "In StartServer(), read UKEY2 Message 3 from endpoint %s",
endpoint_id_.c_str());
timeout_alarm.cancel();
timeout_alarm.Cancel();
if (!handleEncryptionSuccess<Platform>(endpoint_id_,
MakePtr(server.release()),
encryption_result_listener_.get())) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
if (!HandleEncryptionSuccess(endpoint_id_, std::move(server), listener_)) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
}
private:
void logException() {
NEARBY_LOG(ERROR, "In startServer(), UKEY2 failed with endpoint %s",
void LogException() const {
NEARBY_LOG(ERROR, "In StartServer(), UKEY2 failed with endpoint %s",
endpoint_id_.c_str());
}
void handleHandshakeOrIOException(CancelableAlarm* timeout_alarm) {
timeout_alarm->cancel();
encryption_result_listener_->onEncryptionFailure(endpoint_id_,
endpoint_channel_);
void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) const {
timeout_alarm->Cancel();
listener_.on_failure_cb(endpoint_id_, channel_);
}
void handleAlertException(
const securegcm::UKey2Handshake::ParseResult& parse_result) {
Exception::Value write_exception = endpoint_channel_->write(
MakeConstPtr(new ByteArray(parse_result.alert_to_send->data(),
parse_result.alert_to_send->size())));
if (Exception::NONE != write_exception) {
if (Exception::IO == write_exception) {
NEARBY_LOG(WARNING,
"In startServer(), client %" PRId64
" failed to pass the alert error message to endpoint %s",
client_proxy_->getClientId(), endpoint_id_.c_str());
}
void HandleAlertException(
const securegcm::UKey2Handshake::ParseResult& parse_result) const {
Exception write_exception =
channel_->Write(ByteArray(*parse_result.alert_to_send));
if (!write_exception.Ok()) {
NEARBY_LOG(WARNING,
"In StartServer(), client %" PRId64
" failed to pass the alert error message to endpoint %s",
client_->GetClientId(), endpoint_id_.c_str());
}
}
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<typename Platform::ScheduledExecutorType> alarm_executor_;
const string endpoint_id_;
Ptr<EndpointChannel> endpoint_channel_;
ScopedPtr<Ptr<typename EncryptionRunner<Platform>::ResultListener>>
encryption_result_listener_;
ClientProxy* client_;
ScheduledExecutor* alarm_executor_;
const std::string endpoint_id_;
EndpointChannel* channel_;
EncryptionRunner::ResultListener listener_;
};
template <typename Platform>
class ClientRunnable : public Runnable {
class ClientRunnable final {
public:
ClientRunnable(Ptr<ClientProxy<Platform>> client_proxy,
Ptr<typename Platform::ScheduledExecutorType> alarm_executor,
const string& endpoint_id,
Ptr<EndpointChannel> endpoint_channel,
Ptr<typename EncryptionRunner<Platform>::ResultListener>
encryption_result_listener)
: client_proxy_(client_proxy),
ClientRunnable(ClientProxy* client, ScheduledExecutor* alarm_executor,
const std::string& endpoint_id, EndpointChannel* channel,
EncryptionRunner::ResultListener&& listener)
: client_(client),
alarm_executor_(alarm_executor),
endpoint_id_(endpoint_id),
endpoint_channel_(endpoint_channel),
encryption_result_listener_(encryption_result_listener) {}
channel_(channel),
listener_(std::move(listener)) {}
void run() override {
void operator()() const {
CancelableAlarm timeout_alarm(
"EncryptionRunner.startClient() timeout",
MakePtr(new CancelableAlarmRunnable<Platform>(
client_proxy_, endpoint_id_, endpoint_channel_)),
kTimeoutMillis, alarm_executor_);
[this]() { CancelableAlarmRunnable(client_, endpoint_id_, channel_); },
kTimeout, alarm_executor_);
std::unique_ptr<securegcm::UKey2Handshake> client =
std::unique_ptr<securegcm::UKey2Handshake> crypto =
securegcm::UKey2Handshake::ForInitiator(kCipher);
// Java code throws a HandshakeException.
if (client == nullptr) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
if (crypto == nullptr) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
// Message 1 (Client Init)
std::unique_ptr<string> client_init = client->GetNextHandshakeMessage();
std::unique_ptr<std::string> client_init =
crypto->GetNextHandshakeMessage();
// Java code throws a HandshakeException.
if (client_init == nullptr) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
Exception::Value write_init_exception = endpoint_channel_->write(
MakeConstPtr(new ByteArray(client_init->data(), client_init->size())));
if (Exception::NONE != write_init_exception) {
if (Exception::IO == write_init_exception) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
return;
}
Exception write_init_exception = channel_->Write(ByteArray(*client_init));
if (!write_init_exception.Ok()) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
NEARBY_LOG(INFO, "In startClient(), wrote UKEY2 Message 1 to endpoint %s",
endpoint_id_.c_str());
// Message 2 (Server Init)
ExceptionOr<ConstPtr<ByteArray>> server_init = endpoint_channel_->read();
ExceptionOr<ByteArray> server_init = channel_->Read();
if (!server_init.ok()) {
if (Exception::IO == server_init.exception()) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
return;
}
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
ScopedPtr<ConstPtr<ByteArray>> scoped_server_init(server_init.result());
securegcm::UKey2Handshake::ParseResult parse_result =
client->ParseHandshakeMessage(
string(scoped_server_init->getData(), scoped_server_init->size()));
crypto->ParseHandshakeMessage(std::string(server_init.result()));
// Java code throws an AlertException or a HandshakeException.
if (!parse_result.success) {
logException();
LogException();
if (parse_result.alert_to_send != nullptr) {
handleAlertException(parse_result);
HandleAlertException(parse_result);
}
handleHandshakeOrIOException(&timeout_alarm);
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
@@ -327,109 +272,95 @@ class ClientRunnable : public Runnable {
endpoint_id_.c_str());
// Message 3 (Client Finish)
std::unique_ptr<string> client_finish = client->GetNextHandshakeMessage();
std::unique_ptr<std::string> client_finish =
crypto->GetNextHandshakeMessage();
// Java code throws a HandshakeException.
if (client_finish == nullptr) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
Exception::Value write_finish_exception =
endpoint_channel_->write(MakeConstPtr(
new ByteArray(client_finish->data(), client_finish->size())));
if (Exception::NONE != write_finish_exception) {
if (Exception::IO == write_finish_exception) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
return;
}
Exception write_finish_exception =
channel_->Write(ByteArray(*client_finish));
if (!write_finish_exception.Ok()) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
NEARBY_LOG(INFO, "In startClient(), wrote UKEY2 Message 3 to endpoint %s",
endpoint_id_.c_str());
timeout_alarm.cancel();
timeout_alarm.Cancel();
if (!handleEncryptionSuccess<Platform>(endpoint_id_,
MakePtr(client.release()),
encryption_result_listener_.get())) {
logException();
handleHandshakeOrIOException(&timeout_alarm);
if (!HandleEncryptionSuccess(endpoint_id_, std::move(crypto), listener_)) {
LogException();
HandleHandshakeOrIoException(&timeout_alarm);
return;
}
}
private:
void logException() {
void LogException() const {
NEARBY_LOG(ERROR, "In startClient(), UKEY2 failed with endpoint %s",
endpoint_id_.c_str());
}
void handleHandshakeOrIOException(CancelableAlarm* timeout_alarm) {
timeout_alarm->cancel();
encryption_result_listener_->onEncryptionFailure(endpoint_id_,
endpoint_channel_);
void HandleHandshakeOrIoException(CancelableAlarm* timeout_alarm) const {
timeout_alarm->Cancel();
listener_.on_failure_cb(endpoint_id_, channel_);
}
void handleAlertException(
const securegcm::UKey2Handshake::ParseResult& parse_result) {
Exception::Value write_exception = endpoint_channel_->write(
MakeConstPtr(new ByteArray(parse_result.alert_to_send->data(),
parse_result.alert_to_send->size())));
if (Exception::NONE != write_exception) {
if (Exception::IO == write_exception) {
NEARBY_LOG(WARNING,
"In startClient(), client %" PRId64
" failed to pass the alert error message to endpoint %s",
client_proxy_->getClientId(), endpoint_id_.c_str());
}
void HandleAlertException(
const securegcm::UKey2Handshake::ParseResult& parse_result) const {
Exception write_exception =
channel_->Write(ByteArray(*parse_result.alert_to_send));
if (!write_exception.Ok()) {
NEARBY_LOG(WARNING,
"In startClient(), client %" PRId64
" failed to pass the alert error message to endpoint %s",
client_->GetClientId(), endpoint_id_.c_str());
}
}
Ptr<ClientProxy<Platform>> client_proxy_;
Ptr<typename Platform::ScheduledExecutorType> alarm_executor_;
const string endpoint_id_;
Ptr<EndpointChannel> endpoint_channel_;
ScopedPtr<Ptr<typename EncryptionRunner<Platform>::ResultListener>>
encryption_result_listener_;
ClientProxy* client_;
ScheduledExecutor* alarm_executor_;
const std::string endpoint_id_;
EndpointChannel* channel_;
EncryptionRunner::ResultListener listener_;
};
} // namespace
template <typename Platform>
EncryptionRunner<Platform>::EncryptionRunner()
: alarm_executor_(Platform::createScheduledExecutor()),
server_executor_(Platform::createSingleThreadExecutor()),
client_executor_(Platform::createSingleThreadExecutor()) {}
template <typename Platform>
EncryptionRunner<Platform>::~EncryptionRunner() {
EncryptionRunner::~EncryptionRunner() {
// Stop all the ongoing Runnables (as gracefully as possible).
client_executor_->shutdown();
server_executor_->shutdown();
alarm_executor_->shutdown();
client_executor_.Shutdown();
server_executor_.Shutdown();
alarm_executor_.Shutdown();
}
template <typename Platform>
void EncryptionRunner<Platform>::startServer(
Ptr<ClientProxy<Platform>> client_proxy, const string& endpoint_id,
Ptr<EndpointChannel> endpoint_channel,
Ptr<ResultListener> result_listener) {
server_executor_->execute(MakePtr(new ServerRunnable<Platform>(
client_proxy, alarm_executor_.get(), endpoint_id, endpoint_channel,
result_listener)));
void EncryptionRunner::StartServer(
ClientProxy* client, const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
EncryptionRunner::ResultListener&& listener) {
server_executor_.Execute(
[runnable{ServerRunnable(client, &alarm_executor_, endpoint_id,
endpoint_channel, std::move(listener))}]() {
runnable();
});
}
template <typename Platform>
void EncryptionRunner<Platform>::startClient(
Ptr<ClientProxy<Platform>> client_proxy, const string& endpoint_id,
Ptr<EndpointChannel> endpoint_channel,
Ptr<ResultListener> result_listener) {
client_executor_->execute(MakePtr(new ClientRunnable<Platform>(
client_proxy, alarm_executor_.get(), endpoint_id, endpoint_channel,
result_listener)));
void EncryptionRunner::StartClient(
ClientProxy* client, const std::string& endpoint_id,
EndpointChannel* endpoint_channel,
EncryptionRunner::ResultListener&& listener) {
client_executor_.Execute(
[runnable{ClientRunnable(client, &alarm_executor_, endpoint_id,
endpoint_channel, std::move(listener))}]() {
runnable();
});
}
} // namespace connections