| 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..6a4bdc5f43b3ca1b676696cc37d5e9208e93816a 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();
|
| + public_key = parser.public_key();
|
| + record_size = parser.record_size();
|
| + ciphertext = parser.ciphertext();
|
| + 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, std::move(salt), std::move(public_key),
|
| + record_size, std::move(ciphertext), 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,
|
| + uint32_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
|
|
|