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

Side by Side Diff: components/gcm_driver/crypto/gcm_encryption_provider.cc

Issue 2892033002: Enable support for draft-ietf-webpush-encryption-08 (Closed)
Patch Set: Enable support for draft-ietf-webpush-encryption-08 Created 3 years, 7 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 <vector> 7 #include <vector>
8 8
9 #include "base/base64.h" 9 #include "base/base64.h"
10 #include "base/bind.h" 10 #include "base/bind.h"
11 #include "base/logging.h" 11 #include "base/logging.h"
12 #include "components/gcm_driver/common/gcm_messages.h" 12 #include "components/gcm_driver/common/gcm_messages.h"
13 #include "components/gcm_driver/crypto/encryption_header_parsers.h" 13 #include "components/gcm_driver/crypto/encryption_header_parsers.h"
14 #include "components/gcm_driver/crypto/gcm_key_store.h" 14 #include "components/gcm_driver/crypto/gcm_key_store.h"
15 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h" 15 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
16 #include "components/gcm_driver/crypto/message_payload_parser.h"
16 #include "components/gcm_driver/crypto/p256_key_util.h" 17 #include "components/gcm_driver/crypto/p256_key_util.h"
17 #include "components/gcm_driver/crypto/proto/gcm_encryption_data.pb.h" 18 #include "components/gcm_driver/crypto/proto/gcm_encryption_data.pb.h"
18 19
19 namespace gcm { 20 namespace gcm {
20 21
21 namespace { 22 namespace {
22 23
23 const char kEncryptionProperty[] = "encryption"; 24 const char kEncryptionProperty[] = "encryption";
25 const char kContentEncodingProperty[] = "content-encoding";
24 const char kCryptoKeyProperty[] = "crypto-key"; 26 const char kCryptoKeyProperty[] = "crypto-key";
25 27
28 // Content coding name defined by ietf-httpbis-encryption-encoding.
29 const char kContentCodingAes128Gcm[] = "aes128gcm";
30
26 // Directory in the GCM Store in which the encryption database will be stored. 31 // Directory in the GCM Store in which the encryption database will be stored.
27 const base::FilePath::CharType kEncryptionDirectoryName[] = 32 const base::FilePath::CharType kEncryptionDirectoryName[] =
28 FILE_PATH_LITERAL("Encryption"); 33 FILE_PATH_LITERAL("Encryption");
29 34
30 } // namespace 35 } // namespace
31 36
32 std::string GCMEncryptionProvider::ToDecryptionResultDetailsString( 37 std::string GCMEncryptionProvider::ToDecryptionResultDetailsString(
33 DecryptionResult result) { 38 DecryptionResult result) {
34 switch (result) { 39 switch (result) {
35 case DECRYPTION_RESULT_UNENCRYPTED: 40 case DECRYPTION_RESULT_UNENCRYPTED:
36 return "Message was not encrypted"; 41 return "Message was not encrypted";
37 case DECRYPTION_RESULT_DECRYPTED: 42 case DECRYPTION_RESULT_DECRYPTED_DRAFT_03:
38 return "Message decrypted"; 43 return "Message decrypted (draft 03)";
44 case DECRYPTION_RESULT_DECRYPTED_DRAFT_08:
45 return "Message decrypted (draft 08)";
39 case DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER: 46 case DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER:
40 return "Invalid format for the Encryption header"; 47 return "Invalid format for the Encryption header";
41 case DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER: 48 case DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER:
42 return "Invalid format for the Crypto-Key header"; 49 return "Invalid format for the Crypto-Key header";
43 case DECRYPTION_RESULT_NO_KEYS: 50 case DECRYPTION_RESULT_NO_KEYS:
44 return "There are no associated keys with the subscription"; 51 return "There are no associated keys with the subscription";
45 case DECRYPTION_RESULT_INVALID_SHARED_SECRET: 52 case DECRYPTION_RESULT_INVALID_SHARED_SECRET:
46 return "The shared secret cannot be derived from the keying material"; 53 return "The shared secret cannot be derived from the keying material";
47 case DECRYPTION_RESULT_INVALID_PAYLOAD: 54 case DECRYPTION_RESULT_INVALID_PAYLOAD:
48 return "AES-GCM decryption failed"; 55 return "AES-GCM decryption failed";
56 case DECRYPTION_RESULT_INVALID_BINARY_HEADER:
57 return "The message's binary header could not be parsed.";
49 } 58 }
50 59
51 NOTREACHED(); 60 NOTREACHED();
52 return "(invalid result)"; 61 return "(invalid result)";
53 } 62 }
54 63
55 GCMEncryptionProvider::GCMEncryptionProvider() 64 GCMEncryptionProvider::GCMEncryptionProvider()
56 : weak_ptr_factory_(this) { 65 : weak_ptr_factory_(this) {
57 } 66 }
58 67
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after
90 void GCMEncryptionProvider::RemoveEncryptionInfo( 99 void GCMEncryptionProvider::RemoveEncryptionInfo(
91 const std::string& app_id, 100 const std::string& app_id,
92 const std::string& authorized_entity, 101 const std::string& authorized_entity,
93 const base::Closure& callback) { 102 const base::Closure& callback) {
94 DCHECK(key_store_); 103 DCHECK(key_store_);
95 key_store_->RemoveKeys(app_id, authorized_entity, callback); 104 key_store_->RemoveKeys(app_id, authorized_entity, callback);
96 } 105 }
97 106
98 bool GCMEncryptionProvider::IsEncryptedMessage(const IncomingMessage& message) 107 bool GCMEncryptionProvider::IsEncryptedMessage(const IncomingMessage& message)
99 const { 108 const {
109 // Messages that explicitly specify their content coding to be "aes128gcm"
110 // indicate that they use draft-ietf-webpush-encryption-08.
111 auto content_encoding_iter = message.data.find(kContentEncodingProperty);
112 if (content_encoding_iter != message.data.end() &&
113 content_encoding_iter->second == kContentCodingAes128Gcm) {
114 return true;
115 }
116
100 // The Web Push protocol requires the encryption and crypto-key properties to 117 // The Web Push protocol requires the encryption and crypto-key properties to
101 // be set, and the raw_data field to be populated with the payload. 118 // be set, and the raw_data field to be populated with the payload.
102 if (message.data.find(kEncryptionProperty) == message.data.end() || 119 if (message.data.find(kEncryptionProperty) == message.data.end() ||
103 message.data.find(kCryptoKeyProperty) == message.data.end()) 120 message.data.find(kCryptoKeyProperty) == message.data.end())
104 return false; 121 return false;
105 122
106 return message.raw_data.size() > 0; 123 return message.raw_data.size() > 0;
107 } 124 }
108 125
109 void GCMEncryptionProvider::DecryptMessage( 126 void GCMEncryptionProvider::DecryptMessage(
110 const std::string& app_id, 127 const std::string& app_id,
111 const IncomingMessage& message, 128 const IncomingMessage& message,
112 const MessageCallback& callback) { 129 const MessageCallback& callback) {
113 DCHECK(key_store_); 130 DCHECK(key_store_);
114 if (!IsEncryptedMessage(message)) { 131 if (!IsEncryptedMessage(message)) {
115 callback.Run(DECRYPTION_RESULT_UNENCRYPTED, message); 132 callback.Run(DECRYPTION_RESULT_UNENCRYPTED, message);
116 return; 133 return;
117 } 134 }
118 135
119 // IsEncryptedMessage() verifies that both the Encryption and Crypto-Key HTTP 136 std::string salt, public_key, ciphertext;
120 // headers have been provided for the |message|. 137 GCMMessageCryptographer::Version version;
121 const auto& encryption_header = message.data.find(kEncryptionProperty); 138 uint32_t record_size;
122 const auto& crypto_key_header = message.data.find(kCryptoKeyProperty);
123 139
124 DCHECK(encryption_header != message.data.end()); 140 auto content_encoding_iter = message.data.find(kContentEncodingProperty);
125 DCHECK(crypto_key_header != message.data.end()); 141 if (content_encoding_iter != message.data.end() &&
142 content_encoding_iter->second == kContentCodingAes128Gcm) {
143 // The message follows encryption per draft-ietf-webpush-encryption-08. Use
144 // the binary header of the message to derive the values.
126 145
127 EncryptionHeaderIterator encryption_header_iterator( 146 MessagePayloadParser parser(message.raw_data);
128 encryption_header->second.begin(), encryption_header->second.end()); 147 if (!parser.IsValid()) {
129 if (!encryption_header_iterator.GetNext()) { 148 DLOG(ERROR) << "Unable to parse the message's binary header";
130 DLOG(ERROR) << "Unable to parse the value of the Encryption header"; 149 callback.Run(DECRYPTION_RESULT_INVALID_BINARY_HEADER, IncomingMessage());
131 callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, 150 return;
132 IncomingMessage()); 151 }
133 return;
134 }
135 152
136 if (encryption_header_iterator.salt().size() != 153 salt = parser.salt();
137 GCMMessageCryptographer::kSaltSize) { 154 public_key = parser.public_key();
138 DLOG(ERROR) << "Invalid values supplied in the Encryption header"; 155 record_size = parser.record_size();
139 callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER, 156 ciphertext = parser.ciphertext();
140 IncomingMessage()); 157 version = GCMMessageCryptographer::Version::DRAFT_08;
141 return;
142 }
143 158
144 CryptoKeyHeaderIterator crypto_key_header_iterator( 159 } else {
145 crypto_key_header->second.begin(), crypto_key_header->second.end()); 160 // The message follows encryption per draft-ietf-webpush-encryption-03. Use
146 if (!crypto_key_header_iterator.GetNext()) { 161 // the Encryption and Crypto-Key header values to derive the values.
147 DLOG(ERROR) << "Unable to parse the value of the Crypto-Key header";
148 callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
149 IncomingMessage());
150 return;
151 }
152 162
153 // Ignore values that don't include the "dh" property. When using VAPID, it is 163 const auto& encryption_header = message.data.find(kEncryptionProperty);
154 // valid for the application server to supply multiple values. 164 DCHECK(encryption_header != message.data.end());
155 while (crypto_key_header_iterator.dh().empty() &&
156 crypto_key_header_iterator.GetNext()) {}
157 165
158 bool valid_crypto_key_header = false; 166 const auto& crypto_key_header = message.data.find(kCryptoKeyProperty);
159 std::string dh; 167 DCHECK(crypto_key_header != message.data.end());
160 168
161 if (!crypto_key_header_iterator.dh().empty()) { 169 EncryptionHeaderIterator encryption_header_iterator(
162 dh = crypto_key_header_iterator.dh(); 170 encryption_header->second.begin(), encryption_header->second.end());
163 valid_crypto_key_header = true; 171 if (!encryption_header_iterator.GetNext()) {
172 DLOG(ERROR) << "Unable to parse the value of the Encryption header";
173 callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER,
174 IncomingMessage());
175 return;
176 }
164 177
165 // Guard against the "dh" property being included more than once. 178 if (encryption_header_iterator.salt().size() !=
166 while (crypto_key_header_iterator.GetNext()) { 179 GCMMessageCryptographer::kSaltSize) {
167 if (crypto_key_header_iterator.dh().empty()) 180 DLOG(ERROR) << "Invalid values supplied in the Encryption header";
168 continue; 181 callback.Run(DECRYPTION_RESULT_INVALID_ENCRYPTION_HEADER,
182 IncomingMessage());
183 return;
184 }
169 185
170 valid_crypto_key_header = false; 186 salt = encryption_header_iterator.salt();
171 break; 187 record_size = encryption_header_iterator.rs();
188
189 CryptoKeyHeaderIterator crypto_key_header_iterator(
190 crypto_key_header->second.begin(), crypto_key_header->second.end());
191 if (!crypto_key_header_iterator.GetNext()) {
192 DLOG(ERROR) << "Unable to parse the value of the Crypto-Key header";
193 callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
194 IncomingMessage());
195 return;
172 } 196 }
173 }
174 197
175 if (!valid_crypto_key_header) { 198 // Ignore values that don't include the "dh" property. When using VAPID, it
176 DLOG(ERROR) << "Invalid values supplied in the Crypto-Key header"; 199 // is valid for the application server to supply multiple values.
177 callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER, 200 while (crypto_key_header_iterator.dh().empty() &&
178 IncomingMessage()); 201 crypto_key_header_iterator.GetNext()) {
179 return; 202 }
203
204 bool valid_crypto_key_header = false;
205
206 if (!crypto_key_header_iterator.dh().empty()) {
207 public_key = crypto_key_header_iterator.dh();
208 valid_crypto_key_header = true;
209
210 // Guard against the "dh" property being included more than once.
211 while (crypto_key_header_iterator.GetNext()) {
212 if (crypto_key_header_iterator.dh().empty())
213 continue;
214
215 valid_crypto_key_header = false;
216 break;
217 }
218 }
219
220 if (!valid_crypto_key_header) {
221 DLOG(ERROR) << "Invalid values supplied in the Crypto-Key header";
222 callback.Run(DECRYPTION_RESULT_INVALID_CRYPTO_KEY_HEADER,
223 IncomingMessage());
224 return;
225 }
226
227 ciphertext = message.raw_data;
228 version = GCMMessageCryptographer::Version::DRAFT_03;
180 } 229 }
181 230
182 // Use |fallback_to_empty_authorized_entity|, since this message might have 231 // Use |fallback_to_empty_authorized_entity|, since this message might have
183 // been sent to either an InstanceID token or a non-InstanceID registration. 232 // been sent to either an InstanceID token or a non-InstanceID registration.
184 key_store_->GetKeys(app_id, message.sender_id /* authorized_entity */, 233 key_store_->GetKeys(
185 true /* fallback_to_empty_authorized_entity */, 234 app_id, message.sender_id /* authorized_entity */,
186 base::Bind(&GCMEncryptionProvider::DecryptMessageWithKey, 235 true /* fallback_to_empty_authorized_entity */,
187 weak_ptr_factory_.GetWeakPtr(), message, 236 base::Bind(&GCMEncryptionProvider::DecryptMessageWithKey,
188 callback, encryption_header_iterator.salt(), 237 weak_ptr_factory_.GetWeakPtr(), message.collapse_key,
189 dh, encryption_header_iterator.rs())); 238 message.sender_id, std::move(salt), std::move(public_key),
239 record_size, std::move(ciphertext), version, callback));
190 } 240 }
191 241
192 void GCMEncryptionProvider::DidGetEncryptionInfo( 242 void GCMEncryptionProvider::DidGetEncryptionInfo(
193 const std::string& app_id, 243 const std::string& app_id,
194 const std::string& authorized_entity, 244 const std::string& authorized_entity,
195 const EncryptionInfoCallback& callback, 245 const EncryptionInfoCallback& callback,
196 const KeyPair& pair, 246 const KeyPair& pair,
197 const std::string& auth_secret) { 247 const std::string& auth_secret) {
198 if (!pair.IsInitialized()) { 248 if (!pair.IsInitialized()) {
199 key_store_->CreateKeys( 249 key_store_->CreateKeys(
(...skipping 15 matching lines...) Expand all
215 callback.Run(std::string() /* p256dh */, 265 callback.Run(std::string() /* p256dh */,
216 std::string() /* auth_secret */); 266 std::string() /* auth_secret */);
217 return; 267 return;
218 } 268 }
219 269
220 DCHECK_EQ(KeyPair::ECDH_P256, pair.type()); 270 DCHECK_EQ(KeyPair::ECDH_P256, pair.type());
221 callback.Run(pair.public_key(), auth_secret); 271 callback.Run(pair.public_key(), auth_secret);
222 } 272 }
223 273
224 void GCMEncryptionProvider::DecryptMessageWithKey( 274 void GCMEncryptionProvider::DecryptMessageWithKey(
225 const IncomingMessage& message, 275 const std::string& collapse_key,
276 const std::string& sender_id,
277 const std::string& salt,
278 const std::string& public_key,
279 uint32_t record_size,
280 const std::string& ciphertext,
281 GCMMessageCryptographer::Version version,
226 const MessageCallback& callback, 282 const MessageCallback& callback,
227 const std::string& salt,
228 const std::string& dh,
229 uint64_t rs,
230 const KeyPair& pair, 283 const KeyPair& pair,
231 const std::string& auth_secret) { 284 const std::string& auth_secret) {
232 if (!pair.IsInitialized()) { 285 if (!pair.IsInitialized()) {
233 DLOG(ERROR) << "Unable to retrieve the keys for the incoming message."; 286 DLOG(ERROR) << "Unable to retrieve the keys for the incoming message.";
234 callback.Run(DECRYPTION_RESULT_NO_KEYS, IncomingMessage()); 287 callback.Run(DECRYPTION_RESULT_NO_KEYS, IncomingMessage());
235 return; 288 return;
236 } 289 }
237 290
238 DCHECK_EQ(KeyPair::ECDH_P256, pair.type()); 291 DCHECK_EQ(KeyPair::ECDH_P256, pair.type());
239 292
240 std::string shared_secret; 293 std::string shared_secret;
241 if (!ComputeSharedP256Secret(pair.private_key(), pair.public_key_x509(), dh, 294 if (!ComputeSharedP256Secret(pair.private_key(), pair.public_key_x509(),
242 &shared_secret)) { 295 public_key, &shared_secret)) {
243 DLOG(ERROR) << "Unable to calculate the shared secret."; 296 DLOG(ERROR) << "Unable to calculate the shared secret.";
244 callback.Run(DECRYPTION_RESULT_INVALID_SHARED_SECRET, IncomingMessage()); 297 callback.Run(DECRYPTION_RESULT_INVALID_SHARED_SECRET, IncomingMessage());
245 return; 298 return;
246 } 299 }
247 300
248 std::string plaintext; 301 std::string plaintext;
249 302
250 GCMMessageCryptographer cryptographer( 303 GCMMessageCryptographer cryptographer(version);
251 GCMMessageCryptographer::Version::DRAFT_03);
252 304
253 if (!cryptographer.Decrypt(pair.public_key(), dh, shared_secret, auth_secret, 305 if (!cryptographer.Decrypt(pair.public_key(), public_key, shared_secret,
254 salt, message.raw_data, rs, &plaintext)) { 306 auth_secret, salt, ciphertext, record_size,
307 &plaintext)) {
255 DLOG(ERROR) << "Unable to decrypt the incoming data."; 308 DLOG(ERROR) << "Unable to decrypt the incoming data.";
256 callback.Run(DECRYPTION_RESULT_INVALID_PAYLOAD, IncomingMessage()); 309 callback.Run(DECRYPTION_RESULT_INVALID_PAYLOAD, IncomingMessage());
257 return; 310 return;
258 } 311 }
259 312
260 IncomingMessage decrypted_message; 313 IncomingMessage decrypted_message;
261 decrypted_message.collapse_key = message.collapse_key; 314 decrypted_message.collapse_key = collapse_key;
262 decrypted_message.sender_id = message.sender_id; 315 decrypted_message.sender_id = sender_id;
263 decrypted_message.raw_data.swap(plaintext); 316 decrypted_message.raw_data.swap(plaintext);
264 decrypted_message.decrypted = true; 317 decrypted_message.decrypted = true;
265 318
266 // There must be no data associated with the decrypted message at this point, 319 // There must be no data associated with the decrypted message at this point,
267 // to make sure that we don't end up in an infinite decryption loop. 320 // to make sure that we don't end up in an infinite decryption loop.
268 DCHECK_EQ(0u, decrypted_message.data.size()); 321 DCHECK_EQ(0u, decrypted_message.data.size());
269 322
270 callback.Run(DECRYPTION_RESULT_DECRYPTED, decrypted_message); 323 callback.Run(version == GCMMessageCryptographer::Version::DRAFT_03
324 ? DECRYPTION_RESULT_DECRYPTED_DRAFT_03
325 : DECRYPTION_RESULT_DECRYPTED_DRAFT_08,
326 decrypted_message);
271 } 327 }
272 328
273 } // namespace gcm 329 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698