123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672 |
- // Copyright 2015 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "components/gcm_driver/crypto/gcm_encryption_provider.h"
- #include <stddef.h>
- #include <memory>
- #include <sstream>
- #include <string>
- #include "base/base64.h"
- #include "base/base64url.h"
- #include "base/big_endian.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/files/scoped_temp_dir.h"
- #include "base/run_loop.h"
- #include "base/strings/string_number_conversions.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/string_util.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "base/test/task_environment.h"
- #include "base/threading/thread_task_runner_handle.h"
- #include "components/gcm_driver/common/gcm_message.h"
- #include "components/gcm_driver/crypto/gcm_decryption_result.h"
- #include "components/gcm_driver/crypto/gcm_encryption_result.h"
- #include "components/gcm_driver/crypto/gcm_key_store.h"
- #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
- #include "components/gcm_driver/crypto/p256_key_util.h"
- #include "crypto/random.h"
- #include "testing/gtest/include/gtest/gtest.h"
- namespace gcm {
- namespace {
- const char kExampleAppId[] = "my-app-id";
- const char kExampleAuthorizedEntity[] = "my-sender-id";
- const char kExampleMessage[] = "Hello, world, this is the GCM Driver!";
- const char kValidEncryptionHeader[] =
- "keyid=foo;salt=MTIzNDU2Nzg5MDEyMzQ1Ng;rs=1024";
- const char kInvalidEncryptionHeader[] = "keyid";
- const char kValidCryptoKeyHeader[] =
- "keyid=foo;dh=BL_UGhfudEkXMUd4U4-D4nP5KHxKjQHsW6j88ybbehXM7fqi1OMFefDUEi0eJ"
- "vsKfyVBWYkQjH-lSPJKxjAyslg";
- const char kValidThreeValueCryptoKeyHeader[] =
- "keyid=foo,keyid=bar,keyid=baz;dh=BL_UGhfudEkXMUd4U4-D4nP5KHxKjQHsW6j88ybbe"
- "hXM7fqi1OMFefDUEi0eJvsKfyVBWYkQjH-lSPJKxjAyslg";
- const char kInvalidCryptoKeyHeader[] = "keyid";
- const char kInvalidThreeValueCryptoKeyHeader[] =
- "keyid=foo,dh=BL_UGhfudEkXMUd4U4-D4nP5KHxKjQHsW6j88ybbehXM7fqi1OMFefDUEi0eJ"
- "vsKfyVBWYkQjH-lSPJKxjAyslg,keyid=baz,dh=BL_UGhfudEkXMUd4U4-D4nP5KHxKjQHsW6"
- "j88ybbehXM7fqi1OMFefDUEi0eJvsKfyVBWYkQjH-lSPJKxjAyslg";
- } // namespace
- using ECPrivateKeyUniquePtr = std::unique_ptr<crypto::ECPrivateKey>;
- class GCMEncryptionProviderTest : public ::testing::Test {
- public:
- void SetUp() override {
- ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir());
- encryption_provider_ = std::make_unique<GCMEncryptionProvider>();
- encryption_provider_->Init(scoped_temp_dir_.GetPath(),
- base::ThreadTaskRunnerHandle::Get());
- }
- void TearDown() override {
- encryption_provider_.reset();
- // |encryption_provider_| owns a ProtoDatabase whose destructor deletes
- // the underlying LevelDB database on the task runner.
- base::RunLoop().RunUntilIdle();
- }
- // To be used as a callback for GCMEncryptionProvider::GetEncryptionInfo().
- void DidGetEncryptionInfo(std::string* p256dh_out,
- std::string* auth_secret_out,
- std::string p256dh,
- std::string auth_secret) {
- *p256dh_out = std::move(p256dh);
- *auth_secret_out = std::move(auth_secret);
- }
- // To be used as a callback for GCMKeyStore::{GetKeys,CreateKeys}.
- void HandleKeysCallback(ECPrivateKeyUniquePtr* key_out,
- std::string* auth_secret_out,
- ECPrivateKeyUniquePtr key,
- const std::string& auth_secret) {
- *key_out = std::move(key);
- *auth_secret_out = auth_secret;
- }
- protected:
- // Decrypts the |message| and then synchronously waits until either the
- // success or failure callbacks has been invoked.
- void Decrypt(const IncomingMessage& message) {
- encryption_provider_->DecryptMessage(
- kExampleAppId, message,
- base::BindOnce(&GCMEncryptionProviderTest::DidDecryptMessage,
- base::Unretained(this)));
- // The encryption keys will be read asynchronously.
- base::RunLoop().RunUntilIdle();
- }
- // Encrypts the |message| and then synchronously waits until either the
- // success or failure callbacks has been invoked.
- void Encrypt(const std::string& authorized_entity,
- const std::string& p256dh,
- const std::string& auth_secret,
- const std::string& message) {
- encryption_provider_->EncryptMessage(
- kExampleAppId, authorized_entity, p256dh, auth_secret, message,
- base::BindOnce(&GCMEncryptionProviderTest::DidEncryptMessage,
- base::Unretained(this)));
- // The encryption keys will be read asynchronously.
- base::RunLoop().RunUntilIdle();
- }
- // Checks that the underlying key store has a key for the |kExampleAppId| +
- // authorized entity key if and only if |should_have_key| is true. Must wrap
- // with ASSERT/EXPECT_NO_FATAL_FAILURE.
- void CheckHasKey(const std::string& authorized_entity, bool should_have_key) {
- ECPrivateKeyUniquePtr key;
- std::string auth_secret;
- encryption_provider()->key_store_->GetKeys(
- kExampleAppId, authorized_entity,
- false /* fallback_to_empty_authorized_entity */,
- base::BindOnce(&GCMEncryptionProviderTest::HandleKeysCallback,
- base::Unretained(this), &key, &auth_secret));
- base::RunLoop().RunUntilIdle();
- if (should_have_key) {
- ASSERT_TRUE(key);
- std::string private_key, public_key;
- ASSERT_TRUE(GetRawPrivateKey(*key, &private_key));
- ASSERT_TRUE(GetRawPublicKey(*key, &public_key));
- ASSERT_GT(public_key.size(), 0u);
- ASSERT_GT(private_key.size(), 0u);
- ASSERT_GT(auth_secret.size(), 0u);
- } else {
- ASSERT_FALSE(key);
- ASSERT_EQ(0u, auth_secret.size());
- }
- }
- // Returns the result of the previous decryption operation.
- GCMDecryptionResult decryption_result() { return decryption_result_; }
- // Returns the result of the previous encryption operation.
- GCMEncryptionResult encryption_result() { return encryption_result_; }
- // Returns the message resulting from the previous decryption operation.
- const IncomingMessage& decrypted_message() { return decrypted_message_; }
- // Returns the message resulting from the previous encryption operation.
- const std::string& encrypted_message() { return encrypted_message_; }
- GCMEncryptionProvider* encryption_provider() {
- return encryption_provider_.get();
- }
- // Performs a full round-trip test of the encryption feature. Must wrap this
- // in ASSERT_NO_FATAL_FAILURE.
- void TestEncryptionRoundTrip(const std::string& app_id,
- const std::string& authorized_entity,
- GCMMessageCryptographer::Version version,
- bool use_internal_raw_data_for_draft08 = false);
- // Performs a test encryption feature without creating proper keys. Must wrap
- // this in ASSERT_NO_FATAL_FAILURE.
- void TestEncryptionNoKeys(const std::string& app_id,
- const std::string& authorized_entity);
- private:
- void DidDecryptMessage(GCMDecryptionResult result, IncomingMessage message) {
- decryption_result_ = result;
- decrypted_message_ = std::move(message);
- }
- void DidEncryptMessage(GCMEncryptionResult result, std::string message) {
- encryption_result_ = result;
- encrypted_message_ = std::move(message);
- }
- base::test::SingleThreadTaskEnvironment task_environment_;
- base::ScopedTempDir scoped_temp_dir_;
- base::HistogramTester histogram_tester_;
- std::unique_ptr<GCMEncryptionProvider> encryption_provider_;
- GCMDecryptionResult decryption_result_ = GCMDecryptionResult::UNENCRYPTED;
- GCMEncryptionResult encryption_result_ =
- GCMEncryptionResult::ENCRYPTION_FAILED;
- IncomingMessage decrypted_message_;
- std::string encrypted_message_;
- };
- TEST_F(GCMEncryptionProviderTest, IsEncryptedMessage) {
- // Both the Encryption and Encryption-Key headers must be present, and the raw
- // data must be non-empty for a message to be considered encrypted.
- IncomingMessage empty_message;
- EXPECT_FALSE(encryption_provider()->IsEncryptedMessage(empty_message));
- IncomingMessage single_header_message;
- single_header_message.data["encryption"] = "";
- EXPECT_FALSE(
- encryption_provider()->IsEncryptedMessage(single_header_message));
- IncomingMessage double_header_message;
- double_header_message.data["encryption"] = "";
- double_header_message.data["crypto-key"] = "";
- EXPECT_FALSE(
- encryption_provider()->IsEncryptedMessage(double_header_message));
- IncomingMessage double_header_with_data_message;
- double_header_with_data_message.data["encryption"] = "";
- double_header_with_data_message.data["crypto-key"] = "";
- double_header_with_data_message.raw_data = "foo";
- EXPECT_TRUE(encryption_provider()->IsEncryptedMessage(
- double_header_with_data_message));
- IncomingMessage draft08_message;
- draft08_message.data["content-encoding"] = "aes128gcm";
- draft08_message.raw_data = "foo";
- EXPECT_TRUE(encryption_provider()->IsEncryptedMessage(draft08_message));
- }
- TEST_F(GCMEncryptionProviderTest, VerifiesEncryptionHeaderParsing) {
- // The Encryption header must be parsable and contain valid values.
- // Note that this is more extensively tested in EncryptionHeaderParsersTest.
- IncomingMessage invalid_message;
- invalid_message.data["encryption"] = kInvalidEncryptionHeader;
- invalid_message.data["crypto-key"] = kValidCryptoKeyHeader;
- invalid_message.raw_data = "foo";
- ASSERT_NO_FATAL_FAILURE(Decrypt(invalid_message));
- EXPECT_EQ(GCMDecryptionResult::INVALID_ENCRYPTION_HEADER,
- decryption_result());
- IncomingMessage valid_message;
- valid_message.data["encryption"] = kValidEncryptionHeader;
- valid_message.data["crypto-key"] = kInvalidCryptoKeyHeader;
- valid_message.raw_data = "foo";
- ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message));
- EXPECT_NE(GCMDecryptionResult::INVALID_ENCRYPTION_HEADER,
- decryption_result());
- }
- TEST_F(GCMEncryptionProviderTest, VerifiesCryptoKeyHeaderParsing) {
- // The Crypto-Key header must be parsable and contain valid values.
- // Note that this is more extensively tested in EncryptionHeaderParsersTest.
- IncomingMessage invalid_message;
- invalid_message.data["encryption"] = kValidEncryptionHeader;
- invalid_message.data["crypto-key"] = kInvalidCryptoKeyHeader;
- invalid_message.raw_data = "foo";
- ASSERT_NO_FATAL_FAILURE(Decrypt(invalid_message));
- EXPECT_EQ(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
- decryption_result());
- IncomingMessage valid_message;
- valid_message.data["encryption"] = kValidEncryptionHeader;
- valid_message.data["crypto-key"] = kValidCryptoKeyHeader;
- valid_message.raw_data = "foo";
- ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message));
- EXPECT_NE(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
- decryption_result());
- }
- TEST_F(GCMEncryptionProviderTest, VerifiesCryptoKeyHeaderParsingThirdValue) {
- // The Crypto-Key header must be parsable and contain valid values, in which
- // values will be ignored unless they contain a "dh" property.
- IncomingMessage valid_message;
- valid_message.data["encryption"] = kValidEncryptionHeader;
- valid_message.data["crypto-key"] = kValidThreeValueCryptoKeyHeader;
- valid_message.raw_data = "foo";
- ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message));
- EXPECT_NE(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
- decryption_result());
- }
- TEST_F(GCMEncryptionProviderTest, VerifiesCryptoKeyHeaderSingleDhEntry) {
- // The Crypto-Key header must include at most one value that contains the
- // "dh" property. Having more than once occurrence is forbidden.
- IncomingMessage valid_message;
- valid_message.data["encryption"] = kValidEncryptionHeader;
- valid_message.data["crypto-key"] = kInvalidThreeValueCryptoKeyHeader;
- valid_message.raw_data = "foo";
- ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message));
- EXPECT_EQ(GCMDecryptionResult::INVALID_CRYPTO_KEY_HEADER,
- decryption_result());
- }
- TEST_F(GCMEncryptionProviderTest, VerifiesExistingKeys) {
- // When both headers are valid, the encryption keys still must be known to
- // the GCM key store before the message can be decrypted.
- IncomingMessage message;
- message.data["encryption"] = kValidEncryptionHeader;
- message.data["crypto-key"] = kValidCryptoKeyHeader;
- message.raw_data = "foo";
- ASSERT_NO_FATAL_FAILURE(Decrypt(message));
- EXPECT_EQ(GCMDecryptionResult::NO_KEYS, decryption_result());
- std::string public_key, auth_secret;
- encryption_provider()->GetEncryptionInfo(
- kExampleAppId, "" /* empty authorized entity for non-InstanceID */,
- base::BindOnce(&GCMEncryptionProviderTest::DidGetEncryptionInfo,
- base::Unretained(this), &public_key, &auth_secret));
- // Getting (or creating) the public key will be done asynchronously.
- base::RunLoop().RunUntilIdle();
- ASSERT_GT(public_key.size(), 0u);
- ASSERT_GT(auth_secret.size(), 0u);
- ASSERT_NO_FATAL_FAILURE(Decrypt(message));
- EXPECT_NE(GCMDecryptionResult::NO_KEYS, decryption_result());
- }
- TEST_F(GCMEncryptionProviderTest, VerifiesKeyRemovalGCMRegistration) {
- // Removing encryption info for an InstanceID token shouldn't affect a
- // non-InstanceID GCM registration.
- // Non-InstanceID callers pass an empty string for authorized_entity.
- std::string authorized_entity_gcm;
- std::string authorized_entity_1 = kExampleAuthorizedEntity + std::string("1");
- std::string authorized_entity_2 = kExampleAuthorizedEntity + std::string("2");
- // Should create encryption info.
- std::string public_key, auth_secret;
- encryption_provider()->GetEncryptionInfo(
- kExampleAppId, authorized_entity_gcm,
- base::BindOnce(&GCMEncryptionProviderTest::DidGetEncryptionInfo,
- base::Unretained(this), &public_key, &auth_secret));
- base::RunLoop().RunUntilIdle();
- // Should get encryption info created above.
- std::string read_public_key, read_auth_secret;
- encryption_provider()->GetEncryptionInfo(
- kExampleAppId, authorized_entity_gcm,
- base::BindOnce(&GCMEncryptionProviderTest::DidGetEncryptionInfo,
- base::Unretained(this), &read_public_key,
- &read_auth_secret));
- base::RunLoop().RunUntilIdle();
- EXPECT_GT(public_key.size(), 0u);
- EXPECT_GT(auth_secret.size(), 0u);
- EXPECT_EQ(public_key, read_public_key);
- EXPECT_EQ(auth_secret, read_auth_secret);
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_gcm, true));
- encryption_provider()->RemoveEncryptionInfo(
- kExampleAppId, authorized_entity_1, base::DoNothing());
- base::RunLoop().RunUntilIdle();
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_gcm, true));
- encryption_provider()->RemoveEncryptionInfo(kExampleAppId, "*",
- base::DoNothing());
- base::RunLoop().RunUntilIdle();
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_gcm, true));
- encryption_provider()->RemoveEncryptionInfo(
- kExampleAppId, authorized_entity_gcm, base::DoNothing());
- base::RunLoop().RunUntilIdle();
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_gcm, false));
- }
- TEST_F(GCMEncryptionProviderTest, VerifiesKeyRemovalInstanceIDToken) {
- // Removing encryption info for a non-InstanceID GCM registration shouldn't
- // affect an InstanceID token.
- // Non-InstanceID callers pass an empty string for authorized_entity.
- std::string authorized_entity_gcm;
- std::string authorized_entity_1 = kExampleAuthorizedEntity + std::string("1");
- std::string authorized_entity_2 = kExampleAuthorizedEntity + std::string("2");
- std::string public_key_1, auth_secret_1;
- encryption_provider()->GetEncryptionInfo(
- kExampleAppId, authorized_entity_1,
- base::BindOnce(&GCMEncryptionProviderTest::DidGetEncryptionInfo,
- base::Unretained(this), &public_key_1, &auth_secret_1));
- base::RunLoop().RunUntilIdle();
- EXPECT_GT(public_key_1.size(), 0u);
- EXPECT_GT(auth_secret_1.size(), 0u);
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_1, true));
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_2, false));
- std::string public_key_2, auth_secret_2;
- encryption_provider()->GetEncryptionInfo(
- kExampleAppId, authorized_entity_2,
- base::BindOnce(&GCMEncryptionProviderTest::DidGetEncryptionInfo,
- base::Unretained(this), &public_key_2, &auth_secret_2));
- base::RunLoop().RunUntilIdle();
- EXPECT_GT(public_key_2.size(), 0u);
- EXPECT_GT(auth_secret_2.size(), 0u);
- EXPECT_NE(public_key_1, public_key_2);
- EXPECT_NE(auth_secret_1, auth_secret_2);
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_1, true));
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_2, true));
- std::string read_public_key_1, read_auth_secret_1;
- encryption_provider()->GetEncryptionInfo(
- kExampleAppId, authorized_entity_1,
- base::BindOnce(&GCMEncryptionProviderTest::DidGetEncryptionInfo,
- base::Unretained(this), &read_public_key_1,
- &read_auth_secret_1));
- base::RunLoop().RunUntilIdle();
- // Should have returned existing info for authorized_entity_1.
- EXPECT_EQ(public_key_1, read_public_key_1);
- EXPECT_EQ(auth_secret_1, read_auth_secret_1);
- encryption_provider()->RemoveEncryptionInfo(
- kExampleAppId, authorized_entity_gcm, base::DoNothing());
- base::RunLoop().RunUntilIdle();
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_1, true));
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_2, true));
- encryption_provider()->RemoveEncryptionInfo(
- kExampleAppId, authorized_entity_1, base::DoNothing());
- base::RunLoop().RunUntilIdle();
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_1, false));
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_2, true));
- std::string public_key_1_refreshed, auth_secret_1_refreshed;
- encryption_provider()->GetEncryptionInfo(
- kExampleAppId, authorized_entity_1,
- base::BindOnce(&GCMEncryptionProviderTest::DidGetEncryptionInfo,
- base::Unretained(this), &public_key_1_refreshed,
- &auth_secret_1_refreshed));
- base::RunLoop().RunUntilIdle();
- // Since the info was removed, GetEncryptionInfo should have created new info.
- EXPECT_GT(public_key_1_refreshed.size(), 0u);
- EXPECT_GT(auth_secret_1_refreshed.size(), 0u);
- EXPECT_NE(public_key_1, public_key_1_refreshed);
- EXPECT_NE(auth_secret_1, auth_secret_1_refreshed);
- EXPECT_NE(public_key_2, public_key_1_refreshed);
- EXPECT_NE(auth_secret_2, auth_secret_1_refreshed);
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_1, true));
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_2, true));
- encryption_provider()->RemoveEncryptionInfo(kExampleAppId, "*",
- base::DoNothing());
- base::RunLoop().RunUntilIdle();
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_1, false));
- ASSERT_NO_FATAL_FAILURE(CheckHasKey(authorized_entity_2, false));
- }
- void GCMEncryptionProviderTest::TestEncryptionRoundTrip(
- const std::string& app_id,
- const std::string& authorized_entity,
- GCMMessageCryptographer::Version version,
- bool use_internal_raw_data_for_draft08) {
- // Performs a full round-trip of the encryption feature, including getting a
- // public/private key-key and performing the cryptographic operations. This
- // is more of an integration test than a unit test.
- ECPrivateKeyUniquePtr key, server_key;
- std::string auth_secret, server_authentication;
- // Retrieve the public/private key-key immediately from the key store, given
- // that the GCMEncryptionProvider will only share the public key with users.
- // Also create a second key, which will act as the server's keys.
- encryption_provider()->key_store_->CreateKeys(
- app_id, authorized_entity,
- base::BindOnce(&GCMEncryptionProviderTest::HandleKeysCallback,
- base::Unretained(this), &key, &auth_secret));
- encryption_provider()->key_store_->CreateKeys(
- "server-" + app_id, authorized_entity,
- base::BindOnce(&GCMEncryptionProviderTest::HandleKeysCallback,
- base::Unretained(this), &server_key,
- &server_authentication));
- // Creating the public keys will be done asynchronously.
- base::RunLoop().RunUntilIdle();
- std::string public_key, server_public_key;
- ASSERT_TRUE(GetRawPublicKey(*key, &public_key));
- ASSERT_TRUE(GetRawPublicKey(*server_key, &server_public_key));
- ASSERT_GT(public_key.size(), 0u);
- ASSERT_GT(server_public_key.size(), 0u);
- std::string private_key, server_private_key;
- ASSERT_TRUE(GetRawPublicKey(*key, &private_key));
- ASSERT_TRUE(GetRawPublicKey(*server_key, &server_private_key));
- ASSERT_GT(private_key.size(), 0u);
- ASSERT_GT(server_private_key.size(), 0u);
- IncomingMessage message;
- message.sender_id = authorized_entity;
- switch (version) {
- case GCMMessageCryptographer::Version::DRAFT_03: {
- std::string salt;
- // Creates a cryptographically secure salt of |salt_size| octets in size,
- // and calculate the shared secret for the message.
- crypto::RandBytes(base::WriteInto(&salt, 16 + 1), 16);
- std::string shared_secret;
- ASSERT_TRUE(
- ComputeSharedP256Secret(*key, server_public_key, &shared_secret));
- size_t record_size;
- // Encrypts the |kExampleMessage| using the generated shared key and the
- // random |salt|, storing the result in |record_size| and the message.
- GCMMessageCryptographer cryptographer(version);
- std::string ciphertext;
- ASSERT_TRUE(cryptographer.Encrypt(
- public_key, server_public_key, shared_secret, auth_secret, salt,
- kExampleMessage, &record_size, &ciphertext));
- std::string encoded_salt, encoded_key;
- // Compile the incoming GCM message, including the required headers.
- base::Base64UrlEncode(salt, base::Base64UrlEncodePolicy::INCLUDE_PADDING,
- &encoded_salt);
- base::Base64UrlEncode(server_public_key,
- base::Base64UrlEncodePolicy::INCLUDE_PADDING,
- &encoded_key);
- std::stringstream encryption_header;
- encryption_header << "rs=" << base::NumberToString(record_size) << ";";
- encryption_header << "salt=" << encoded_salt;
- message.data["encryption"] = encryption_header.str();
- message.data["crypto-key"] = "dh=" + encoded_key;
- message.raw_data.swap(ciphertext);
- break;
- }
- case GCMMessageCryptographer::Version::DRAFT_08: {
- ASSERT_NO_FATAL_FAILURE(
- Encrypt(authorized_entity, public_key, auth_secret, kExampleMessage));
- ASSERT_EQ(GCMEncryptionResult::ENCRYPTED_DRAFT_08, encryption_result());
- message.data["content-encoding"] = "aes128gcm";
- if (use_internal_raw_data_for_draft08) {
- std::string raw_data_base64;
- base::Base64Encode(encrypted_message(), &raw_data_base64);
- message.data["_googRawData"] = raw_data_base64;
- } else {
- message.raw_data = encrypted_message();
- }
- break;
- }
- }
- ASSERT_TRUE(encryption_provider()->IsEncryptedMessage(message));
- // Decrypt the message, and expect everything to go wonderfully well.
- ASSERT_NO_FATAL_FAILURE(Decrypt(message));
- ASSERT_EQ(version == GCMMessageCryptographer::Version::DRAFT_03
- ? GCMDecryptionResult::DECRYPTED_DRAFT_03
- : GCMDecryptionResult::DECRYPTED_DRAFT_08,
- decryption_result());
- EXPECT_TRUE(decrypted_message().decrypted);
- EXPECT_EQ(kExampleMessage, decrypted_message().raw_data);
- }
- void GCMEncryptionProviderTest::TestEncryptionNoKeys(
- const std::string& app_id,
- const std::string& authorized_entity) {
- // Only create proper keys for receipeint without creating keys for sender.
- ECPrivateKeyUniquePtr key;
- std::string auth_secret;
- encryption_provider()->key_store_->CreateKeys(
- "receiver" + app_id, authorized_entity,
- base::BindOnce(&GCMEncryptionProviderTest::HandleKeysCallback,
- base::Unretained(this), &key, &auth_secret));
- // Creating the public keys will be done asynchronously.
- base::RunLoop().RunUntilIdle();
- std::string public_key;
- ASSERT_TRUE(GetRawPublicKey(*key, &public_key));
- ASSERT_GT(public_key.size(), 0u);
- ASSERT_NO_FATAL_FAILURE(
- Encrypt(authorized_entity, public_key, auth_secret, kExampleMessage));
- EXPECT_EQ(GCMEncryptionResult::NO_KEYS, encryption_result());
- }
- TEST_F(GCMEncryptionProviderTest, EncryptionRoundTripGCMRegistration) {
- // GCMEncryptionProvider::DecryptMessage should succeed when the message was
- // sent to a non-InstanceID GCM registration (empty authorized_entity).
- ASSERT_NO_FATAL_FAILURE(TestEncryptionRoundTrip(
- kExampleAppId, "" /* empty authorized entity for non-InstanceID */,
- GCMMessageCryptographer::Version::DRAFT_03));
- }
- TEST_F(GCMEncryptionProviderTest, EncryptionRoundTripInstanceIDToken) {
- // GCMEncryptionProvider::DecryptMessage should succeed when the message was
- // sent to an InstanceID token (non-empty authorized_entity).
- ASSERT_NO_FATAL_FAILURE(
- TestEncryptionRoundTrip(kExampleAppId, kExampleAuthorizedEntity,
- GCMMessageCryptographer::Version::DRAFT_03));
- }
- TEST_F(GCMEncryptionProviderTest, EncryptionRoundTripDraft08) {
- // GCMEncryptionProvider::DecryptMessage should succeed when the message was
- // encrypted following raft-ietf-webpush-encryption-08.
- ASSERT_NO_FATAL_FAILURE(
- TestEncryptionRoundTrip(kExampleAppId, kExampleAuthorizedEntity,
- GCMMessageCryptographer::Version::DRAFT_08));
- }
- TEST_F(GCMEncryptionProviderTest, EncryptionRoundTripDraft08InternalRawData) {
- // GCMEncryptionProvider::DecryptMessage should succeed when the message was
- // encrypted following raft-ietf-webpush-encryption-08 with raw_data base64
- // encoded in message data.
- ASSERT_NO_FATAL_FAILURE(
- TestEncryptionRoundTrip(kExampleAppId, kExampleAuthorizedEntity,
- GCMMessageCryptographer::Version::DRAFT_08,
- /*use_internal_raw_data_for_draft08=*/true));
- }
- TEST_F(GCMEncryptionProviderTest, EncryptionNoKeys) {
- ASSERT_NO_FATAL_FAILURE(
- TestEncryptionNoKeys(kExampleAppId, kExampleAuthorizedEntity));
- }
- } // namespace gcm
|