Chromium Code Reviews| Index: components/gcm_driver/crypto/gcm_encryption_provider.cc |
| diff --git a/components/gcm_driver/crypto/gcm_encryption_provider.cc b/components/gcm_driver/crypto/gcm_encryption_provider.cc |
| index c9c7183ebe6757f330e4a032ef45508eb76b0323..40aa7f7d1ec8638cc1ab0d02bcfca064e71bdb87 100644 |
| --- a/components/gcm_driver/crypto/gcm_encryption_provider.cc |
| +++ b/components/gcm_driver/crypto/gcm_encryption_provider.cc |
| @@ -13,6 +13,7 @@ |
| #include "components/gcm_driver/crypto/encryption_header_parsers.h" |
| #include "components/gcm_driver/crypto/gcm_key_store.h" |
| #include "components/gcm_driver/crypto/gcm_message_cryptographer.h" |
| +#include "components/gcm_driver/crypto/message_payload_parser.h" |
| #include "components/gcm_driver/crypto/p256_key_util.h" |
| #include "components/gcm_driver/crypto/proto/gcm_encryption_data.pb.h" |
| @@ -21,8 +22,12 @@ namespace gcm { |
| namespace { |
| const char kEncryptionProperty[] = "encryption"; |
| +const char kContentEncodingProperty[] = "content-encoding"; |
| const char kCryptoKeyProperty[] = "crypto-key"; |
| +// Content coding name defined by ietf-httpbis-encryption-encoding. |
| +const char kContentCodingAes128Gcm[] = "aes128gcm"; |
| + |
| // Directory in the GCM Store in which the encryption database will be stored. |
| const base::FilePath::CharType kEncryptionDirectoryName[] = |
| FILE_PATH_LITERAL("Encryption"); |
| @@ -34,8 +39,10 @@ std::string GCMEncryptionProvider::ToDecryptionResultDetailsString( |
| switch (result) { |
| case DECRYPTION_RESULT_UNENCRYPTED: |
| return "Message was not encrypted"; |
| - case DECRYPTION_RESULT_DECRYPTED: |
| - return "Message decrypted"; |
| + case DECRYPTION_RESULT_DECRYPTED_DRAFT_03: |
| + return "Message decrypted (draft 03)"; |
| + case DECRYPTION_RESULT_DECRYPTED_DRAFT_08: |
| + return "Message decrypted (draft 08)"; |
| case DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER: |
| return "Invalid format for the Encryption header"; |
| case DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER: |
| @@ -46,6 +53,8 @@ std::string GCMEncryptionProvider::ToDecryptionResultDetailsString( |
| return "The shared secret cannot be derived from the keying material"; |
| case DECRYPTION_RESULT_INVALID_PAYLOAD: |
| return "AES-GCM decryption failed"; |
| + case DECRYPTION_RESULT_INVALID_BINARY_HEADER: |
| + return "The message's binary header could not be parsed."; |
| } |
| NOTREACHED(); |
| @@ -97,6 +106,14 @@ void GCMEncryptionProvider::RemoveEncryptionInfo( |
| bool GCMEncryptionProvider::IsEncryptedMessage(const IncomingMessage& message) |
| const { |
| + // Messages that explicitly specify their content coding to be "aes128gcm" |
| + // indicate that they use draft-ietf-webpush-encryption-08. |
| + auto content_encoding_iter = message.data.find(kContentEncodingProperty); |
| + if (content_encoding_iter != message.data.end() && |
| + content_encoding_iter->second == kContentCodingAes128Gcm) { |
| + return true; |
| + } |
| + |
| // The Web Push protocol requires the encryption and crypto-key properties to |
| // be set, and the raw_data field to be populated with the payload. |
| if (message.data.find(kEncryptionProperty) == message.data.end() || |
| @@ -116,77 +133,110 @@ void GCMEncryptionProvider::DecryptMessage( |
| return; |
| } |
| - // IsEncryptedMessage() verifies that both the Encryption and Crypto-Key HTTP |
| - // headers have been provided for the |message|. |
| - const auto& encryption_header = message.data.find(kEncryptionProperty); |
| - const auto& crypto_key_header = message.data.find(kCryptoKeyProperty); |
| + std::string salt, public_key, ciphertext; |
| + GCMMessageCryptographer::Version version; |
| + uint32_t record_size; |
| + |
| + auto content_encoding_iter = message.data.find(kContentEncodingProperty); |
| + if (content_encoding_iter != message.data.end() && |
| + content_encoding_iter->second == kContentCodingAes128Gcm) { |
| + // The message follows encryption per draft-ietf-webpush-encryption-08. Use |
| + // the binary header of the message to derive the values. |
| + |
| + MessagePayloadParser parser(message.raw_data); |
| + if (!parser.IsValid()) { |
| + DLOG(ERROR) << "Unable to parse the message's binary header"; |
| + callback.Run(DECRYPTION_RESULT_INVALID_BINARY_HEADER, IncomingMessage()); |
| + return; |
| + } |
| - DCHECK(encryption_header != message.data.end()); |
| - DCHECK(crypto_key_header != message.data.end()); |
| + salt = parser.salt().as_string(); |
| + public_key = parser.public_key().as_string(); |
| + record_size = parser.record_size(); |
| + ciphertext = parser.ciphertext().as_string(); |
| + version = GCMMessageCryptographer::Version::DRAFT_08; |
| + |
| + } else { |
| + // The message follows encryption per draft-ietf-webpush-encryption-03. Use |
| + // the Encryption and Crypto-Key header values to derive the values. |
| + |
| + const auto& encryption_header = message.data.find(kEncryptionProperty); |
| + DCHECK(encryption_header != message.data.end()); |
| + |
| + const auto& crypto_key_header = message.data.find(kCryptoKeyProperty); |
| + DCHECK(crypto_key_header != message.data.end()); |
| + |
| + EncryptionHeaderIterator encryption_header_iterator( |
| + encryption_header->second.begin(), encryption_header->second.end()); |
| + if (!encryption_header_iterator.GetNext()) { |
| + DLOG(ERROR) << "Unable to parse the value of the Encryption header"; |
| + callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, |
| + IncomingMessage()); |
| + return; |
| + } |
| - EncryptionHeaderIterator encryption_header_iterator( |
| - encryption_header->second.begin(), encryption_header->second.end()); |
| - if (!encryption_header_iterator.GetNext()) { |
| - DLOG(ERROR) << "Unable to parse the value of the Encryption header"; |
| - callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, |
| - IncomingMessage()); |
| - return; |
| - } |
| + if (encryption_header_iterator.salt().size() != |
| + GCMMessageCryptographer::kSaltSize) { |
| + DLOG(ERROR) << "Invalid values supplied in the Encryption header"; |
| + callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, |
| + IncomingMessage()); |
| + return; |
| + } |
| - if (encryption_header_iterator.salt().size() != |
| - GCMMessageCryptographer::kSaltSize) { |
| - DLOG(ERROR) << "Invalid values supplied in the Encryption header"; |
| - callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, |
| - IncomingMessage()); |
| - return; |
| - } |
| + salt = encryption_header_iterator.salt(); |
| + record_size = encryption_header_iterator.rs(); |
| - CryptoKeyHeaderIterator crypto_key_header_iterator( |
| - crypto_key_header->second.begin(), crypto_key_header->second.end()); |
| - if (!crypto_key_header_iterator.GetNext()) { |
| - DLOG(ERROR) << "Unable to parse the value of the Crypto-Key header"; |
| - callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, |
| - IncomingMessage()); |
| - return; |
| - } |
| + CryptoKeyHeaderIterator crypto_key_header_iterator( |
| + crypto_key_header->second.begin(), crypto_key_header->second.end()); |
| + if (!crypto_key_header_iterator.GetNext()) { |
| + DLOG(ERROR) << "Unable to parse the value of the Crypto-Key header"; |
| + callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, |
| + IncomingMessage()); |
| + return; |
| + } |
| - // Ignore values that don't include the "dh" property. When using VAPID, it is |
| - // valid for the application server to supply multiple values. |
| - while (crypto_key_header_iterator.dh().empty() && |
| - crypto_key_header_iterator.GetNext()) {} |
| + // Ignore values that don't include the "dh" property. When using VAPID, it |
| + // is valid for the application server to supply multiple values. |
| + while (crypto_key_header_iterator.dh().empty() && |
| + crypto_key_header_iterator.GetNext()) { |
| + } |
| - bool valid_crypto_key_header = false; |
| - std::string dh; |
| + bool valid_crypto_key_header = false; |
| - if (!crypto_key_header_iterator.dh().empty()) { |
| - dh = crypto_key_header_iterator.dh(); |
| - valid_crypto_key_header = true; |
| + if (!crypto_key_header_iterator.dh().empty()) { |
| + public_key = crypto_key_header_iterator.dh(); |
| + valid_crypto_key_header = true; |
| - // Guard against the "dh" property being included more than once. |
| - while (crypto_key_header_iterator.GetNext()) { |
| - if (crypto_key_header_iterator.dh().empty()) |
| - continue; |
| + // Guard against the "dh" property being included more than once. |
| + while (crypto_key_header_iterator.GetNext()) { |
| + if (crypto_key_header_iterator.dh().empty()) |
| + continue; |
| - valid_crypto_key_header = false; |
| - break; |
| + valid_crypto_key_header = false; |
| + break; |
| + } |
| } |
| - } |
| - if (!valid_crypto_key_header) { |
| - DLOG(ERROR) << "Invalid values supplied in the Crypto-Key header"; |
| - callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, |
| - IncomingMessage()); |
| - return; |
| + if (!valid_crypto_key_header) { |
| + DLOG(ERROR) << "Invalid values supplied in the Crypto-Key header"; |
| + callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, |
| + IncomingMessage()); |
| + return; |
| + } |
| + |
| + ciphertext = message.raw_data; |
| + version = GCMMessageCryptographer::Version::DRAFT_03; |
| } |
| // Use |fallback_to_empty_authorized_entity|, since this message might have |
| // been sent to either an InstanceID token or a non-InstanceID registration. |
| - key_store_->GetKeys(app_id, message.sender_id /* authorized_entity */, |
| - true /* fallback_to_empty_authorized_entity */, |
| - base::Bind(&GCMEncryptionProvider::DecryptMessageWithKey, |
| - weak_ptr_factory_.GetWeakPtr(), message, |
| - callback, encryption_header_iterator.salt(), |
| - dh, encryption_header_iterator.rs())); |
| + key_store_->GetKeys( |
| + app_id, message.sender_id /* authorized_entity */, |
| + true /* fallback_to_empty_authorized_entity */, |
| + base::Bind(&GCMEncryptionProvider::DecryptMessageWithKey, |
| + weak_ptr_factory_.GetWeakPtr(), message.collapse_key, |
| + message.sender_id, salt, public_key, record_size, ciphertext, |
|
johnme
2017/05/23 17:37:48
Use std::move(ciphertext) to avoid copying it into
Peter Beverloo
2017/05/23 17:58:32
Done.
|
| + version, callback)); |
| } |
| void GCMEncryptionProvider::DidGetEncryptionInfo( |
| @@ -222,11 +272,14 @@ void GCMEncryptionProvider::DidCreateEncryptionInfo( |
| } |
| void GCMEncryptionProvider::DecryptMessageWithKey( |
| - const IncomingMessage& message, |
| - const MessageCallback& callback, |
| + const std::string& collapse_key, |
| + const std::string& sender_id, |
| const std::string& salt, |
| - const std::string& dh, |
| - uint64_t rs, |
| + const std::string& public_key, |
| + uint64_t record_size, |
| + const std::string& ciphertext, |
| + GCMMessageCryptographer::Version version, |
| + const MessageCallback& callback, |
| const KeyPair& pair, |
| const std::string& auth_secret) { |
| if (!pair.IsInitialized()) { |
| @@ -238,8 +291,8 @@ void GCMEncryptionProvider::DecryptMessageWithKey( |
| DCHECK_EQ(KeyPair::ECDH_P256, pair.type()); |
| std::string shared_secret; |
| - if (!ComputeSharedP256Secret(pair.private_key(), pair.public_key_x509(), dh, |
| - &shared_secret)) { |
| + if (!ComputeSharedP256Secret(pair.private_key(), pair.public_key_x509(), |
| + public_key, &shared_secret)) { |
| DLOG(ERROR) << "Unable to calculate the shared secret."; |
| callback.Run(DECRYPTION_RESULT_INVALID_SHARED_SECRET, IncomingMessage()); |
| return; |
| @@ -247,19 +300,19 @@ void GCMEncryptionProvider::DecryptMessageWithKey( |
| std::string plaintext; |
| - GCMMessageCryptographer cryptographer( |
| - GCMMessageCryptographer::Version::DRAFT_03); |
| + GCMMessageCryptographer cryptographer(version); |
| - if (!cryptographer.Decrypt(pair.public_key(), dh, shared_secret, auth_secret, |
| - salt, message.raw_data, rs, &plaintext)) { |
| + if (!cryptographer.Decrypt(pair.public_key(), public_key, shared_secret, |
| + auth_secret, salt, ciphertext, record_size, |
| + &plaintext)) { |
| DLOG(ERROR) << "Unable to decrypt the incoming data."; |
| callback.Run(DECRYPTION_RESULT_INVALID_PAYLOAD, IncomingMessage()); |
| return; |
| } |
| IncomingMessage decrypted_message; |
| - decrypted_message.collapse_key = message.collapse_key; |
| - decrypted_message.sender_id = message.sender_id; |
| + decrypted_message.collapse_key = collapse_key; |
| + decrypted_message.sender_id = sender_id; |
| decrypted_message.raw_data.swap(plaintext); |
| decrypted_message.decrypted = true; |
| @@ -267,7 +320,10 @@ void GCMEncryptionProvider::DecryptMessageWithKey( |
| // to make sure that we don't end up in an infinite decryption loop. |
| DCHECK_EQ(0u, decrypted_message.data.size()); |
| - callback.Run(DECRYPTION_RESULT_DECRYPTED, decrypted_message); |
| + callback.Run(version == GCMMessageCryptographer::Version::DRAFT_03 |
| + ? DECRYPTION_RESULT_DECRYPTED_DRAFT_03 |
| + : DECRYPTION_RESULT_DECRYPTED_DRAFT_08, |
| + decrypted_message); |
| } |
| } // namespace gcm |