Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(65)

Unified Diff: components/gcm_driver/crypto/gcm_encryption_provider.cc

Issue 1243563002: Teach the GCM Driver how to decrypt incoming messages. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@gcm-push-keys
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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

Powered by Google App Engine
This is Rietveld 408576698