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

Side by Side 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 unified diff | Download patch
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "components/gcm_driver/crypto/gcm_encryption_provider.h" 5 #include "components/gcm_driver/crypto/gcm_encryption_provider.h"
6 6
7 #include "base/base64.h"
7 #include "base/bind.h" 8 #include "base/bind.h"
8 #include "base/logging.h" 9 #include "base/logging.h"
10 #include "base/strings/string_util.h"
11 #include "components/gcm_driver/common/gcm_messages.h"
12 #include "components/gcm_driver/crypto/encryption_header_parsers.h"
9 #include "components/gcm_driver/crypto/gcm_key_store.h" 13 #include "components/gcm_driver/crypto/gcm_key_store.h"
14 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
10 #include "components/gcm_driver/crypto/proto/gcm_encryption_data.pb.h" 15 #include "components/gcm_driver/crypto/proto/gcm_encryption_data.pb.h"
16 #include "crypto/curve25519.h"
11 17
12 namespace gcm { 18 namespace gcm {
13 19
20 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
21 const char kEncryptionKeyProperty[] = "encryption_key";
22 const char kEncryptionDataProperty[] = "encryption_data";
23
14 // Directory in the GCM Store in which the encryption database will be stored. 24 // Directory in the GCM Store in which the encryption database will be stored.
15 const base::FilePath::CharType kEncryptionDirectoryName[] = 25 const base::FilePath::CharType kEncryptionDirectoryName[] =
16 FILE_PATH_LITERAL("Encryption"); 26 FILE_PATH_LITERAL("Encryption");
17 27
18 GCMEncryptionProvider::GCMEncryptionProvider() 28 GCMEncryptionProvider::GCMEncryptionProvider()
19 : weak_ptr_factory_(this) { 29 : weak_ptr_factory_(this) {
20 } 30 }
21 31
22 GCMEncryptionProvider::~GCMEncryptionProvider() { 32 GCMEncryptionProvider::~GCMEncryptionProvider() {
23 } 33 }
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after
64 const PublicKeyCallback& callback, 74 const PublicKeyCallback& callback,
65 const KeyPair& pair) { 75 const KeyPair& pair) {
66 if (!pair.IsInitialized()) { 76 if (!pair.IsInitialized()) {
67 callback.Run(std::string()); 77 callback.Run(std::string());
68 return; 78 return;
69 } 79 }
70 80
71 DCHECK_EQ(KeyPair::ECDH_CURVE_25519, pair.type()); 81 DCHECK_EQ(KeyPair::ECDH_CURVE_25519, pair.type());
72 callback.Run(pair.public_key()); 82 callback.Run(pair.public_key());
73 } 83 }
84 bool GCMEncryptionProvider::IsEncryptedMessage(const IncomingMessage& message)
85 const {
86 // Both the proprietary GCM protocol and the Web Push protocol require the
87 // encryption and encryption_key properties to be set.
88 if (message.data.find(kEncryptionProperty) == message.data.end() ||
89 message.data.find(kEncryptionKeyProperty) == message.data.end())
90 return false;
91
92 // The proprietary GCM protocol will store the encrypted payload in an app
93 // data field called "encryption_data".
94 if (message.data.find(kEncryptionDataProperty) != message.data.end())
95 return true;
96
97 // The Web Push protocol uses the raw message data for the encrypted payload.
98 return message.raw_data.size() > 0;
99 }
100
101 void GCMEncryptionProvider::DecryptMessage(const std::string& app_id,
102 const IncomingMessage& message,
103 const MessageCallback& callback) {
104 DCHECK(key_store_);
105
106 const auto& encryption_header = message.data.find(kEncryptionProperty);
107 const auto& encryption_key_header = message.data.find(kEncryptionKeyProperty);
108
109 // Callers are expected to call IsEncryptedMessage() prior to this method.
110 DCHECK(encryption_header != message.data.end());
111 DCHECK(encryption_key_header != message.data.end());
112
113 std::vector<EncryptionHeaderValue> encryption_headers;
114 if (!ParseEncryptionHeader(encryption_header->second, &encryption_headers)) {
115 DLOG(ERROR) << "Unable to parse the value of the Encryption header";
116 callback.Run(IncomingMessage(), false /* success */);
117 return;
118 }
119
120 std::vector<EncryptionKeyHeaderValue> encryption_key_headers;
121 if (!ParseEncryptionKeyHeader(encryption_key_header->second,
122 &encryption_key_headers)) {
123 DLOG(ERROR) << "Unable to parse the value of the Encryption-Key header";
124 callback.Run(IncomingMessage(), false /* success */);
125 return;
126 }
127
128 // Note: The HTTP Encrypted Content specification defines that multiple rounds
129 // of encryption are allowed. Chrome's Web Push protocol implementation does
130 // not currently support this, due to not using the "keyid" parameter.
131 if (encryption_headers.size() != 1 || encryption_key_headers.size() != 1) {
132 DLOG(ERROR) << "Only a single encryption key is currently supported";
133 callback.Run(IncomingMessage(), false /* success */);
134 return;
135 }
136
137 std::string ciphertext;
138
139 // Determine the payload. For the proprietary GCM protocol the payload is part
140 // of the app data, base64 URL encoded. For the Web Push protocol, it's
141 // included as raw data for the incoming message.
142 const auto& encryption_data_iter = message.data.find(kEncryptionDataProperty);
143 if (encryption_data_iter != message.data.end()) {
144 if (!Base64DecodeUrlSafe(encryption_data_iter->second, &ciphertext)) {
145 DLOG(ERROR) << "Unable to URL-safe base64 decode the encrypted data";
146 callback.Run(IncomingMessage(), false /* success */);
147 return;
148 }
149 } else {
150 DCHECK(message.raw_data.size());
151 ciphertext = message.raw_data;
152 }
153
154 key_store_->GetKeys(
155 app_id, base::Bind(&GCMEncryptionProvider::DecryptMessageWithKey,
156 weak_ptr_factory_.GetWeakPtr(), message, callback,
157 encryption_headers[0], encryption_key_headers[0],
158 ciphertext));
159 }
160
161 void GCMEncryptionProvider::DecryptMessageWithKey(
162 const IncomingMessage& message,
163 const MessageCallback& callback,
164 const EncryptionHeaderValue& encryption_header,
165 const EncryptionKeyHeaderValue& encryption_key_header,
166 const std::string& ciphertext,
167 const KeyPair& pair) {
168 if (!pair.IsInitialized()) {
169 DLOG(ERROR) << "Unable to retrieve the keys for the incoming message.";
170 callback.Run(IncomingMessage(), false /* success */);
171 return;
172 }
173
174 DCHECK_EQ(KeyPair::ECDH_CURVE_25519, pair.type());
175
176 // Calculate the shared secret between the client's private key and the
177 // server's public key.
178 uint8_t shared_key[crypto::curve25519::kBytes];
179 crypto::curve25519::ScalarMult(
180 reinterpret_cast<const unsigned char*>(pair.private_key().data()),
181 reinterpret_cast<const unsigned char*>(encryption_key_header.dh.data()),
182 shared_key);
183
184 const std::string shared_key_string(shared_key,
185 shared_key + sizeof(shared_key));
186
187 std::string plaintext;
188
189 // TODO(peter): Support explicit keys for the decryption (don't use HKDF).
190
191 GCMMessageCryptographer cryptographer;
192 if (!cryptographer.Decrypt(ciphertext, shared_key_string,
193 encryption_header.salt, encryption_header.rs,
194 &plaintext)) {
195 DLOG(ERROR) << "Unable to decrypt the incoming data.";
196 callback.Run(IncomingMessage(), false /* success */);
197 return;
198 }
199
200 IncomingMessage decrypted_message;
201 decrypted_message.collapse_key = message.collapse_key;
202 decrypted_message.raw_data = plaintext;
203
204 callback.Run(decrypted_message, true /* success */);
205 }
74 206
75 } // namespace gcm 207 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698