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

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, 5 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 namespace {
21
22 // TODO(peter): Generalize a URL-safe base64 implementation.
23 bool Base64DecodeUrlSafe(const std::string& input, std::string* output) {
24 if (input.find_first_of("+/=") != std::string::npos)
25 return false;
26
27 // Add padding.
28 size_t padded_size = (input.size() + 3) - (input.size() + 3) % 4;
29 std::string padded_input(input);
30 padded_input.resize(padded_size, '=');
31
32 // Convert to standard base64 alphabet.
33 base::ReplaceChars(padded_input, "-", "+", &padded_input);
34 base::ReplaceChars(padded_input, "_", "/", &padded_input);
35
36 return base::Base64Decode(padded_input, output);
37 }
38
39 } // namespace
40
41 const char kEncryptionProperty[] = "encryption";
42 const char kEncryptionKeyProperty[] = "encryption_key";
43 const char kEncryptionDataProperty[] = "encryption_data";
44
14 GCMEncryptionProvider::GCMEncryptionProvider() 45 GCMEncryptionProvider::GCMEncryptionProvider()
15 : weak_ptr_factory_(this) { 46 : weak_ptr_factory_(this) {
16 } 47 }
17 48
18 GCMEncryptionProvider::~GCMEncryptionProvider() { 49 GCMEncryptionProvider::~GCMEncryptionProvider() {
19 } 50 }
20 51
21 void GCMEncryptionProvider::Init( 52 void GCMEncryptionProvider::Init(
22 const base::FilePath& store_path, 53 const base::FilePath& store_path,
23 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) { 54 const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner) {
(...skipping 28 matching lines...) Expand all
52 const KeyPair& pair) { 83 const KeyPair& pair) {
53 if (!pair.IsInitialized()) { 84 if (!pair.IsInitialized()) {
54 callback.Run(std::string()); 85 callback.Run(std::string());
55 return; 86 return;
56 } 87 }
57 88
58 DCHECK_EQ(KeyPair::ECDH_CURVE_25519, pair.type()); 89 DCHECK_EQ(KeyPair::ECDH_CURVE_25519, pair.type());
59 callback.Run(pair.public_key()); 90 callback.Run(pair.public_key());
60 } 91 }
61 92
93 bool GCMEncryptionProvider::IsEncryptedMessage(const IncomingMessage& message)
94 const {
95 // Both the proprietary GCM protocol and the Web Push protocol require the
96 // encryption and encryption_key properties to be set.
97 if (message.data.find(kEncryptionProperty) == message.data.end() ||
98 message.data.find(kEncryptionKeyProperty) == message.data.end())
99 return false;
100
101 // The proprietary GCM protocol will store the encrypted payload in an app
102 // data field called "encryption_data".
103 if (message.data.find(kEncryptionDataProperty) != message.data.end())
104 return true;
105
106 // The Web Push protocol uses the raw message data for the encrypted payload.
107 return message.raw_data.size() > 0;
108 }
109
110 void GCMEncryptionProvider::DecryptMessage(const std::string& app_id,
111 const IncomingMessage& message,
112 const MessageCallback& callback) {
113 DCHECK(key_store_);
114
115 const auto& encryption_header = message.data.find(kEncryptionProperty);
116 const auto& encryption_key_header = message.data.find(kEncryptionKeyProperty);
117
118 // Callers are expected to call IsEncryptedMessage() prior to this method.
119 DCHECK(encryption_header != message.data.end());
120 DCHECK(encryption_key_header != message.data.end());
121
122 std::vector<EncryptionHeaderValue> encryption_headers;
123 if (!ParseEncryptionHeader(encryption_header->second, &encryption_headers)) {
124 DLOG(ERROR) << "Unable to parse the value of the Encryption header";
125 callback.Run(IncomingMessage(), false /* success */);
126 return;
127 }
128
129 std::vector<EncryptionKeyHeaderValue> encryption_key_headers;
130 if (!ParseEncryptionKeyHeader(encryption_key_header->second,
131 &encryption_key_headers)) {
132 DLOG(ERROR) << "Unable to parse the value of the Encryption-Key header";
133 callback.Run(IncomingMessage(), false /* success */);
134 return;
135 }
136
137 // Note: The HTTP Encrypted Content specification defines that multiple rounds
138 // of encryption are allowed. Chrome's Web Push protocol implementation does
139 // not currently support this, due to not using the "keyid" parameter.
140 if (encryption_headers.size() != 1 || encryption_key_headers.size() != 1) {
141 DLOG(ERROR) << "Only a single encryption key is currently supported";
142 callback.Run(IncomingMessage(), false /* success */);
143 return;
144 }
145
146 std::string ciphertext;
147
148 // Determine the payload. For the proprietary GCM protocol the payload is part
149 // of the app data, base64 URL encoded. For the Web Push protocol, it's
150 // included as raw data for the incoming message.
151 const auto& encryption_data_iter = message.data.find(kEncryptionDataProperty);
152 if (encryption_data_iter != message.data.end()) {
153 if (!Base64DecodeUrlSafe(encryption_data_iter->second, &ciphertext)) {
154 DLOG(ERROR) << "Unable to URL-safe base64 decode the encrypted data";
155 callback.Run(IncomingMessage(), false /* success */);
156 return;
157 }
158 } else {
159 DCHECK(message.raw_data.size());
160 ciphertext = message.raw_data;
161 }
162
163 key_store_->GetKeys(
164 app_id, base::Bind(&GCMEncryptionProvider::DecryptMessageWithKey,
165 weak_ptr_factory_.GetWeakPtr(), message, callback,
166 encryption_headers[0], encryption_key_headers[0],
167 ciphertext));
168 }
169
170 void GCMEncryptionProvider::DecryptMessageWithKey(
171 const IncomingMessage& message,
172 const MessageCallback& callback,
173 const EncryptionHeaderValue& encryption_header,
174 const EncryptionKeyHeaderValue& encryption_key_header,
175 const std::string& ciphertext,
176 const KeyPair& pair) {
177 if (!pair.IsInitialized()) {
178 DLOG(ERROR) << "Unable to retrieve the keys for the incoming message.";
179 callback.Run(IncomingMessage(), false /* success */);
180 return;
181 }
182
183 DCHECK_EQ(KeyPair::ECDH_CURVE_25519, pair.type());
184
185 // Calculate the shared secret between the client's private key and the
186 // server's public key.
187 uint8_t shared_key[crypto::curve25519::kBytes];
188 crypto::curve25519::ScalarMult(
189 reinterpret_cast<const unsigned char*>(pair.private_key().data()),
190 reinterpret_cast<const unsigned char*>(encryption_key_header.dh.data()),
191 shared_key);
192
193 const std::string shared_key_string(shared_key,
194 shared_key + sizeof(shared_key));
195
196 std::string plaintext;
197
198 // TODO(peter): Support explicit keys for the decryption (don't use HKDF).
199
200 GCMMessageCryptographer cryptographer;
201 if (!cryptographer.Decrypt(ciphertext, shared_key_string,
202 encryption_header.salt, encryption_header.rs,
203 &plaintext)) {
204 DLOG(ERROR) << "Unable to decrypt the incoming data.";
205 callback.Run(IncomingMessage(), false /* success */);
206 return;
207 }
208
209 IncomingMessage decrypted_message;
210 decrypted_message.collapse_key = message.collapse_key;
211 decrypted_message.raw_data = plaintext;
212
213 callback.Run(decrypted_message, true /* success */);
214 }
215
62 } // namespace gcm 216 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698