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 2b775e4c8ee56b0d6e2d1029e63736f7282bff5f..9c53a6e915c0a24be5dddca845fcf45699aefd9d 100644 |
| --- a/components/gcm_driver/crypto/gcm_encryption_provider.cc |
| +++ b/components/gcm_driver/crypto/gcm_encryption_provider.cc |
| @@ -4,13 +4,23 @@ |
| #include "components/gcm_driver/crypto/gcm_encryption_provider.h" |
| +#include "base/base64.h" |
| #include "base/bind.h" |
| #include "base/logging.h" |
| +#include "base/strings/string_util.h" |
| +#include "components/gcm_driver/common/gcm_messages.h" |
| +#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/proto/gcm_encryption_data.pb.h" |
| +#include "crypto/curve25519.h" |
| namespace gcm { |
| +const char kEncryptionProperty[] = "encryption"; |
|
jianli
2015/08/04 00:49:30
All these constants, including the previously adde
Peter Beverloo
2015/09/25 16:37:35
Done for consistency, but please do note that cons
|
| +const char kEncryptionKeyProperty[] = "encryption_key"; |
| +const char kEncryptionDataProperty[] = "encryption_data"; |
| + |
| // Directory in the GCM Store in which the encryption database will be stored. |
| const base::FilePath::CharType kEncryptionDirectoryName[] = |
| FILE_PATH_LITERAL("Encryption"); |
| @@ -71,5 +81,127 @@ void GCMEncryptionProvider::DidCreatePublicKey( |
| DCHECK_EQ(KeyPair::ECDH_CURVE_25519, pair.type()); |
| callback.Run(pair.public_key()); |
| } |
| +bool GCMEncryptionProvider::IsEncryptedMessage(const IncomingMessage& message) |
| + const { |
| + // Both the proprietary GCM protocol and the Web Push protocol require the |
| + // encryption and encryption_key properties to be set. |
| + if (message.data.find(kEncryptionProperty) == message.data.end() || |
| + message.data.find(kEncryptionKeyProperty) == message.data.end()) |
| + return false; |
| + |
| + // The proprietary GCM protocol will store the encrypted payload in an app |
| + // data field called "encryption_data". |
| + if (message.data.find(kEncryptionDataProperty) != message.data.end()) |
| + return true; |
| + |
| + // The Web Push protocol uses the raw message data for the encrypted payload. |
| + return message.raw_data.size() > 0; |
| +} |
| + |
| +void GCMEncryptionProvider::DecryptMessage(const std::string& app_id, |
| + const IncomingMessage& message, |
| + const MessageCallback& callback) { |
| + DCHECK(key_store_); |
| + |
| + const auto& encryption_header = message.data.find(kEncryptionProperty); |
| + const auto& encryption_key_header = message.data.find(kEncryptionKeyProperty); |
| + |
| + // Callers are expected to call IsEncryptedMessage() prior to this method. |
| + DCHECK(encryption_header != message.data.end()); |
| + DCHECK(encryption_key_header != message.data.end()); |
| + |
| + std::vector<EncryptionHeaderValue> encryption_headers; |
| + if (!ParseEncryptionHeader(encryption_header->second, &encryption_headers)) { |
| + DLOG(ERROR) << "Unable to parse the value of the Encryption header"; |
| + callback.Run(IncomingMessage(), false /* success */); |
| + return; |
| + } |
| + |
| + std::vector<EncryptionKeyHeaderValue> encryption_key_headers; |
| + if (!ParseEncryptionKeyHeader(encryption_key_header->second, |
| + &encryption_key_headers)) { |
| + DLOG(ERROR) << "Unable to parse the value of the Encryption-Key header"; |
| + callback.Run(IncomingMessage(), false /* success */); |
| + return; |
| + } |
| + |
| + // Note: The HTTP Encrypted Content specification defines that multiple rounds |
| + // of encryption are allowed. Chrome's Web Push protocol implementation does |
| + // not currently support this, due to not using the "keyid" parameter. |
| + if (encryption_headers.size() != 1 || encryption_key_headers.size() != 1) { |
| + DLOG(ERROR) << "Only a single encryption key is currently supported"; |
| + callback.Run(IncomingMessage(), false /* success */); |
| + return; |
| + } |
| + |
| + std::string ciphertext; |
| + |
| + // Determine the payload. For the proprietary GCM protocol the payload is part |
| + // of the app data, base64 URL encoded. For the Web Push protocol, it's |
| + // included as raw data for the incoming message. |
| + const auto& encryption_data_iter = message.data.find(kEncryptionDataProperty); |
| + if (encryption_data_iter != message.data.end()) { |
| + if (!Base64DecodeUrlSafe(encryption_data_iter->second, &ciphertext)) { |
| + DLOG(ERROR) << "Unable to URL-safe base64 decode the encrypted data"; |
| + callback.Run(IncomingMessage(), false /* success */); |
| + return; |
| + } |
| + } else { |
| + DCHECK(message.raw_data.size()); |
| + ciphertext = message.raw_data; |
| + } |
| + |
| + key_store_->GetKeys( |
| + app_id, base::Bind(&GCMEncryptionProvider::DecryptMessageWithKey, |
| + weak_ptr_factory_.GetWeakPtr(), message, callback, |
| + encryption_headers[0], encryption_key_headers[0], |
| + ciphertext)); |
| +} |
| + |
| +void GCMEncryptionProvider::DecryptMessageWithKey( |
| + const IncomingMessage& message, |
| + const MessageCallback& callback, |
| + const EncryptionHeaderValue& encryption_header, |
| + const EncryptionKeyHeaderValue& encryption_key_header, |
| + const std::string& ciphertext, |
| + const KeyPair& pair) { |
| + if (!pair.IsInitialized()) { |
| + DLOG(ERROR) << "Unable to retrieve the keys for the incoming message."; |
| + callback.Run(IncomingMessage(), false /* success */); |
| + return; |
| + } |
| + |
| + DCHECK_EQ(KeyPair::ECDH_CURVE_25519, pair.type()); |
| + |
| + // Calculate the shared secret between the client's private key and the |
| + // server's public key. |
| + uint8_t shared_key[crypto::curve25519::kBytes]; |
| + crypto::curve25519::ScalarMult( |
| + reinterpret_cast<const unsigned char*>(pair.private_key().data()), |
| + reinterpret_cast<const unsigned char*>(encryption_key_header.dh.data()), |
| + shared_key); |
| + |
| + const std::string shared_key_string(shared_key, |
| + shared_key + sizeof(shared_key)); |
| + |
| + std::string plaintext; |
| + |
| + // TODO(peter): Support explicit keys for the decryption (don't use HKDF). |
| + |
| + GCMMessageCryptographer cryptographer; |
| + if (!cryptographer.Decrypt(ciphertext, shared_key_string, |
| + encryption_header.salt, encryption_header.rs, |
| + &plaintext)) { |
| + DLOG(ERROR) << "Unable to decrypt the incoming data."; |
| + callback.Run(IncomingMessage(), false /* success */); |
| + return; |
| + } |
| + |
| + IncomingMessage decrypted_message; |
| + decrypted_message.collapse_key = message.collapse_key; |
| + decrypted_message.raw_data = plaintext; |
| + |
| + callback.Run(decrypted_message, true /* success */); |
| +} |
| } // namespace gcm |