Files
nearby/fastpair/message_stream/fake_provider.cc
T
2023-06-16 16:14:14 -07:00

340 lines
12 KiB
C++

// Copyright 2023 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 "fastpair/message_stream/fake_provider.h"
#include <string>
#include <utility>
#include "absl/status/status.h"
#include "absl/strings/escaping.h"
#include "absl/strings/numbers.h"
#include "fastpair/common/constant.h"
#include "internal/platform/medium_environment.h"
#include <openssl/base.h>
#include <openssl/bn.h>
#include <openssl/ec_key.h>
namespace nearby {
namespace fastpair {
namespace {
constexpr uint8_t kKeyBasedPairingResponseCode = 1;
constexpr uint8_t kSeekerPasskeyResponseCode = 2;
constexpr uint8_t kProviderPasskeyResponseCode = 3;
static EC_POINT *load_public_key(absl::string_view public_key) {
CHECK_EQ(public_key.size(), kPublicKeyByteSize);
BN_CTX *bn_ctx;
EC_KEY *key;
EC_POINT *point;
const EC_GROUP *group;
uint8_t oct_key[kPublicKeyByteSize + 1];
oct_key[0] = 0x04;
memcpy(oct_key + 1, public_key.data(), 64);
bn_ctx = BN_CTX_new();
key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
group = EC_KEY_get0_group(key);
point = EC_POINT_new(group);
CHECK_EQ(EC_POINT_oct2point(group, point, oct_key, sizeof(oct_key), bn_ctx),
1);
BN_CTX_free(bn_ctx);
EC_KEY_free(key);
return point;
}
static BIGNUM *load_private_key(absl::string_view private_key) {
CHECK_EQ(private_key.length(), 32);
uint8_t buffer[37];
buffer[0] = buffer[1] = buffer[2] = 0;
buffer[3] = 33;
buffer[4] = 0;
memcpy(buffer + 5, private_key.data(), 32);
return BN_mpi2bn(buffer, sizeof(buffer), nullptr);
}
std::string Aes128Decrypt(absl::string_view input, absl::string_view key) {
CHECK_EQ(input.size(), kAesBlockByteSize);
CHECK_EQ(key.size(), kSharedSecretKeyByteSize);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int input_length = kAesBlockByteSize;
int output_length = kAesBlockByteSize;
std::string output(output_length, 0);
CHECK_EQ(
EVP_DecryptInit(ctx, EVP_aes_128_ecb(),
reinterpret_cast<const uint8_t *>(key.data()), nullptr),
1);
CHECK_EQ(EVP_CIPHER_CTX_set_padding(ctx, 0), 1);
CHECK_EQ(EVP_EncryptUpdate(
ctx, reinterpret_cast<uint8_t *>(output.data()), &output_length,
reinterpret_cast<const uint8_t *>(input.data()), input_length),
1);
EVP_CIPHER_CTX_free(ctx);
return output;
}
std::string Aes128Encrypt(absl::string_view input, absl::string_view key) {
CHECK_EQ(input.size(), kAesBlockByteSize);
CHECK_EQ(key.size(), kSharedSecretKeyByteSize);
EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
int input_length = kAesBlockByteSize;
int output_length = kAesBlockByteSize;
std::string output(output_length, 0);
CHECK_EQ(
EVP_EncryptInit(ctx, EVP_aes_128_ecb(),
reinterpret_cast<const uint8_t *>(key.data()), nullptr),
1);
CHECK_EQ(EVP_CIPHER_CTX_set_padding(ctx, 0), 1);
CHECK_EQ(EVP_DecryptUpdate(
ctx, reinterpret_cast<uint8_t *>(output.data()), &output_length,
reinterpret_cast<const uint8_t *>(input.data()), input_length),
1);
EVP_CIPHER_CTX_free(ctx);
return output;
}
} // namespace
void FakeProvider::LoadAntiSpoofingKey(absl::string_view private_key,
absl::string_view public_key) {
EC_KEY *key;
BIGNUM *prv;
EC_POINT *pub;
prv = load_private_key(private_key);
CHECK_NE(prv, nullptr);
pub = load_public_key(public_key);
CHECK_NE(pub, nullptr);
key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
CHECK_EQ(EC_KEY_set_private_key(key, prv), 1);
CHECK_EQ(EC_KEY_set_public_key(key, pub), 1);
anti_spoofing_key_.reset(EVP_PKEY_new());
CHECK_EQ(EVP_PKEY_assign_EC_KEY(anti_spoofing_key_.get(), key), 1);
BN_free(prv);
EC_POINT_free(pub);
}
std::string FakeProvider::DecryptKbpRequest(absl::string_view request) {
NEARBY_LOGS(VERBOSE) << "Encrypted KBP request "
<< absl::BytesToHexString(request);
CHECK_EQ(request.size(), kEncryptedDataByteSize + kPublicKeyByteSize);
absl::string_view encrypted = request.substr(0, kEncryptedDataByteSize);
absl::string_view remote_public_key =
request.substr(kEncryptedDataByteSize, kPublicKeyByteSize);
std::string shared_secret = CreateSharedSecret(remote_public_key);
std::string decrypted = Aes128Decrypt(encrypted, shared_secret);
NEARBY_LOGS(VERBOSE) << "Decrypted KBP request "
<< absl::BytesToHexString(decrypted);
shared_secret_ = shared_secret;
return decrypted;
}
std::string FakeProvider::Encrypt(absl::string_view data) {
return Aes128Encrypt(data, shared_secret_);
}
std::string FakeProvider::GenSec256r1Secret(
absl::string_view remote_party_public_key) {
EVP_PKEY_CTX *ctx;
EVP_PKEY *peerkey;
EC_POINT *peer_point;
EC_KEY *ec_peer_key;
size_t secret_len;
std::string secret(32, 0);
/* Create the context for the shared secret derivation */
ctx = EVP_PKEY_CTX_new(anti_spoofing_key_.get(), nullptr);
CHECK_NE(ctx, nullptr);
/* Initialise */
CHECK_EQ(EVP_PKEY_derive_init(ctx), 1);
peer_point = load_public_key(remote_party_public_key);
CHECK_NE(peer_point, nullptr);
ec_peer_key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1);
CHECK_EQ(EC_KEY_set_public_key(ec_peer_key, peer_point), 1);
peerkey = EVP_PKEY_new();
CHECK_EQ(EVP_PKEY_assign_EC_KEY(peerkey, ec_peer_key), 1);
/* Provide the peer public key */
CHECK_EQ(EVP_PKEY_derive_set_peer(ctx, peerkey), 1);
/* Determine buffer length for shared secret */
CHECK_EQ(EVP_PKEY_derive(ctx, nullptr, &secret_len), 1);
CHECK_EQ(secret_len, secret.size());
/* Derive the shared secret */
CHECK_EQ(EVP_PKEY_derive(ctx, reinterpret_cast<uint8_t *>(secret.data()),
&secret_len),
1);
EVP_PKEY_CTX_free(ctx);
EC_POINT_free(peer_point);
EVP_PKEY_free(peerkey);
return secret;
}
std::string FakeProvider::CreateSharedSecret(
absl::string_view remote_public_key) {
std::string secret = GenSec256r1Secret(remote_public_key);
return std::string(
Crypto::Sha256(secret).AsStringView().substr(0, kAccountKeySize));
}
void FakeProvider::StartGattServer(FakeGattCallbacks *fake_gatt_callbacks) {
fake_gatt_callbacks_ = fake_gatt_callbacks;
gatt_server_ = ble_.StartGattServer(fake_gatt_callbacks_->GetGattCallback());
}
absl::Status FakeProvider::NotifyKeyBasedPairing(ByteArray response) {
CHECK_NE(gatt_server_, nullptr);
CHECK(key_based_characteristic_.has_value());
return gatt_server_->NotifyCharacteristicChanged(*key_based_characteristic_,
false, response);
}
absl::Status FakeProvider::NotifyPasskey(ByteArray response) {
CHECK_NE(gatt_server_, nullptr);
CHECK(passkey_characteristic_.has_value());
return gatt_server_->NotifyCharacteristicChanged(*passkey_characteristic_,
false, response);
}
void FakeProvider::StartDiscoverableAdvertisement(absl::string_view model_id) {
advertising_ = true;
model_id_ = model_id;
ble_v1_.StartAdvertising(std::string(kServiceID),
ByteArray(absl::HexStringToBytes(model_id)),
std::string(kFastPairServiceUuid));
}
void FakeProvider::StopAdvertising() {
if (advertising_) {
advertising_ = false;
ble_v1_.StopAdvertising(std::string(kServiceID));
}
}
void FakeProvider::SetKeyBasedPairingCallback() {
CHECK_NE(fake_gatt_callbacks_, nullptr);
CHECK(key_based_characteristic_.has_value());
fake_gatt_callbacks_->characteristics_[*key_based_characteristic_]
.write_callback = [this](absl::string_view request) {
NEARBY_LOGS(VERBOSE) << "Encrypted request: "
<< absl::BytesToHexString(request);
std::string decrypted_request = DecryptKbpRequest(request);
NEARBY_LOGS(VERBOSE) << "KBP decrypted request "
<< absl::BytesToHexString(decrypted_request);
fake_gatt_callbacks_->characteristics_[*key_based_characteristic_]
.write_value.Set(std::string(decrypted_request));
std::string response;
response.push_back(kKeyBasedPairingResponseCode);
response.append(GetMacAddressAsBytes());
response.resize(kEncryptedDataByteSize, 0);
absl::Status status = NotifyKeyBasedPairing(ByteArray(Encrypt(response)));
NEARBY_LOGS(VERBOSE) << "KBP notify result: " << status;
return absl::OkStatus();
};
}
void FakeProvider::SetPasskeyCallback() {
CHECK_NE(fake_gatt_callbacks_, nullptr);
CHECK(passkey_characteristic_.has_value());
fake_gatt_callbacks_->characteristics_[*passkey_characteristic_]
.write_callback = [this](absl::string_view request) {
NEARBY_LOGS(VERBOSE) << "Passkey Encrypted request: "
<< absl::BytesToHexString(request);
std::string decrypted = Aes128Decrypt(request, shared_secret_);
NEARBY_LOGS(VERBOSE) << "Passkey decrypted request "
<< absl::BytesToHexString(decrypted);
fake_gatt_callbacks_->characteristics_[*passkey_characteristic_]
.write_value.Set(std::string(decrypted));
if (decrypted[0] != kSeekerPasskeyResponseCode) {
return absl::InvalidArgumentError(
absl::StrFormat("Invalid passkey response code: 0x%x", decrypted[0]));
}
std::string response;
response.push_back(kProviderPasskeyResponseCode);
response.push_back(pass_key_ >> 16);
response.push_back(pass_key_ >> 8);
response.push_back(pass_key_);
response.resize(kEncryptedDataByteSize, 0);
absl::Status status = NotifyPasskey(ByteArray(Encrypt(response)));
NEARBY_LOGS(VERBOSE) << "Passkey notify result: " << status;
return absl::OkStatus();
};
}
void FakeProvider::SetAccountkeyCallback() {
CHECK_NE(fake_gatt_callbacks_, nullptr);
CHECK(accountkey_characteristic_.has_value());
fake_gatt_callbacks_->characteristics_[*accountkey_characteristic_]
.write_callback = [this](absl::string_view request) {
NEARBY_LOGS(VERBOSE) << "Account key encrypted request: "
<< absl::BytesToHexString(request);
std::string decrypted = Aes128Decrypt(request, shared_secret_);
NEARBY_LOGS(VERBOSE) << "Account key decrypted request "
<< absl::BytesToHexString(decrypted);
fake_gatt_callbacks_->characteristics_[*accountkey_characteristic_]
.write_value.Set(std::string(decrypted));
account_key_ = AccountKey(decrypted);
return absl::OkStatus();
};
}
void FakeProvider::ConfigurePairingContext(absl::string_view pass_key) {
api::PairingParams pairing_params;
pairing_params.pairing_type =
api::PairingParams::PairingType::kConfirmPasskey;
pairing_params.passkey = pass_key;
auto device = MediumEnvironment::Instance().FindBluetoothDevice(
provider_medium_.GetMacAddress());
CHECK_NE(device, nullptr);
MediumEnvironment::Instance().ConfigBluetoothPairingContext(device,
pairing_params);
CHECK(absl::SimpleAtoi(pass_key, &pass_key_));
}
void FakeProvider::PrepareForInitialPairing(
PairingConfig config, FakeGattCallbacks *fake_gatt_callbacks) {
LoadAntiSpoofingKey(config.private_key, config.public_key);
StartGattServer(fake_gatt_callbacks);
InsertCorrectGattCharacteristics();
SetKeyBasedPairingCallback();
SetPasskeyCallback();
SetAccountkeyCallback();
provider_adapter_.SetScanMode(
BluetoothAdapter::ScanMode::kConnectableDiscoverable);
ConfigurePairingContext(config.pass_key);
StartDiscoverableAdvertisement(config.model_id);
}
} // namespace fastpair
} // namespace nearby