Implement ECDH and AES Encryption/ Decryption for Fast Pair Windows

PiperOrigin-RevId: 501666600
This commit is contained in:
Qin Wang
2023-01-12 14:25:57 -08:00
committed by Copybara-Service
parent bde4a780c6
commit 7eeea7a283
15 changed files with 1092 additions and 0 deletions
+41
View File
@@ -15,9 +15,12 @@
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_COMMON_CONSTANT_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_COMMON_CONSTANT_H_
#include <stdint.h>
namespace nearby {
namespace fastpair {
// Bluetooth Uuid
constexpr char kServiceId[] = "Fast Pair";
constexpr char kFastPairServiceUuid[] = "0000FE2C-0000-1000-8000-00805F9B34FB";
const char kKeyBasedPairingCharacteristicUuidV1[] = "1234";
@@ -30,6 +33,44 @@ const char kAccountKeyCharacteristicUuidV1[] = "1236";
const char kAccountKeyCharacteristicUuidV2[] =
"FE2C1236-8366-4814-8EB0-01DE32100BEA";
// Key pair
constexpr int kSharedSecretKeyByteSize = 16;
constexpr int kPublicKeyByteSize = 64;
// Encryption
constexpr int kAesBlockByteSize = 16;
constexpr int kEncryptedDataByteSize = 16;
// Decryption
constexpr int kDecryptedResponseAddressByteSize = 6;
constexpr int kDecryptedResponseSaltByteSize = 9;
constexpr int kDecryptedPasskeySaltByteSize = 12;
// Handshake response index
constexpr int kMessageTypeIndex = 0;
constexpr int kResponseAddressStartIndex = 1;
constexpr int kResponseSaltStartIndex = 7;
constexpr int kPasskeySaltStartIndex = 4;
// Handshake response message type
constexpr uint8_t kKeybasedPairingResponseType = 0x01;
constexpr uint8_t kSeekerPasskeyType = 0x02;
constexpr uint8_t kProviderPasskeyType = 0x03;
// Handshake request inex
constexpr uint8_t kProviderAddressStartIndex = 2;
constexpr uint8_t kSeekerAddressStartIndex = 8;
constexpr uint8_t kSeekerPasskey = 0x02;
constexpr uint8_t kAccountKeyStartByte = 0x04;
// Handshake request message type
constexpr uint8_t kKeyBasedPairingType = 0x00;
constexpr uint8_t kInitialOrSubsequentFlags = 0x00;
constexpr uint8_t kRetroactiveFlags = 0x10;
// GATT connection
constexpr int kMaxNumGattConnectionAttempts = 3;
constexpr int kGattOperationTimeout = 15;
} // namespace fastpair
} // namespace nearby
+120
View File
@@ -0,0 +1,120 @@
# Copyright 2022 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.
licenses(["notice"])
cc_library(
name = "handshake",
srcs = [
"fast_pair_decryption.cc",
"fast_pair_encryption.cc",
],
hdrs = [
"decrypted_passkey.h",
"decrypted_response.h",
"fast_pair_decryption.h",
"fast_pair_encryption.h",
"fast_pair_key_pair.h",
"fast_pair_message_type.h",
],
visibility = [
"//:__subpackages__",
"//fastpair:__subpackages__",
],
deps = [
"//fastpair/common",
"//internal/platform:base",
"//internal/platform:logging",
"@boringssl//:crypto",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/types:optional",
],
)
cc_test(
name = "decrypted_passkey_test",
size = "small",
srcs = [
"decrypted_passkey_test.cc",
],
shard_count = 1,
deps = [
":handshake",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "decrypted_response_test",
size = "small",
srcs = [
"decrypted_response_test.cc",
],
shard_count = 1,
deps = [
":handshake",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "fast_pair_key_pair_test",
size = "small",
srcs = [
"fast_pair_key_pair_test.cc",
],
shard_count = 1,
deps = [
":handshake",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "fast_pair_encryption_test",
size = "small",
srcs = [
"fast_pair_encryption_test.cc",
],
shard_count = 1,
deps = [
":handshake",
"//fastpair/common",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_absl//absl/strings",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "fast_pair_decryption_test",
size = "small",
srcs = [
"fast_pair_decryption_test.cc",
],
shard_count = 1,
deps = [
":handshake",
"//internal/platform/implementation/g3", # build_cleaner: keep
"@com_github_protobuf_matchers//protobuf-matchers",
"@com_google_googletest//:gtest_main",
],
)
+45
View File
@@ -0,0 +1,45 @@
// Copyright 2022 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.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_DECRYPTED_PASSKEY_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_DECRYPTED_PASSKEY_H_
#include <stddef.h>
#include <stdint.h>
#include <array>
#include "fastpair/common/constant.h"
#include "fastpair/handshake/fast_pair_message_type.h"
namespace nearby {
namespace fastpair {
// Thin structure which is used by the higher level components of the Fast Pair
// system to represent a decrypted account passkey.
struct DecryptedPasskey {
DecryptedPasskey(
FastPairMessageType message_type, uint32_t passkey,
const std::array<uint8_t, kDecryptedPasskeySaltByteSize>& salt)
: message_type(message_type), passkey(passkey), salt(salt) {}
FastPairMessageType message_type;
uint32_t passkey;
std::array<uint8_t, kDecryptedPasskeySaltByteSize> salt;
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_DECRYPTED_PASSKEY_H_
@@ -0,0 +1,45 @@
// Copyright 2022 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/handshake/decrypted_passkey.h"
#include <array>
#include "gtest/gtest.h"
namespace nearby {
namespace fastpair {
namespace {
TEST(DecryptedPasskeyTest, CreateDecryptedPasskey) {
// Message type
FastPairMessageType messgaeType = FastPairMessageType::kSeekersPasskey;
// Passkey bytes.
std::array<uint8_t, 3> passkey_bytes = {0x02, 0x03, 0x04};
uint32_t passkey = passkey_bytes[2];
passkey += passkey_bytes[1] << 8;
passkey += passkey_bytes[0] << 16;
// Random salt
std::array<uint8_t, 12> salt = {0x08, 0x09, 0x0A, 0x08, 0x09, 0x0E,
0x0A, 0x0C, 0x0D, 0x0E, 0x05, 0x02};
DecryptedPasskey decryptedPasskey(messgaeType, passkey, salt);
EXPECT_EQ(decryptedPasskey.message_type, messgaeType);
EXPECT_EQ(decryptedPasskey.passkey, passkey);
EXPECT_EQ(decryptedPasskey.salt, salt);
}
} // namespace
} // namespace fastpair
} // namespace nearby
+47
View File
@@ -0,0 +1,47 @@
// Copyright 2022 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.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_DECRYPTED_RESPONSE_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_DECRYPTED_RESPONSE_H_
#include <stddef.h>
#include <stdint.h>
#include <array>
#include "fastpair/common/constant.h"
#include "fastpair/handshake/fast_pair_message_type.h"
namespace nearby {
namespace fastpair {
// Thin structure which is used by the higher level components of the Fast Pair
// system to represent a decrypted response.
struct DecryptedResponse {
DecryptedResponse(
FastPairMessageType message_type,
const std::array<uint8_t, kDecryptedResponseAddressByteSize>&
address_bytes,
const std::array<uint8_t, kDecryptedResponseSaltByteSize>& salt)
: message_type(message_type), address_bytes(address_bytes), salt(salt) {}
FastPairMessageType message_type;
std::array<uint8_t, kDecryptedResponseAddressByteSize> address_bytes;
std::array<uint8_t, kDecryptedResponseSaltByteSize> salt;
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_DECRYPTED_RESPONSE_H_
@@ -0,0 +1,42 @@
// Copyright 2022 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/handshake/decrypted_response.h"
#include <array>
#include "gtest/gtest.h"
namespace nearby {
namespace fastpair {
namespace {
TEST(DecryptedResponseTest, CreateDecryptedResponse) {
constexpr std::array<uint8_t, 6> address_bytes = {0x02, 0x03, 0x04,
0x05, 0x06, 0x07};
constexpr std::array<uint8_t, 9> salt = {0x08, 0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x00};
DecryptedResponse decryptedResponse(
FastPairMessageType::kKeyBasedPairingResponse, address_bytes, salt);
EXPECT_EQ(decryptedResponse.message_type,
FastPairMessageType::kKeyBasedPairingResponse);
EXPECT_EQ(decryptedResponse.address_bytes, address_bytes);
EXPECT_EQ(decryptedResponse.salt, salt);
}
} // namespace
} // namespace fastpair
} // namespace nearby
+115
View File
@@ -0,0 +1,115 @@
// Copyright 2022 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/handshake/fast_pair_decryption.h"
#include <algorithm>
#include <array>
#ifdef NEARBY_CHROMIUM
#include "base/check.h"
#elif defined(NEARBY_SWIFTPM)
#include "internal/platform/logging.h"
#else
#include "absl/log/check.h" // nogncheck
#endif
#include "absl/types/optional.h"
#include "fastpair/common/constant.h"
#include "fastpair/handshake/decrypted_passkey.h"
#include "fastpair/handshake/decrypted_response.h"
#include <openssl/aes.h>
namespace nearby {
namespace fastpair {
std::array<uint8_t, kAesBlockByteSize> FastPairDecryption::DecryptBytes(
const std::array<uint8_t, kAesBlockByteSize>& aes_key_bytes,
const std::array<uint8_t, kAesBlockByteSize>& encrypted_bytes) {
AES_KEY aes_key;
int aes_key_was_set = AES_set_decrypt_key(aes_key_bytes.data(),
aes_key_bytes.size() * 8, &aes_key);
CHECK(aes_key_was_set == 0) << "Invalid AES key size.";
std::array<uint8_t, kAesBlockByteSize> decrypted_bytes;
// Encrypted_bytes is less than 16 bytes and can be guaranteed to be a
// single block, so we encrypt/decrypt it using AES ECB mode
// (Approved by: b/73360609)
AES_decrypt(encrypted_bytes.data(), decrypted_bytes.data(), &aes_key);
return decrypted_bytes;
}
// Decrypts the encrypted response
// (https://developers.google.com/nearby/fast-pair/specifications/characteristics#table1.4)
// and returns the parsed decrypted response
// (https://developers.google.com/nearby/fast-pair/specifications/characteristics#table1.3)
absl::optional<DecryptedResponse> FastPairDecryption::ParseDecryptResponse(
const std::array<uint8_t, kAesBlockByteSize>& aes_key_bytes,
const std::array<uint8_t, kAesBlockByteSize>& encrypted_response_bytes) {
std::array<uint8_t, kAesBlockByteSize> decrypted_response_bytes =
DecryptBytes(aes_key_bytes, encrypted_response_bytes);
uint8_t message_type = decrypted_response_bytes[kMessageTypeIndex];
// If the message type index is not the expected fast pair message type, then
// this is not a valid fast pair response.
if (message_type != kKeybasedPairingResponseType) {
return absl::nullopt;
}
std::array<uint8_t, kDecryptedResponseAddressByteSize> address_bytes;
std::copy(decrypted_response_bytes.begin() + kResponseAddressStartIndex,
decrypted_response_bytes.begin() + kResponseSaltStartIndex,
address_bytes.begin());
std::array<uint8_t, kDecryptedResponseSaltByteSize> salt;
std::copy(decrypted_response_bytes.begin() + kResponseSaltStartIndex,
decrypted_response_bytes.end(), salt.begin());
return DecryptedResponse(FastPairMessageType::kKeyBasedPairingResponse,
address_bytes, salt);
}
// Decrypts the encrypted passkey
// (https://developers.google.com/nearby/fast-pair/specifications/characteristics#table2.1)
// and returns the parsed decrypted passkey
// (https://developers.google.com/nearby/fast-pair/specifications/characteristics#table2.2)
// TODO(b/263400788) Add unit test to cover this function and fix all Mutants
// warning
absl::optional<DecryptedPasskey> FastPairDecryption::ParseDecryptPasskey(
const std::array<uint8_t, kAesBlockByteSize>& aes_key_bytes,
const std::array<uint8_t, kAesBlockByteSize>& encrypted_passkey_bytes) {
std::array<uint8_t, kAesBlockByteSize> decrypted_passkey_bytes =
DecryptBytes(aes_key_bytes, encrypted_passkey_bytes);
FastPairMessageType message_type;
if (decrypted_passkey_bytes[kMessageTypeIndex] == kSeekerPasskeyType) {
message_type = FastPairMessageType::kSeekersPasskey;
} else if (decrypted_passkey_bytes[kMessageTypeIndex] ==
kProviderPasskeyType) {
message_type = FastPairMessageType::kProvidersPasskey;
} else {
return absl::nullopt;
}
uint32_t passkey = decrypted_passkey_bytes[3];
passkey += decrypted_passkey_bytes[2] << 8;
passkey += decrypted_passkey_bytes[1] << 16;
std::array<uint8_t, kDecryptedPasskeySaltByteSize> salt;
std::copy(decrypted_passkey_bytes.begin() + kPasskeySaltStartIndex,
decrypted_passkey_bytes.end(), salt.begin());
return DecryptedPasskey(message_type, passkey, salt);
}
} // namespace fastpair
} // namespace nearby
+48
View File
@@ -0,0 +1,48 @@
// Copyright 2022 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.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_DECRYPTION_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_DECRYPTION_H_
#include <array>
#include <string>
#include "absl/types/optional.h"
#include "fastpair/handshake/decrypted_passkey.h"
#include "fastpair/handshake/decrypted_response.h"
namespace nearby {
namespace fastpair {
/** Utilities used for encrypting and decrypting Fast Pair packets. */
class FastPairDecryption {
public:
static constexpr int kAesBlockByteSize = 16;
static std::array<uint8_t, kAesBlockByteSize> DecryptBytes(
const std::array<uint8_t, kAesBlockByteSize>& aes_key_bytes,
const std::array<uint8_t, kAesBlockByteSize>& encrypted_bytes);
static absl::optional<DecryptedResponse> ParseDecryptResponse(
const std::array<uint8_t, 16>& aes_key_bytes,
const std::array<uint8_t, 16>& encrypted_response_bytes);
static absl::optional<DecryptedPasskey> ParseDecryptPasskey(
const std::array<uint8_t, 16>& aes_key_bytes,
const std::array<uint8_t, 16>& encrypted_passkey_bytes);
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_DECRYPTION_H_
@@ -0,0 +1,157 @@
// Copyright 2022 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/handshake/fast_pair_decryption.h"
#include <algorithm>
#include <array>
#include <iterator>
#include <vector>
#include "gtest/gtest.h"
#include "fastpair/handshake/fast_pair_encryption.h"
namespace nearby {
namespace fastpair {
namespace {
// All test data comes from
// https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#test_cases
constexpr std::array<uint8_t, kAesBlockByteSize> aes_key_bytes = {
0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6,
0xCF, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D};
TEST(FastPairDecryptionTest, ParseDecryptResponseSuccess) {
std::vector<uint8_t> response_bytes;
// Message type.
response_bytes.push_back(0x01);
// Address bytes.
std::array<uint8_t, 6> address_bytes = {0x02, 0x03, 0x04, 0x05, 0x06, 0x07};
std::copy(address_bytes.begin(), address_bytes.end(),
std::back_inserter(response_bytes));
// Random salt
std::array<uint8_t, 9> salt = {0x08, 0x09, 0x0A, 0x0B, 0x0C,
0x0D, 0x0E, 0x0F, 0x00};
std::copy(salt.begin(), salt.end(), std::back_inserter(response_bytes));
std::array<uint8_t, kAesBlockByteSize> response_bytes_array;
std::copy_n(response_bytes.begin(), kAesBlockByteSize,
response_bytes_array.begin());
auto encrypted_bytes =
FastPairEncryption::EncryptBytes(aes_key_bytes, response_bytes_array);
auto response =
FastPairDecryption::ParseDecryptResponse(aes_key_bytes, encrypted_bytes);
EXPECT_TRUE(response.has_value());
EXPECT_EQ(response->message_type,
FastPairMessageType::kKeyBasedPairingResponse);
EXPECT_EQ(response->address_bytes, address_bytes);
EXPECT_EQ(response->salt, salt);
}
TEST(FastPairDecryptionTest, ParseDecryptResponseFailure) {
constexpr std::array<uint8_t, kAesBlockByteSize> response_bytes = {
/*message_type=*/0x02,
/*address_bytes=*/0x02,
0x03,
0x04,
0x05,
0x06,
0x07,
/*salt=*/0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x00};
auto encrypted_bytes =
FastPairEncryption::EncryptBytes(aes_key_bytes, response_bytes);
auto response =
FastPairDecryption::ParseDecryptResponse(aes_key_bytes, encrypted_bytes);
EXPECT_FALSE(response.has_value());
}
TEST(FastPairDecryptionTest, ParseDecryptPasskeySuccess) {
std::vector<uint8_t> passkey_bytes;
// Message type.
passkey_bytes.push_back(0x02);
// Passkey bytes.
uint32_t passkey = 5;
passkey_bytes.push_back(passkey >> 16);
passkey_bytes.push_back(passkey >> 8);
passkey_bytes.push_back(passkey);
// Random salt
std::array<uint8_t, 12> salt = {0x08, 0x09, 0x0A, 0x08, 0x09, 0x0E,
0x0A, 0x0C, 0x0D, 0x0E, 0x05, 0x02};
std::copy(salt.begin(), salt.end(), std::back_inserter(passkey_bytes));
std::array<uint8_t, kAesBlockByteSize> passkey_bytes_array;
std::copy_n(passkey_bytes.begin(), kAesBlockByteSize,
passkey_bytes_array.begin());
auto encrypted_bytes =
FastPairEncryption::EncryptBytes(aes_key_bytes, passkey_bytes_array);
auto decrypted_passkey =
FastPairDecryption::ParseDecryptPasskey(aes_key_bytes, encrypted_bytes);
EXPECT_TRUE(decrypted_passkey.has_value());
EXPECT_EQ(decrypted_passkey->message_type,
FastPairMessageType::kSeekersPasskey);
EXPECT_EQ(decrypted_passkey->passkey, passkey);
EXPECT_EQ(decrypted_passkey->salt, salt);
}
TEST(FastPairDecryptionTest, ParseDecryptPasskeyFailure) {
constexpr std::array<uint8_t, kAesBlockByteSize> passkey_bytes = {
/*message_type=*/0x04,
/*passkey=*/0x02,
0x03,
0x04,
/*salt=*/0x05,
0x06,
0x07,
0x08,
0x09,
0x0A,
0x0B,
0x0C,
0x0D,
0x0E,
0x0F,
0x0E};
auto encrypted_bytes =
FastPairEncryption::EncryptBytes(aes_key_bytes, passkey_bytes);
auto passkey =
FastPairDecryption::ParseDecryptPasskey(aes_key_bytes, encrypted_bytes);
EXPECT_FALSE(passkey.has_value());
}
} // namespace
} // namespace fastpair
} // namespace nearby
+165
View File
@@ -0,0 +1,165 @@
// Copyright 2022 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/handshake/fast_pair_encryption.h"
#include <algorithm>
#include <array>
#include <iterator>
#include <string_view>
#ifdef NEARBY_CHROMIUM
#include "base/check.h"
#elif defined(NEARBY_SWIFTPM)
#include "internal/platform/logging.h"
#else
#include "absl/log/check.h" // nogncheck
#endif
#include "absl/types/optional.h"
#include "fastpair/common/constant.h"
#include "fastpair/handshake/fast_pair_key_pair.h"
#include "fastpair/handshake/fast_pair_message_type.h"
#include "internal/platform/logging.h"
#include <openssl/aes.h>
#include <openssl/base.h>
#include <openssl/ec.h>
#include <openssl/ec_key.h>
#include <openssl/ecdh.h>
#include <openssl/nid.h>
#include <openssl/sha.h>
namespace nearby {
namespace fastpair {
namespace {
// Converts the public anti-spoofing key into an EC_Point.
bssl::UniquePtr<EC_POINT> GetEcPointFromPublicAntiSpoofingKey(
const bssl::UniquePtr<EC_GROUP>& ec_group,
std::string_view decoded_public_anti_spoofing) {
std::array<uint8_t, kPublicKeyByteSize + 1> buffer;
buffer[0] = POINT_CONVERSION_UNCOMPRESSED;
std::copy(decoded_public_anti_spoofing.begin(),
decoded_public_anti_spoofing.end(), buffer.begin() + 1);
bssl::UniquePtr<EC_POINT> new_ec_point(EC_POINT_new(ec_group.get()));
if (!EC_POINT_oct2point(ec_group.get(), new_ec_point.get(), buffer.data(),
buffer.size(), nullptr)) {
return nullptr;
}
return new_ec_point;
}
// Key derivation function to be used in hashing the generated secret key.
void* KDF(const void* in, size_t inlen, void* out, size_t* outlen) {
// Set this to 16 since that's the amount of bytes we want to use
// for the key, even though more will be written by SHA256 below.
*outlen = kSharedSecretKeyByteSize;
return SHA256(static_cast<const uint8_t*>(in), inlen,
static_cast<uint8_t*>(out));
}
} // namespace
// TODO(b/263400788) Add unit test to cover this function and fix all Mutants
// warning
absl::optional<KeyPair> FastPairEncryption::GenerateKeysWithEcdhKeyAgreement(
std::string_view decoded_public_anti_spoofing) {
if (decoded_public_anti_spoofing.size() != kPublicKeyByteSize) {
NEARBY_LOGS(VERBOSE) << "Expected " << kPublicKeyByteSize
<< " byte value for anti-spoofing key. Got:"
<< decoded_public_anti_spoofing.size();
return absl::nullopt;
}
// Generate the secp256r1 key-pair.
bssl::UniquePtr<EC_GROUP> ec_group(
EC_GROUP_new_by_curve_name(NID_X9_62_prime256v1));
bssl::UniquePtr<EC_KEY> ec_key(
EC_KEY_new_by_curve_name(NID_X9_62_prime256v1));
if (!EC_KEY_generate_key(ec_key.get())) {
NEARBY_LOGS(VERBOSE) << __func__ << ": Failed to generate ec key";
return absl::nullopt;
}
// The ultimate goal here is to get a 64-byte public key. We accomplish this
// by converting the generated public key into the uncompressed X9.62 format,
// which is 0x04 followed by padded x and y coordinates.
std::array<uint8_t, kPublicKeyByteSize + 1> uncompressed_private_key;
int point_bytes_written = EC_POINT_point2oct(
ec_group.get(), EC_KEY_get0_public_key(ec_key.get()),
POINT_CONVERSION_UNCOMPRESSED, uncompressed_private_key.data(),
uncompressed_private_key.size(), nullptr);
if (point_bytes_written != uncompressed_private_key.size()) {
NEARBY_LOGS(VERBOSE) << __func__
<< ": EC_POINT_point2oct failed to convert public key "
"to uncompressed x9.62 format.";
return absl::nullopt;
}
bssl::UniquePtr<EC_POINT> public_anti_spoofing_point =
GetEcPointFromPublicAntiSpoofingKey(ec_group,
decoded_public_anti_spoofing);
if (!public_anti_spoofing_point) {
NEARBY_LOGS(VERBOSE)
<< __func__
<< ": Failed to convert Public Anti-Spoofing key to EC_POINT";
return absl::nullopt;
}
uint8_t secret[SHA256_DIGEST_LENGTH];
int computed_key_size =
ECDH_compute_key(secret, SHA256_DIGEST_LENGTH,
public_anti_spoofing_point.get(), ec_key.get(), &KDF);
if (computed_key_size != kSharedSecretKeyByteSize) {
NEARBY_LOGS(VERBOSE) << __func__ << ": ECDH_compute_key failed.";
return absl::nullopt;
}
// Take first 16 bytes from secret as the shared secret key.
std::array<uint8_t, kSharedSecretKeyByteSize> shared_secret_key;
std::copy(secret, secret + kSharedSecretKeyByteSize,
std::begin(shared_secret_key));
// Ignore the first byte since it is 0x04, from the above uncompressed X9 .62
// format.
std::array<uint8_t, kPublicKeyByteSize> public_key;
std::copy(uncompressed_private_key.begin() + 1,
uncompressed_private_key.end(), public_key.begin());
return KeyPair(shared_secret_key, public_key);
}
std::array<uint8_t, kAesBlockByteSize> FastPairEncryption::EncryptBytes(
const std::array<uint8_t, kAesBlockByteSize>& aes_key_bytes,
const std::array<uint8_t, kAesBlockByteSize>& bytes_to_encrypt) {
AES_KEY aes_key;
int aes_key_was_set = AES_set_encrypt_key(aes_key_bytes.data(),
aes_key_bytes.size() * 8, &aes_key);
DCHECK(aes_key_was_set == 0) << "Invalid AES key size.";
std::array<uint8_t, kAesBlockByteSize> encrypted_bytes;
// Bytes_to_encrypt is less than 16 bytes and can be guaranteed to be a
// single block, so we encrypt it using AES ECB mode (Approved by: b/73360609)
AES_encrypt(bytes_to_encrypt.data(), encrypted_bytes.data(), &aes_key);
return encrypted_bytes;
}
} // namespace fastpair
} // namespace nearby
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2022 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.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_ENCRYPTION_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_ENCRYPTION_H_
#include <stdint.h>
#include <array>
#include <string>
#include <string_view>
#include "absl/types/optional.h"
#include "fastpair/common/constant.h"
#include "fastpair/handshake/fast_pair_key_pair.h"
namespace nearby {
namespace fastpair {
/** Utilities used for generating Ecdh key agreement and
* encrypting Fast Pair packets. */
class FastPairEncryption {
public:
static absl::optional<KeyPair> GenerateKeysWithEcdhKeyAgreement(
std::string_view decoded_public_anti_spoofing);
static std::array<uint8_t, kAesBlockByteSize> EncryptBytes(
const std::array<uint8_t, kAesBlockByteSize>& aes_key_bytes,
const std::array<uint8_t, kAesBlockByteSize>& bytes_to_encrypt);
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_ENCRYPTION_H_
@@ -0,0 +1,84 @@
// Copyright 2022 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/handshake/fast_pair_encryption.h"
#include <algorithm>
#include <array>
#include <iterator>
#include <string>
#include "gtest/gtest.h"
#include "absl/strings/escaping.h"
namespace nearby {
namespace fastpair {
namespace {
// All test data comes from
// https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#test_cases
constexpr std::array<uint8_t, kAesBlockByteSize> aes_key_bytes = {
0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F, 0xF7, 0xB6,
0xCF, 0x5E, 0x3F, 0x45, 0x61, 0xC3, 0x32, 0x1D};
std::string DecodeKey(const std::string& encoded_key) {
std::string key;
absl::Base64Unescape(encoded_key, &key);
return key;
}
class FastPairEncryptionTest : public testing::Test {};
TEST(FastPairEncryptionTest, EncryptBytes_Success) {
constexpr std::array<uint8_t, kAesBlockByteSize> input = {
0xF3, 0x0F, 0x4E, 0x78, 0x6C, 0x59, 0xA7, 0xBB,
0xF3, 0x87, 0x3B, 0x5A, 0x49, 0xBA, 0x97, 0xEA};
constexpr std::array<uint8_t, kAesBlockByteSize> expected = {
0xAC, 0x9A, 0x16, 0xF0, 0x95, 0x3A, 0x3F, 0x22,
0x3D, 0xD1, 0x0C, 0xF5, 0x36, 0xE0, 0x9E, 0x9C};
EXPECT_EQ(FastPairEncryption::EncryptBytes(aes_key_bytes, input), expected);
}
TEST(FastPairEncryptionTest, GenerateKeysWithEcdhKeyAgreement_EmptyKey) {
EXPECT_FALSE(
FastPairEncryption::GenerateKeysWithEcdhKeyAgreement("").has_value());
}
TEST(FastPairEncryptionTest, GenerateKeysWithEcdhKeyAgreement_ShortKey) {
EXPECT_FALSE(FastPairEncryption::GenerateKeysWithEcdhKeyAgreement("too_short")
.has_value());
}
TEST(FastPairEncryptionTest, GenerateKeysWithEcdhKeyAgreement_InvalidKey) {
EXPECT_FALSE(
FastPairEncryption::GenerateKeysWithEcdhKeyAgreement(
DecodeKey("U2PWc3FHTxah/o0YT9n1VRvtm57SNIRSXOEBXm4fdtMo+06tNoFlt8D0/"
"2BsN8auolz5ikwLRvQh+MiQ6oYveg=="))
.has_value());
}
TEST(FastPairEncryptionTest, GenerateKeysWithEcdhKeyAgreement_ValidKey) {
EXPECT_TRUE(
FastPairEncryption::GenerateKeysWithEcdhKeyAgreement(
DecodeKey("U2PWc3FHTxah/o0YU9n1VRvtm57SNIRSXOEBXm4fdtMo+06tNoFlt8D0/"
"2BsN8auolz5ikwLRvQh+MiQ6oYveg=="))
.has_value());
}
} // namespace
} // namespace fastpair
} // namespace nearby
+46
View File
@@ -0,0 +1,46 @@
// Copyright 2022 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.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_KEY_PAIR_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_KEY_PAIR_H_
#include <stddef.h>
#include <stdint.h>
#include <algorithm>
#include <array>
#include <utility>
#include "fastpair/common/constant.h"
namespace nearby {
namespace fastpair {
// Key pair structure to represent public and private keys used for encryption/
// decryption.
struct KeyPair {
KeyPair(
const std::array<uint8_t, kSharedSecretKeyByteSize>& shared_secret_key,
const std::array<uint8_t, kPublicKeyByteSize>& public_key)
: shared_secret_key(std::move(shared_secret_key)),
public_key(std::move(public_key)) {}
const std::array<uint8_t, kSharedSecretKeyByteSize> shared_secret_key;
const std::array<uint8_t, kPublicKeyByteSize> public_key;
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_KEY_PAIR_H_
@@ -0,0 +1,53 @@
// Copyright 2022 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/handshake/fast_pair_key_pair.h"
#include <array>
#include "gmock/gmock.h"
#include "protobuf-matchers/protocol-buffer-matchers.h"
#include "gtest/gtest.h"
namespace nearby {
namespace fastpair {
namespace {
// Test data comes from
// https://developers.google.com/nearby/fast-pair/specifications/appendix/testcases#test_cases
TEST(KeyPairTest, CreateKeyPair) {
// Shared secret key bytes.
constexpr std::array<uint8_t, kSharedSecretKeyByteSize>
shared_secret_key_bytes = {0xA0, 0xBA, 0xF0, 0xBB, 0x95, 0x1F,
0xF7, 0xB6, 0xCF, 0x5E, 0x3F, 0x45,
0x61, 0xC3, 0x32, 0x1D};
// Public key bytes.
constexpr std::array<uint8_t, kPublicKeyByteSize> public_key_bytes = {
0x04, 0xb9, 0xda, 0x0d, 0x71, 0x60, 0xb3, 0x63, 0x28, 0x22, 0x67,
0xe7, 0xe0, 0xa3, 0xf8, 0x00, 0x8e, 0x4c, 0x89, 0xed, 0x31, 0x34,
0xf6, 0xdb, 0xc4, 0xfe, 0x0b, 0x5d, 0xe1, 0x11, 0x39, 0x49, 0xa6,
0x50, 0xa8, 0xe3, 0x4a, 0xc0, 0x40, 0x88, 0xb8, 0x38, 0x3f, 0x56,
0xfb, 0x33, 0x8d, 0xd4, 0x64, 0x91, 0xd6, 0x15, 0x77, 0x42, 0x27,
0xc5, 0xaa, 0x44, 0xff, 0xab, 0x4d, 0xb5, 0x7e, 0x25};
KeyPair keyPair(shared_secret_key_bytes, public_key_bytes);
EXPECT_EQ(keyPair.shared_secret_key, shared_secret_key_bytes);
EXPECT_EQ(keyPair.public_key, public_key_bytes);
}
} // namespace
} // namespace fastpair
} // namespace nearby
@@ -0,0 +1,38 @@
// Copyright 2022 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.
#ifndef THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_MESSAGE_TYPE_H_
#define THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_MESSAGE_TYPE_H_
namespace nearby {
namespace fastpair {
// Type values for Fast Pair messages.
enum class FastPairMessageType {
// Key-based Pairing Request.
kKeyBasedPairingRequest,
// Key-based Pairing Response.
kKeyBasedPairingResponse,
// Seeker's passkey.
kSeekersPasskey,
// Provider's passkey.
kProvidersPasskey,
// Unknown message type.
kUnknown,
};
} // namespace fastpair
} // namespace nearby
#endif // THIRD_PARTY_NEARBY_FASTPAIR_HANDSHAKE_FAST_PAIR_MESSAGE_TYPE_H_