OLD | NEW |
(Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. |
| 4 |
| 5 #include "components/gcm_driver/crypto/gcm_encryption_provider.h" |
| 6 |
| 7 #include <sstream> |
| 8 #include <string> |
| 9 |
| 10 #include "base/base64.h" |
| 11 #include "base/bind.h" |
| 12 #include "base/files/scoped_temp_dir.h" |
| 13 #include "base/message_loop/message_loop.h" |
| 14 #include "base/run_loop.h" |
| 15 #include "base/strings/string_number_conversions.h" |
| 16 #include "base/strings/string_piece.h" |
| 17 #include "base/strings/string_util.h" |
| 18 #include "components/gcm_driver/common/gcm_messages.h" |
| 19 #include "components/gcm_driver/crypto/gcm_key_store.h" |
| 20 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h" |
| 21 #include "crypto/curve25519.h" |
| 22 #include "crypto/random.h" |
| 23 #include "testing/gtest/include/gtest/gtest.h" |
| 24 |
| 25 namespace gcm { |
| 26 namespace { |
| 27 |
| 28 const char kExampleAppId[] = "my-app-id"; |
| 29 const char kExampleMessage[] = "Hello, world, this is the GCM Driver!"; |
| 30 |
| 31 const char kValidEncryptionHeader[] = |
| 32 "keyid=foo;salt=MTIzNDU2Nzg5MDEyMzQ1Ng;rs=1024"; |
| 33 const char kInvalidEncryptionHeader[] = "keyid"; |
| 34 |
| 35 const char kValidEncryptionKeyHeader[] = |
| 36 "keyid=foo;dh=NjU0MzIxMDk4NzY1NDMyMTEyMzQ1Njc4OTAxMjM0NTY"; |
| 37 const char kInvalidEncryptionKeyHeader[] = "keyid"; |
| 38 |
| 39 // TODO(peter): Unify the Base64Url implementations. https://crbug.com/536745. |
| 40 void Base64UrlEncode(const std::string& decoded_input, |
| 41 std::string* encoded_output) { |
| 42 base::Base64Encode(decoded_input, encoded_output); |
| 43 base::ReplaceChars(*encoded_output, "+", "-", encoded_output); |
| 44 base::ReplaceChars(*encoded_output, "/", "_", encoded_output); |
| 45 } |
| 46 |
| 47 } // namespace |
| 48 |
| 49 class GCMEncryptionProviderTest : public ::testing::Test { |
| 50 public: |
| 51 void SetUp() override { |
| 52 ASSERT_TRUE(scoped_temp_dir_.CreateUniqueTempDir()); |
| 53 |
| 54 encryption_provider_.reset(new GCMEncryptionProvider); |
| 55 encryption_provider_->Init(scoped_temp_dir_.path(), |
| 56 message_loop_.task_runner()); |
| 57 } |
| 58 |
| 59 void TearDown() override { |
| 60 encryption_provider_.reset(); |
| 61 |
| 62 // |encryption_provider_| owns a ProtoDatabaseImpl whose destructor deletes |
| 63 // the underlying LevelDB database on the task runner. |
| 64 base::RunLoop().RunUntilIdle(); |
| 65 } |
| 66 |
| 67 // To be used as a callback for GCMEncryptionProvider::GetPublicKey(). |
| 68 void DidGetPublicKey(std::string* key_out, const std::string& key) { |
| 69 *key_out = key; |
| 70 } |
| 71 |
| 72 // To be used as a callback for GCMKeyStore::CreateKeys(). |
| 73 void DidCreateKeys(KeyPair* pair_out, const KeyPair& pair) { |
| 74 *pair_out = pair; |
| 75 } |
| 76 |
| 77 protected: |
| 78 // Tri-state enumaration listing whether the decryption operation is idle |
| 79 // (hasn't started yet), succeeded or failed. |
| 80 enum DecryptionResult { |
| 81 DECRYPTION_IDLE, |
| 82 DECRYPTION_SUCCEEDED, |
| 83 DECRYPTION_FAILED |
| 84 }; |
| 85 |
| 86 // Decrypts the |message| and then synchronously waits until either the |
| 87 // success or failure callbacks has been invoked. |
| 88 void Decrypt(const IncomingMessage& message) { |
| 89 decryption_result_ = DECRYPTION_IDLE; |
| 90 encryption_provider_->DecryptMessage( |
| 91 kExampleAppId, message, |
| 92 base::Bind(&GCMEncryptionProviderTest::OnDecryptionSucceeded, |
| 93 base::Unretained(this)), |
| 94 base::Bind(&GCMEncryptionProviderTest::OnDecryptionFailed, |
| 95 base::Unretained(this))); |
| 96 |
| 97 // The encryption keys will be read asynchronously. |
| 98 base::RunLoop().RunUntilIdle(); |
| 99 |
| 100 ASSERT_NE(decryption_result_, DECRYPTION_IDLE); |
| 101 } |
| 102 |
| 103 DecryptionResult decryption_result() { return decryption_result_; } |
| 104 |
| 105 const IncomingMessage& decrypted_message() { return decrypted_message_; } |
| 106 |
| 107 GCMEncryptionProvider::DecryptionFailure failure_reason() { |
| 108 return failure_reason_; |
| 109 } |
| 110 |
| 111 GCMEncryptionProvider* encryption_provider() { |
| 112 return encryption_provider_.get(); |
| 113 } |
| 114 |
| 115 private: |
| 116 void OnDecryptionSucceeded(const IncomingMessage& message) { |
| 117 decryption_result_ = DECRYPTION_SUCCEEDED; |
| 118 decrypted_message_ = message; |
| 119 } |
| 120 |
| 121 void OnDecryptionFailed(GCMEncryptionProvider::DecryptionFailure reason) { |
| 122 decryption_result_ = DECRYPTION_FAILED; |
| 123 failure_reason_ = reason; |
| 124 } |
| 125 |
| 126 base::MessageLoop message_loop_; |
| 127 base::ScopedTempDir scoped_temp_dir_; |
| 128 |
| 129 scoped_ptr<GCMEncryptionProvider> encryption_provider_; |
| 130 |
| 131 DecryptionResult decryption_result_ = DECRYPTION_IDLE; |
| 132 GCMEncryptionProvider::DecryptionFailure failure_reason_ = |
| 133 GCMEncryptionProvider::DECRYPTION_FAILURE_UNKNOWN; |
| 134 |
| 135 IncomingMessage decrypted_message_; |
| 136 }; |
| 137 |
| 138 TEST_F(GCMEncryptionProviderTest, IsEncryptedMessage) { |
| 139 // Both the Encryption and Encryption-Key headers must be present, and the raw |
| 140 // data must be non-empty for a message to be considered encrypted. |
| 141 |
| 142 IncomingMessage empty_message; |
| 143 EXPECT_FALSE(encryption_provider()->IsEncryptedMessage(empty_message)); |
| 144 |
| 145 IncomingMessage single_header_message; |
| 146 single_header_message.data["encryption"] = ""; |
| 147 EXPECT_FALSE(encryption_provider()->IsEncryptedMessage( |
| 148 single_header_message)); |
| 149 |
| 150 IncomingMessage double_header_message; |
| 151 double_header_message.data["encryption"] = ""; |
| 152 double_header_message.data["encryption_key"] = ""; |
| 153 EXPECT_FALSE(encryption_provider()->IsEncryptedMessage( |
| 154 double_header_message)); |
| 155 |
| 156 IncomingMessage double_header_with_data_message; |
| 157 double_header_with_data_message.data["encryption"] = ""; |
| 158 double_header_with_data_message.data["encryption_key"] = ""; |
| 159 double_header_with_data_message.raw_data = "foo"; |
| 160 EXPECT_TRUE(encryption_provider()->IsEncryptedMessage( |
| 161 double_header_with_data_message)); |
| 162 } |
| 163 |
| 164 TEST_F(GCMEncryptionProviderTest, VerifiesEncryptionHeaderParsing) { |
| 165 // The Encryption header must be parsable and contain valid values. |
| 166 // Note that this is more extensively tested in EncryptionHeaderParsersTest. |
| 167 |
| 168 IncomingMessage invalid_message; |
| 169 invalid_message.data["encryption"] = kInvalidEncryptionHeader; |
| 170 invalid_message.data["encryption_key"] = kValidEncryptionKeyHeader; |
| 171 |
| 172 ASSERT_NO_FATAL_FAILURE(Decrypt(invalid_message)); |
| 173 ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); |
| 174 EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_ENCRYPTION_HEADER, |
| 175 failure_reason()); |
| 176 |
| 177 IncomingMessage valid_message; |
| 178 valid_message.data["encryption"] = kValidEncryptionHeader; |
| 179 valid_message.data["encryption_key"] = kInvalidEncryptionKeyHeader; |
| 180 |
| 181 ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message)); |
| 182 ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); |
| 183 EXPECT_NE(GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_ENCRYPTION_HEADER, |
| 184 failure_reason()); |
| 185 } |
| 186 |
| 187 TEST_F(GCMEncryptionProviderTest, VerifiesEncryptionKeyHeaderParsing) { |
| 188 // The Encryption-Key header must be parsable and contain valid values. |
| 189 // Note that this is more extensively tested in EncryptionHeaderParsersTest. |
| 190 |
| 191 IncomingMessage invalid_message; |
| 192 invalid_message.data["encryption"] = kValidEncryptionHeader; |
| 193 invalid_message.data["encryption_key"] = kInvalidEncryptionKeyHeader; |
| 194 |
| 195 ASSERT_NO_FATAL_FAILURE(Decrypt(invalid_message)); |
| 196 ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); |
| 197 EXPECT_EQ( |
| 198 GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_ENCRYPTION_KEY_HEADER, |
| 199 failure_reason()); |
| 200 |
| 201 IncomingMessage valid_message; |
| 202 valid_message.data["encryption"] = kInvalidEncryptionHeader; |
| 203 valid_message.data["encryption_key"] = kValidEncryptionKeyHeader; |
| 204 |
| 205 ASSERT_NO_FATAL_FAILURE(Decrypt(valid_message)); |
| 206 ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); |
| 207 EXPECT_NE( |
| 208 GCMEncryptionProvider::DECRYPTION_FAILURE_INVALID_ENCRYPTION_KEY_HEADER, |
| 209 failure_reason()); |
| 210 } |
| 211 |
| 212 TEST_F(GCMEncryptionProviderTest, VerifiesExistingKeys) { |
| 213 // When both headers are valid, the encryption keys still must be known to |
| 214 // the GCM key store before the message can be decrypted. |
| 215 |
| 216 IncomingMessage message; |
| 217 message.data["encryption"] = kValidEncryptionHeader; |
| 218 message.data["encryption_key"] = kValidEncryptionKeyHeader; |
| 219 |
| 220 ASSERT_NO_FATAL_FAILURE(Decrypt(message)); |
| 221 ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); |
| 222 EXPECT_EQ(GCMEncryptionProvider::DECRYPTION_FAILURE_NO_KEYS, |
| 223 failure_reason()); |
| 224 |
| 225 std::string public_key; |
| 226 encryption_provider()->GetPublicKey( |
| 227 kExampleAppId, |
| 228 base::Bind(&GCMEncryptionProviderTest::DidGetPublicKey, |
| 229 base::Unretained(this), &public_key)); |
| 230 |
| 231 // Getting (or creating) the public key will be done asynchronously. |
| 232 base::RunLoop().RunUntilIdle(); |
| 233 |
| 234 ASSERT_EQ(crypto::curve25519::kBytes, public_key.size()); |
| 235 |
| 236 ASSERT_NO_FATAL_FAILURE(Decrypt(message)); |
| 237 ASSERT_EQ(DECRYPTION_FAILED, decryption_result()); |
| 238 EXPECT_NE(GCMEncryptionProvider::DECRYPTION_FAILURE_NO_KEYS, |
| 239 failure_reason()); |
| 240 } |
| 241 |
| 242 TEST_F(GCMEncryptionProviderTest, EncryptionRoundTrip) { |
| 243 // Performs a full round-trip of the encryption feature, including getting a |
| 244 // public/private key-pair and performing the cryptographic operations. This |
| 245 // is more of an integration test than a unit test. |
| 246 |
| 247 KeyPair pair; |
| 248 KeyPair server_pair; |
| 249 |
| 250 // Retrieve the public/private key-pair immediately from the key store, given |
| 251 // that the GCMEncryptionProvider will only share the public key with users. |
| 252 // Also create a second pair, which will act as the server's keys. |
| 253 encryption_provider()->key_store_->CreateKeys( |
| 254 kExampleAppId, |
| 255 base::Bind(&GCMEncryptionProviderTest::DidCreateKeys, |
| 256 base::Unretained(this), &pair)); |
| 257 |
| 258 encryption_provider()->key_store_->CreateKeys( |
| 259 std::string(kExampleAppId) + "-server", |
| 260 base::Bind(&GCMEncryptionProviderTest::DidCreateKeys, |
| 261 base::Unretained(this), &server_pair)); |
| 262 |
| 263 // Creating the public keys will be done asynchronously. |
| 264 base::RunLoop().RunUntilIdle(); |
| 265 |
| 266 ASSERT_EQ(crypto::curve25519::kScalarBytes, pair.private_key().size()); |
| 267 ASSERT_EQ(crypto::curve25519::kBytes, pair.public_key().size()); |
| 268 |
| 269 ASSERT_EQ(crypto::curve25519::kScalarBytes, server_pair.private_key().size()); |
| 270 ASSERT_EQ(crypto::curve25519::kBytes, server_pair.public_key().size()); |
| 271 |
| 272 std::string salt; |
| 273 |
| 274 // Creates a cryptographically secure salt of |salt_size| octets in size, and |
| 275 // calculate the shared secret for the message. |
| 276 crypto::RandBytes(base::WriteInto(&salt, 16 + 1), 16); |
| 277 |
| 278 uint8_t shared_key[crypto::curve25519::kBytes]; |
| 279 crypto::curve25519::ScalarMult( |
| 280 reinterpret_cast<const unsigned char*>(server_pair.private_key().data()), |
| 281 reinterpret_cast<const unsigned char*>(pair.public_key().data()), |
| 282 shared_key); |
| 283 |
| 284 base::StringPiece shared_key_string_piece( |
| 285 reinterpret_cast<char*>(shared_key), crypto::curve25519::kBytes); |
| 286 |
| 287 IncomingMessage message; |
| 288 size_t record_size; |
| 289 |
| 290 // Encrypts the |kExampleMessage| using the generated shared key and the |
| 291 // random |salt|, storing the result in |record_size| and the message. |
| 292 GCMMessageCryptographer cryptographer; |
| 293 ASSERT_TRUE(cryptographer.Encrypt(kExampleMessage, shared_key_string_piece, |
| 294 salt, &record_size, &message.raw_data)); |
| 295 |
| 296 std::string encoded_salt, encoded_key; |
| 297 |
| 298 // Compile the incoming GCM message, including the required headers. |
| 299 Base64UrlEncode(salt, &encoded_salt); |
| 300 Base64UrlEncode(server_pair.public_key(), &encoded_key); |
| 301 |
| 302 std::stringstream encryption_header; |
| 303 encryption_header << "rs=" << base::SizeTToString(record_size) << ";"; |
| 304 encryption_header << "salt=" << encoded_salt; |
| 305 |
| 306 message.data["encryption"] = encryption_header.str(); |
| 307 message.data["encryption_key"] = "dh=" + encoded_key; |
| 308 |
| 309 ASSERT_TRUE(encryption_provider()->IsEncryptedMessage(message)); |
| 310 |
| 311 // Decrypt the message, and expect everything to go wonderfully well. |
| 312 ASSERT_NO_FATAL_FAILURE(Decrypt(message)); |
| 313 ASSERT_EQ(DECRYPTION_SUCCEEDED, decryption_result()); |
| 314 |
| 315 EXPECT_TRUE(decrypted_message().decrypted); |
| 316 EXPECT_EQ(kExampleMessage, decrypted_message().raw_data); |
| 317 } |
| 318 |
| 319 } // namespace gcm |
OLD | NEW |