From 09c04d4cc8392a209ce399f48ff2dd251cad7988 Mon Sep 17 00:00:00 2001 From: jsobczak Date: Wed, 1 Jun 2022 22:35:35 -0700 Subject: [PATCH] Build visibility changes PiperOrigin-RevId: 452460772 --- internal/platform/BUILD | 1 + internal/platform/implementation/g3/BUILD | 1 + presence/BUILD | 29 +++++ presence/encryption.cc | 111 ++++++++++++++++++ presence/encryption.h | 74 ++++++++++++ presence/encryption_test.cc | 131 ++++++++++++++++++++++ 6 files changed, 347 insertions(+) create mode 100644 presence/encryption.cc create mode 100644 presence/encryption.h create mode 100644 presence/encryption_test.cc diff --git a/internal/platform/BUILD b/internal/platform/BUILD index 6ea26e6b..ed01f07b 100644 --- a/internal/platform/BUILD +++ b/internal/platform/BUILD @@ -107,6 +107,7 @@ cc_library( "//internal/analytics:__subpackages__", "//internal/platform:__subpackages__", "//internal/proto/analytics:__subpackages__", + "//third_party/nearby/presence:__pkg__", ], deps = [ "//internal/platform/implementation:platform", diff --git a/internal/platform/implementation/g3/BUILD b/internal/platform/implementation/g3/BUILD index ea1486cc..a7447c86 100644 --- a/internal/platform/implementation/g3/BUILD +++ b/internal/platform/implementation/g3/BUILD @@ -115,6 +115,7 @@ cc_library( "//internal/platform:__subpackages__", "//internal/proto/analytics:__subpackages__", "//location/nearby/cpp/sharing:__subpackages__", + "//third_party/nearby/presence:__subpackages__", ], deps = [ ":comm", diff --git a/presence/BUILD b/presence/BUILD index 4848e991..dc7571f7 100644 --- a/presence/BUILD +++ b/presence/BUILD @@ -64,6 +64,35 @@ cc_library( ], ) +cc_library( + name = "encryption", + srcs = ["encryption.cc"], + hdrs = ["encryption.h"], + deps = [ + "//internal/platform:logging", + "//third_party/tink/cc/subtle", + "//util/random:ssl_bit_gen", + "@boringssl//:crypto", + "@com_google_absl//absl/status", + "@com_google_absl//absl/status:statusor", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + ], +) + +cc_test( + name = "encryption_test", + size = "small", + srcs = ["encryption_test.cc"], + deps = [ + ":encryption", + "//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 = "types_test", size = "small", diff --git a/presence/encryption.cc b/presence/encryption.cc new file mode 100644 index 00000000..bbb2bf8b --- /dev/null +++ b/presence/encryption.cc @@ -0,0 +1,111 @@ +// 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 "third_party/nearby/presence/encryption.h" + +#include +#include +#include + +#include "absl/status/status.h" +#include "absl/status/statusor.h" +#include "absl/strings/str_format.h" +#include "internal/platform/logging.h" +#include "third_party/openssl/cipher.h" // NOLINT +#include "third_party/openssl/crypto.h" +#include "third_party/openssl/evp.h" // NOLINT +#include "third_party/tink/cc/subtle/hkdf.h" +#include "third_party/tink/cc/subtle/random.h" +#include "util/random/ssl_bit_gen.h" + +namespace nearby { +namespace presence { + +using ::crypto::tink::subtle::HashType; +using ::crypto::tink::subtle::Hkdf; +using ::crypto::tink::subtle::Random; +constexpr int kAuthenticityKeyByteSize = 16; +constexpr int kMetadataKeyMaxSize = 16; +constexpr int kAesCtrIvSize = 16; +constexpr int kSaltSize = 2; + +std::string Encryption::CustomizeBytesSize(absl::string_view bytes, + size_t len) { + auto result = + Hkdf::ComputeHkdf(HashType::SHA256, /*ikm=*/bytes, + /*salt=*/std::string(kAuthenticityKeyByteSize, 0), + /*info=*/"", /*out_len=*/len); + return result.value(); +} + +std::string Encryption::GenerateRandomByteArray(size_t len) { + return Random::GetRandomBytes(len); +} + +absl::StatusOr Encryption::RunMetadataEncryption( + absl::string_view metadata, absl::string_view key, absl::string_view salt, + bool encrypt) { + if (metadata.size() > kMetadataKeyMaxSize) { + return absl::InvalidArgumentError( + absl::StrFormat("Metadata key length %d greater than %d", + metadata.size(), kMetadataKeyMaxSize)); + } + if (key.size() != kAuthenticityKeyByteSize) { + return absl::InvalidArgumentError( + absl::StrFormat("Invalid authenticity key length %d. Expected %d", + key.size(), kAuthenticityKeyByteSize)); + } + if (salt.size() != kSaltSize) { + return absl::InvalidArgumentError(absl::StrFormat( + "Invalid salt length %d, Expected %d.", salt.size(), kSaltSize)); + } + auto output = std::string(metadata.size(), 0); + int output_size; + std::string iv = CustomizeBytesSize(salt, kAesCtrIvSize); + + // AES-CTR is used without authentication because it's used as a PRF. + auto ctx = + std::unique_ptr>( + EVP_CIPHER_CTX_new(), EVP_CIPHER_CTX_free); + if (1 != EVP_CipherInit_ex(ctx.get(), EVP_aes_128_ctr(), nullptr, + reinterpret_cast(key.data()), + reinterpret_cast(iv.data()), + encrypt ? 1 : 0)) { + return absl::InvalidArgumentError("Failed to initialize AES encryption."); + } + + int input_size = metadata.size(); + if (1 != EVP_CipherUpdate( + ctx.get(), reinterpret_cast(output.data()), + &output_size, reinterpret_cast(metadata.data()), + input_size)) { + return absl::InvalidArgumentError("AES error in EVP_CipherUpdate"); + } + int tmp_size = 0; + if (1 != EVP_EncryptFinal_ex( + ctx.get(), + reinterpret_cast(output.data() + output_size), + &tmp_size)) { + return absl::InvalidArgumentError("AES errorin EVP_EncryptFinal_ex"); + } + output_size += tmp_size; + if (output_size != input_size) { + return absl::InvalidArgumentError(absl::StrFormat( + "Invalid output size %d. Expected %d", output_size, input_size)); + } + return output; +} + +} // namespace presence +} // namespace nearby diff --git a/presence/encryption.h b/presence/encryption.h new file mode 100644 index 00000000..7d745216 --- /dev/null +++ b/presence/encryption.h @@ -0,0 +1,74 @@ +// 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_PRESENCE_ENCRYPTION_H_ +#define THIRD_PARTY_NEARBY_PRESENCE_ENCRYPTION_H_ + +#include + +#include "absl/status/statusor.h" +#include "absl/strings/string_view.h" + +namespace nearby { +namespace presence { + +/** Utilities for encryption and decryption of presence advertisements. */ +class Encryption { + public: + /** + * Uses a HMAC based HKDF algorithm to generate and return a + * new byte array of given length based on input bytes and a zero-initialized + * salt. + */ + static std::string CustomizeBytesSize(absl::string_view bytes, size_t len); + + /** Generates a random byte array of given size. */ + static std::string GenerateRandomByteArray(size_t len); + + /** Generates encrypted metadata key. `metadata_encryption_key` must be 14 or + * 16 bytes long. `authenticity_key` is the AES key and must be 128 bit (16 + * bytes) long. `salt` must be 2 bytes long. + * + * Note, because the salt is ony 16 bit long, the caller should not reuse + * salts. Reusing salts will expose the encrypted metadata key. The solution + * in NP is to store all used salts and rotate the autheticity key when we run + * out of salts. + */ + static absl::StatusOr GenerateEncryptedMetadataKey( + absl::string_view metadata_encryption_key, + absl::string_view authenticity_key, absl::string_view salt) { + return RunMetadataEncryption(metadata_encryption_key, authenticity_key, + salt, /*encrypt= */ true); + } + + /** Generates decrypted metadata key. `encrypted_metadata_key` must be 14 or + * 16 bytes long. `authenticity_key` is the AES key and must be 128 bit (16 + * bytes) long. `salt` must be 2 bytes long.*/ + static absl::StatusOr GenerateDecryptedMetadataKey( + absl::string_view encrypted_metadata_key, + absl::string_view authenticity_key, absl::string_view salt) { + return RunMetadataEncryption(encrypted_metadata_key, authenticity_key, salt, + /*encrypt= */ false); + } + + private: + static absl::StatusOr RunMetadataEncryption( + absl::string_view metadata, absl::string_view key, absl::string_view salt, + bool encrypt); +}; + +} // namespace presence +} // namespace nearby + +#endif // THIRD_PARTY_NEARBY_PRESENCE_ENCRYPTION_H_ diff --git a/presence/encryption_test.cc b/presence/encryption_test.cc new file mode 100644 index 00000000..03accd40 --- /dev/null +++ b/presence/encryption_test.cc @@ -0,0 +1,131 @@ +// 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 "third_party/nearby/presence/encryption.h" + +#include + +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "absl/strings/escaping.h" + +namespace nearby { +namespace presence { +namespace { + +TEST(EncryptionTest, CustomizeBytesSize) { + std::string ikm = "Input data"; + std::string kExpectedOutputHex = "51d2c0506732febbccf093066db66f269682137e"; + + std::string result = Encryption::CustomizeBytesSize(ikm, 20); + + EXPECT_EQ(absl::BytesToHexString(result), kExpectedOutputHex); +} + +TEST(EncryptionTest, GenerateRandomByteArray) { + std::string random1 = Encryption::GenerateRandomByteArray(10); + std::string random2 = Encryption::GenerateRandomByteArray(10); + + EXPECT_NE(random1, random2); +} + +TEST(EncryptionTest, GenerateEncryptedMetadataKeyFor14bytes) { + const std::string kAuthenticityKey = + absl::HexStringToBytes("20212223242526272829303132333435"); + const std::string kSalt = absl::HexStringToBytes("0102"); + const std::string kMetadataKey = + absl::HexStringToBytes("4041424344454647484950515253"); + const std::string kExpectedOutputHex = "637b4868fcbcb3d8a67c47481807"; + + auto result = Encryption::GenerateEncryptedMetadataKey( + kMetadataKey, kAuthenticityKey, kSalt); + + EXPECT_TRUE(result.ok()); + EXPECT_EQ(absl::BytesToHexString(result.value()), kExpectedOutputHex); +} + +TEST(EncryptionTest, GenerateEncryptedMetadataKeyFor16bytes) { + const std::string kAuthenticityKey = + absl::HexStringToBytes("20212223242526272829303132333435"); + const std::string kSalt = absl::HexStringToBytes("0102"); + const std::string kMetadataKey = + absl::HexStringToBytes("40414243444546474849505152535455"); + const std::string kExpectedOutputHex = "637b4868fcbcb3d8a67c47481807d139"; + + auto result = Encryption::GenerateEncryptedMetadataKey( + kMetadataKey, kAuthenticityKey, kSalt); + + EXPECT_TRUE(result.ok()); + EXPECT_EQ(absl::BytesToHexString(result.value()), kExpectedOutputHex); +} + +TEST(EncryptionTest, GenerateDecryptedMetadataKeyFor14bytes) { + const std::string kAuthenticityKey = + absl::HexStringToBytes("20212223242526272829303132333435"); + const std::string kSalt = absl::HexStringToBytes("0102"); + const std::string kMetadataKeyHex = "4041424344454647484950515253"; + const std::string kEncryptedMetadata = + absl::HexStringToBytes("637b4868fcbcb3d8a67c47481807"); + + auto result = Encryption::GenerateDecryptedMetadataKey( + kEncryptedMetadata, kAuthenticityKey, kSalt); + + EXPECT_TRUE(result.ok()); + EXPECT_EQ(absl::BytesToHexString(result.value()), kMetadataKeyHex); +} + +TEST(EncryptionTest, GenerateDecryptedMetadataKeyFor16bytes) { + const std::string kAuthenticityKey = + absl::HexStringToBytes("20212223242526272829303132333435"); + const std::string kSalt = absl::HexStringToBytes("0102"); + const std::string kMetadataKeyHex = "40414243444546474849505152535455"; + const std::string kEncryptedMetadata = + absl::HexStringToBytes("637b4868fcbcb3d8a67c47481807d139"); + + auto result = Encryption::GenerateDecryptedMetadataKey( + kEncryptedMetadata, kAuthenticityKey, kSalt); + + EXPECT_TRUE(result.ok()); + EXPECT_EQ(absl::BytesToHexString(result.value()), kMetadataKeyHex); +} + +TEST(EncryptionTest, RejectTooLongMetadataKey) { + const std::string kAuthenticityKey = + absl::HexStringToBytes("20212223242526272829303132333435"); + const std::string kSalt = absl::HexStringToBytes("0102"); + const std::string kMetadataKey = + absl::HexStringToBytes("4041424344454647484950515253545566"); + + auto result = Encryption::GenerateEncryptedMetadataKey( + kMetadataKey, kAuthenticityKey, kSalt); + + EXPECT_FALSE(result.ok()); +} + +TEST(EncryptionTest, RejectInvalidAuthenticityKeySize) { + const std::string kAuthenticityKey = + absl::HexStringToBytes("2021222324252627282930313233343546"); + const std::string kSalt = absl::HexStringToBytes("0102"); + const std::string kMetadataKey = + absl::HexStringToBytes("40414243444546474849505152535455"); + + auto result = Encryption::GenerateEncryptedMetadataKey( + kMetadataKey, kAuthenticityKey, kSalt); + + EXPECT_FALSE(result.ok()); +} +} // namespace +} // namespace presence +} // namespace nearby