diff --git a/presence/implementation/BUILD b/presence/implementation/BUILD index 773f2357..ff2da2aa 100644 --- a/presence/implementation/BUILD +++ b/presence/implementation/BUILD @@ -15,7 +15,10 @@ licenses(["notice"]) cc_library( name = "internal", - srcs = ["advertisement_decoder.cc"], + srcs = [ + "advertisement_decoder.cc", + "credential_manager_impl.cc", + ], hdrs = [ "advertisement_decoder.h", "broadcast_manager.h", @@ -30,12 +33,16 @@ cc_library( "//third_party/nearby/presence:__subpackages__", ], deps = [ + "//internal/crypto", + "//internal/platform:base", "//internal/platform:comm", "//internal/platform:logging", "//internal/platform/implementation:comm", + "//internal/platform/implementation:types", "//third_party/nearby/presence:action_factory", "//third_party/nearby/presence:advertisement_factory", "//third_party/nearby/presence:credential", + "//third_party/nearby/presence:encryption", "//third_party/nearby/presence:types", "//third_party/nearby/presence/implementation/mediums", "//third_party/nearby/presence/proto:credential_cc_proto", @@ -59,3 +66,19 @@ cc_test( "@com_google_googletest//:gtest_main", ], ) + +cc_test( + name = "credential_manager_impl_test", + size = "small", + srcs = ["credential_manager_impl_test.cc"], + deps = [ + ":internal", + "//net/proto2/contrib/parse_proto:testing", + "//internal/crypto", + "//internal/platform/implementation:types", + "//internal/platform/implementation/g3", # build_cleaner: keep + "//third_party/nearby/presence:encryption", + "@com_github_protobuf_matchers//protobuf-matchers", + "@com_google_googletest//:gtest_main", + ], +) diff --git a/presence/implementation/credential_manager.h b/presence/implementation/credential_manager.h index 13768807..c2a468d6 100644 --- a/presence/implementation/credential_manager.h +++ b/presence/implementation/credential_manager.h @@ -57,7 +57,8 @@ class CredentialManager { // storage. virtual void GenerateCredentials( proto::DeviceMetadata device_metadata, - std::vector identity_types, + std::vector identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, GenerateCredentialsCallback credentials_generated_cb) = 0; // Update remote public credentials. diff --git a/presence/implementation/credential_manager_impl.cc b/presence/implementation/credential_manager_impl.cc new file mode 100644 index 00000000..4a0c0d0c --- /dev/null +++ b/presence/implementation/credential_manager_impl.cc @@ -0,0 +1,173 @@ +// 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/implementation/credential_manager_impl.h" + +#include +#include +#include +#include + +#include "internal/crypto/ec_private_key.h" +#include "internal/crypto/encryptor.h" +#include "internal/crypto/symmetric_key.h" +#include "internal/platform/base64_utils.h" +#include "internal/platform/implementation/crypto.h" +#include "internal/platform/logging.h" +#include "third_party/nearby/presence/encryption.h" + +namespace nearby { +namespace presence { +namespace { +using ::location::nearby::Base64Utils; +using ::location::nearby::Crypto; +using ::nearby::presence::proto::PrivateCredential; +using ::nearby::presence::proto::PublicCredential; + +// Key to retrieve local device's Private/Public Key Credentials from key store. +constexpr char kPairedKeyAliasPrefix[] = "nearby_presence_paired_key_alias_"; + +} // namespace + +void CredentialManagerImpl::GenerateCredentials( + proto::DeviceMetadata device_metadata, + std::vector identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsCallback credentials_generated_cb) { + std::vector public_credentials; + std::vector private_credentials; + + for (auto identity_type : identity_types) { + // TODO(b/241587906): Get linux time from the platform (like Android) + uint64_t start_time_millis = 0; + const uint64_t gap_millis = credential_life_cycle_days * 24 * 3600 * 1000; + uint64_t end_time_millis = start_time_millis + gap_millis; + + for (int index = 0; index < contiguous_copy_of_credentials; index++) { + auto public_private_credentials = CreatePrivateCredential( + device_metadata, identity_type, start_time_millis, end_time_millis); + if (public_private_credentials.first != nullptr) { + private_credentials.push_back(*public_private_credentials.first); + public_credentials.push_back(*public_private_credentials.second); + } + start_time_millis += gap_millis; + end_time_millis += gap_millis; + } + } + // TODO(b/241488275) Store all the public and private credentials and call the + // callback to inform client. + credentials_generated_cb.credentials_generated_cb(public_credentials); +} + +std::pair, std::unique_ptr> +CredentialManagerImpl::CreatePrivateCredential( + proto::DeviceMetadata device_metadata, proto::IdentityType identity_type, + uint64_t start_time_ms, uint64_t end_time_ms) { + auto private_credential_ptr = std::make_unique(); + private_credential_ptr->set_start_time_millis(start_time_ms); + private_credential_ptr->set_end_time_millis(end_time_ms); + private_credential_ptr->set_identity_type(identity_type); + + // Creates an AES key to encrypt the whole broadcast. + std::string secret_key = + Encryption::GenerateRandomByteArray(kAuthenticityKeyByteSize); + private_credential_ptr->set_authenticity_key(secret_key); + + // Uses SHA-256 algorithm to generate the credential ID from the authenticity + // key + auto secret_id = Crypto::Sha256(secret_key); + if (secret_id.Empty()) { + NEARBY_LOG(ERROR, + "Failed to create private credential because it failed to " + "create a secret id."); + return std::pair, + std::unique_ptr>( + std::unique_ptr(nullptr), + std::unique_ptr(nullptr)); + } + private_credential_ptr->set_secret_id(secret_id.AsStringView()); + + std::string alias = Base64Utils::Encode(secret_id); + auto prefixedAlias = kPairedKeyAliasPrefix + alias; + + // Generate key pair. Store the private key in private credential. + auto key_pair = crypto::ECPrivateKey::Create(); + std::vector private_key; + key_pair->ExportPrivateKey(&private_key); + private_credential_ptr->set_verification_key( + std::string(private_key.begin(), private_key.end())); + + // Create an AES key to encrypt the device metadata. + auto metadata_key = + Encryption::GenerateRandomByteArray(kAuthenticityKeyByteSize); + private_credential_ptr->set_metadata_encryption_key(metadata_key); + + // set device meta data + *(private_credential_ptr->mutable_device_metadata()) = device_metadata; + + // Generate the public credential + std::vector public_key; + key_pair->ExportPublicKey(&public_key); + auto public_credential_ptr = + CreatePublicCredential(private_credential_ptr.get(), &public_key); + + return std::pair, + std::unique_ptr>( + std::move(private_credential_ptr), std::move(public_credential_ptr)); +} + +std::unique_ptr +CredentialManagerImpl::CreatePublicCredential( + proto::PrivateCredential* private_credential, + std::vector* public_key) { + auto public_credential_ptr = std::make_unique(); + + public_credential_ptr->set_identity_type(private_credential->identity_type()); + public_credential_ptr->set_secret_id(private_credential->secret_id()); + public_credential_ptr->set_authenticity_key( + private_credential->authenticity_key()); + public_credential_ptr->set_start_time_millis( + private_credential->start_time_millis()); + public_credential_ptr->set_end_time_millis( + private_credential->end_time_millis()); + // set up the public key + public_credential_ptr->set_verification_key( + std::string(public_key->begin(), public_key->end())); + + auto metadata_encryption_key_tag = + Crypto::Sha256(private_credential->metadata_encryption_key()); + public_credential_ptr->set_metadata_encryption_key_tag( + metadata_encryption_key_tag.AsStringView()); + + // Encrypt the device metadata + crypto::Encryptor encryptor; + auto sym_key = crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, private_credential->metadata_encryption_key()); + + auto iv = Encryption::CustomizeBytesSize( + private_credential->authenticity_key(), kAesGcmIVSize); + // It is GCM in the spec. Here we use CBC instead since GCM is not supported + // now. + if (!encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv)) { + NEARBY_LOG(ERROR, "Fails to initialize the encryptor"); + return std::unique_ptr(nullptr); + } + + encryptor.Encrypt(private_credential->device_metadata().SerializeAsString(), + public_credential_ptr->mutable_encrypted_metadata_bytes()); + return public_credential_ptr; +} + +} // namespace presence +} // namespace nearby diff --git a/presence/implementation/credential_manager_impl.h b/presence/implementation/credential_manager_impl.h index abaef319..a85624bc 100644 --- a/presence/implementation/credential_manager_impl.h +++ b/presence/implementation/credential_manager_impl.h @@ -15,7 +15,9 @@ #ifndef THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_IMPL_H_ #define THIRD_PARTY_NEARBY_PRESENCE_IMPLEMENTATION_CREDENTIAL_MANAGER_IMPL_H_ +#include #include +#include #include #include "absl/status/status.h" @@ -33,10 +35,17 @@ class CredentialManagerImpl : public CredentialManager { public: CredentialManagerImpl() = default; + // AES only supports key sizes of 16, 24 or 32 bytes. + static constexpr int kAuthenticityKeyByteSize = 16; + + // Modify this to 12 after use real AES. + static constexpr int kAesGcmIVSize = 16; + void GenerateCredentials( proto::DeviceMetadata device_metadata, - std::vector identity_types, - GenerateCredentialsCallback credentials_generated_cb) override {} + std::vector identity_types, + int credential_life_cycle_days, int contiguous_copy_of_credentials, + GenerateCredentialsCallback credentials_generated_cb); void UpdateRemotePublicCredentials( std::string account_name, @@ -61,8 +70,17 @@ class CredentialManagerImpl : public CredentialManager { } private: - location::nearby::CredentialStorage* - credential_storage_; // NOLINT: further impl will use it. + FRIEND_TEST(CredentialManagerImpl, CreateOneCredentialSuccessfully); + + std::pair, + std::unique_ptr> + CreatePrivateCredential(proto::DeviceMetadata device_metadata, + proto::IdentityType identity_type, + uint64_t start_time_ms, uint64_t end_time_ms); + + std::unique_ptr CreatePublicCredential( + proto::PrivateCredential* private_credential_ptr, + std::vector* public_key); }; } // namespace presence diff --git a/presence/implementation/credential_manager_impl_test.cc b/presence/implementation/credential_manager_impl_test.cc new file mode 100644 index 00000000..e2a4e6fa --- /dev/null +++ b/presence/implementation/credential_manager_impl_test.cc @@ -0,0 +1,98 @@ +// 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/implementation/credential_manager_impl.h" + +#include + +#include "net/proto2/contrib/parse_proto/testing.h" +#include "gmock/gmock.h" +#include "protobuf-matchers/protocol-buffer-matchers.h" +#include "gtest/gtest.h" +#include "internal/crypto/encryptor.h" +#include "internal/crypto/symmetric_key.h" +#include "internal/platform/implementation/crypto.h" +#include "third_party/nearby/presence/encryption.h" + +namespace nearby { +namespace presence { +using ::location::nearby::Crypto; + +using ::nearby::presence::proto::IdentityType::IDENTITY_TYPE_PRIVATE; +using ::proto2::contrib::parse_proto::ParseTestProto; +using ::protobuf_matchers::EqualsProto; + +// TODO(b/241926454): Make sure CredentialManager builds with Github. +TEST(CredentialManagerImpl, CreateOneCredentialSuccessfully) { + proto::DeviceMetadata device_metadata = ParseTestProto(R"pb( + stable_device_id: "test_device_id" + ; + account_name: "test_account"; + device_name: "NP test device"; + icon_url: "test_image.test.com" + bluetooth_mac_address: "FF:FF:FF:FF:FF:FF"; + device_type: PHONE; + )pb"); + + CredentialManagerImpl credential_manager; + auto credentials = credential_manager.CreatePrivateCredential( + device_metadata, IDENTITY_TYPE_PRIVATE, /* start_time_ms= */ 0, + /* end_time_ms= */ 1000); + + proto::PrivateCredential* private_credential = credentials.first.get(); + proto::PublicCredential* public_credential = credentials.second.get(); + + // Verify the private credential. + EXPECT_THAT(private_credential->device_metadata(), + EqualsProto(device_metadata)); + EXPECT_EQ(private_credential->identity_type(), IDENTITY_TYPE_PRIVATE); + EXPECT_FALSE(private_credential->secret_id().empty()); + EXPECT_EQ(private_credential->start_time_millis(), 0); + EXPECT_EQ(private_credential->end_time_millis(), 1000); + EXPECT_EQ(private_credential->authenticity_key().size(), + CredentialManagerImpl::kAuthenticityKeyByteSize); + EXPECT_FALSE(private_credential->verification_key().empty()); + EXPECT_EQ(private_credential->metadata_encryption_key().size(), + CredentialManagerImpl::kAuthenticityKeyByteSize); + + // Verify the public credential. + EXPECT_EQ(public_credential->identity_type(), IDENTITY_TYPE_PRIVATE); + EXPECT_FALSE(public_credential->secret_id().empty()); + EXPECT_EQ(private_credential->authenticity_key(), + public_credential->authenticity_key()); + EXPECT_EQ(public_credential->start_time_millis(), 0); + EXPECT_EQ(public_credential->end_time_millis(), 1000); + EXPECT_EQ(Crypto::Sha256(private_credential->metadata_encryption_key()) + .AsStringView(), + public_credential->metadata_encryption_key_tag()); + EXPECT_FALSE(public_credential->verification_key().empty()); + EXPECT_FALSE(public_credential->encrypted_metadata_bytes().empty()); + + // Decrypt the device metadata + crypto::Encryptor encryptor; + auto sym_key = crypto::SymmetricKey::Import( + crypto::SymmetricKey::AES, private_credential->metadata_encryption_key()); + EXPECT_TRUE(sym_key.get() != nullptr); + auto iv = + Encryption::CustomizeBytesSize(private_credential->authenticity_key(), + CredentialManagerImpl::kAesGcmIVSize); + encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv); + std::string decrypted_metadata; + EXPECT_TRUE(encryptor.Decrypt(public_credential->encrypted_metadata_bytes(), + &decrypted_metadata)); + EXPECT_EQ(private_credential->device_metadata().SerializeAsString(), + decrypted_metadata); +} +} // namespace presence +} // namespace nearby