| Index: components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
|
| diff --git a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
|
| index 15ff1aeacd0dc9354672bcebe1d2f338ee8b5790..0f0fc0c4d1c53e4434b31193dfbcfafe2c976abc 100644
|
| --- a/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
|
| +++ b/components/gcm_driver/crypto/gcm_encryption_provider_unittest.cc
|
| @@ -10,6 +10,7 @@
|
| #include <string>
|
|
|
| #include "base/base64url.h"
|
| +#include "base/big_endian.h"
|
| #include "base/bind.h"
|
| #include "base/bind_helpers.h"
|
| #include "base/files/scoped_temp_dir.h"
|
| @@ -141,7 +142,8 @@ class GCMEncryptionProviderTest : public ::testing::Test {
|
| // 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);
|
| + const std::string& authorized_entity,
|
| + GCMMessageCryptographer::Version version);
|
|
|
| private:
|
| void DidDecryptMessage(GCMEncryptionProvider::DecryptionResult result,
|
| @@ -186,6 +188,11 @@ TEST_F(GCMEncryptionProviderTest, IsEncryptedMessage) {
|
| 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) {
|
| @@ -447,7 +454,8 @@ TEST_F(GCMEncryptionProviderTest, VerifiesKeyRemovalInstanceIDToken) {
|
|
|
| void GCMEncryptionProviderTest::TestEncryptionRoundTrip(
|
| const std::string& app_id,
|
| - const std::string& authorized_entity) {
|
| + const std::string& authorized_entity,
|
| + GCMMessageCryptographer::Version version) {
|
| // Performs a full round-trip of the encryption feature, including getting a
|
| // public/private key-pair and performing the cryptographic operations. This
|
| // is more of an integration test than a unit test.
|
| @@ -495,34 +503,71 @@ void GCMEncryptionProviderTest::TestEncryptionRoundTrip(
|
|
|
| // Encrypts the |kExampleMessage| using the generated shared key and the
|
| // random |salt|, storing the result in |record_size| and the message.
|
| - GCMMessageCryptographer cryptographer(
|
| - GCMMessageCryptographer::Version::DRAFT_03);
|
| + GCMMessageCryptographer cryptographer(version);
|
|
|
| + std::string ciphertext;
|
| ASSERT_TRUE(cryptographer.Encrypt(
|
| pair.public_key(), server_pair.public_key(), shared_secret, auth_secret,
|
| - salt, kExampleMessage, &record_size, &message.raw_data));
|
| + salt, kExampleMessage, &record_size, &ciphertext));
|
| +
|
| + switch (version) {
|
| + case GCMMessageCryptographer::Version::DRAFT_03: {
|
| + 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_pair.public_key(),
|
| + base::Base64UrlEncodePolicy::INCLUDE_PADDING,
|
| + &encoded_key);
|
| +
|
| + std::stringstream encryption_header;
|
| + encryption_header << "rs=" << base::SizeTToString(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: {
|
| + uint32_t rs = record_size;
|
| + uint8_t key_length = server_pair.public_key().size();
|
| +
|
| + std::vector<char> payload(salt.size() + sizeof(rs) + sizeof(key_length) +
|
| + server_pair.public_key().size() +
|
| + ciphertext.size());
|
| +
|
| + char* current = &payload.front();
|
|
|
| - std::string encoded_salt, encoded_key;
|
| + memcpy(current, salt.data(), salt.size());
|
| + current += salt.size();
|
|
|
| - // Compile the incoming GCM message, including the required headers.
|
| - base::Base64UrlEncode(
|
| - salt, base::Base64UrlEncodePolicy::INCLUDE_PADDING, &encoded_salt);
|
| - base::Base64UrlEncode(
|
| - server_pair.public_key(), base::Base64UrlEncodePolicy::INCLUDE_PADDING,
|
| - &encoded_key);
|
| + base::WriteBigEndian(current, rs);
|
| + current += sizeof(rs);
|
|
|
| - std::stringstream encryption_header;
|
| - encryption_header << "rs=" << base::SizeTToString(record_size) << ";";
|
| - encryption_header << "salt=" << encoded_salt;
|
| + base::WriteBigEndian(current, key_length);
|
| + current += sizeof(key_length);
|
|
|
| - message.data["encryption"] = encryption_header.str();
|
| - message.data["crypto-key"] = "dh=" + encoded_key;
|
| + memcpy(current, server_pair.public_key().data(),
|
| + server_pair.public_key().size());
|
| + current += server_pair.public_key().size();
|
| +
|
| + memcpy(current, ciphertext.data(), ciphertext.size());
|
| +
|
| + message.data["content-encoding"] = "aes128gcm";
|
| + message.raw_data.assign(payload.begin(), payload.end());
|
| + 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(GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED,
|
| + ASSERT_EQ(version == GCMMessageCryptographer::Version::DRAFT_03
|
| + ? GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_03
|
| + : GCMEncryptionProvider::DECRYPTION_RESULT_DECRYPTED_DRAFT_08,
|
| decryption_result());
|
|
|
| EXPECT_TRUE(decrypted_message().decrypted);
|
| @@ -533,14 +578,24 @@ 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 */));
|
| + 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));
|
| + 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));
|
| }
|
|
|
| } // namespace gcm
|
|
|