Chromium Code Reviews| 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..c500b695104e5834c10a8c942a892fa649eb8416 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 = ciphertext; |
|
johnme
2017/05/23 17:37:48
Nit: .swap?
Peter Beverloo
2017/05/23 17:58:32
Done.
|
| + 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 |