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

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

Issue 2716443002: Implement support for draft-ietf-webpush-encryption-08 (Closed)
Patch Set: Implement support for draft-ietf-webpush-encryption-08 Created 3 years, 9 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_message_cryptographer.h" 5 #include "components/gcm_driver/crypto/gcm_message_cryptographer.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
44 // https://tools.ietf.org/html/draft-ietf-webpush-encryption-03 44 // https://tools.ietf.org/html/draft-ietf-webpush-encryption-03
45 // https://tools.ietf.org/html/draft-ietf-httpbis-encryption-encoding-02 45 // https://tools.ietf.org/html/draft-ietf-httpbis-encryption-encoding-02
46 class WebPushEncryptionDraft03 46 class WebPushEncryptionDraft03
47 : public GCMMessageCryptographer::EncryptionScheme { 47 : public GCMMessageCryptographer::EncryptionScheme {
48 public: 48 public:
49 WebPushEncryptionDraft03() = default; 49 WebPushEncryptionDraft03() = default;
50 ~WebPushEncryptionDraft03() override = default; 50 ~WebPushEncryptionDraft03() override = default;
51 51
52 // GCMMessageCryptographer::EncryptionScheme implementation. 52 // GCMMessageCryptographer::EncryptionScheme implementation.
53 std::string DerivePseudoRandomKey( 53 std::string DerivePseudoRandomKey(
54 const base::StringPiece& /* recipient_public_key */,
55 const base::StringPiece& /* sender_public_key */,
54 const base::StringPiece& ecdh_shared_secret, 56 const base::StringPiece& ecdh_shared_secret,
55 const base::StringPiece& auth_secret) override { 57 const base::StringPiece& auth_secret) override {
56 std::stringstream info_stream; 58 std::stringstream info_stream;
57 info_stream << "Content-Encoding: auth" << '\x00'; 59 info_stream << "Content-Encoding: auth" << '\x00';
Peter Beverloo 2017/05/18 17:10:21 Updated this too per the previous CL.
58 60
59 crypto::HKDF hkdf(ecdh_shared_secret, auth_secret, info_stream.str(), 61 crypto::HKDF hkdf(ecdh_shared_secret, auth_secret, info_stream.str(),
60 32, /* key_bytes_to_generate */ 62 32, /* key_bytes_to_generate */
61 0, /* iv_bytes_to_generate */ 63 0, /* iv_bytes_to_generate */
62 0 /* subkey_secret_bytes_to_generate */); 64 0 /* subkey_secret_bytes_to_generate */);
63 65
64 return hkdf.client_write_key().as_string(); 66 return hkdf.client_write_key().as_string();
65 } 67 }
66 68
67 // Creates the info parameter for an HKDF value for the given 69 // Creates the info parameter for an HKDF value for the given
(...skipping 82 matching lines...) Expand 10 before | Expand all | Expand 10 after
150 } 152 }
151 153
152 record.remove_prefix(padding_length); 154 record.remove_prefix(padding_length);
153 return true; 155 return true;
154 } 156 }
155 157
156 private: 158 private:
157 DISALLOW_COPY_AND_ASSIGN(WebPushEncryptionDraft03); 159 DISALLOW_COPY_AND_ASSIGN(WebPushEncryptionDraft03);
158 }; 160 };
159 161
162 // Implementation of draft 08 of the Web Push Encryption standard:
163 // https://tools.ietf.org/html/draft-ietf-webpush-encryption-08
164 // https://tools.ietf.org/html/draft-ietf-httpbis-encryption-encoding-07
165 class WebPushEncryptionDraft08
166 : public GCMMessageCryptographer::EncryptionScheme {
167 public:
168 WebPushEncryptionDraft08() = default;
169 ~WebPushEncryptionDraft08() override = default;
170
171 // GCMMessageCryptographer::EncryptionScheme implementation.
172 std::string DerivePseudoRandomKey(
173 const base::StringPiece& recipient_public_key,
174 const base::StringPiece& sender_public_key,
175 const base::StringPiece& ecdh_shared_secret,
176 const base::StringPiece& auth_secret) override {
177 std::stringstream info_stream;
eroman 2017/05/16 21:03:27 same comment as on other CLs -- i don't think stri
Peter Beverloo 2017/05/18 17:10:21 Done.
178 info_stream << "WebPush: info" << '\x00';
179 info_stream << recipient_public_key;
180 info_stream << sender_public_key;
181
182 const std::string info = info_stream.str();
183 DCHECK_EQ(info.size(), 144u);
184
185 crypto::HKDF hkdf(ecdh_shared_secret, auth_secret, info_stream.str(),
186 32, /* key_bytes_to_generate */
187 0, /* iv_bytes_to_generate */
188 0 /* subkey_secret_bytes_to_generate */);
189
190 return hkdf.client_write_key().as_string();
191 }
192
193 // The info string used for generating the content encryption key and the
194 // nonce was simplified in draft-ietf-webpush-encryption-08, because the
195 // public keys of both the recipient and the sender are now in the PRK.
196 std::string GenerateInfoForContentEncoding(
197 EncodingType type,
198 const base::StringPiece& /* recipient_public_key */,
199 const base::StringPiece& /* sender_public_key */) override {
200 std::stringstream info_stream;
201 info_stream << "Content-Encoding: ";
202
203 switch (type) {
204 case EncodingType::CONTENT_ENCRYPTION_KEY:
205 info_stream << "aes128gcm";
206 break;
207 case EncodingType::NONCE:
208 info_stream << "nonce";
209 break;
210 }
211
212 info_stream << '\x00';
213 return info_stream.str();
214 }
215
216 // draft-ietf-webpush-encryption-08 defines that the padding follows the
217 // plaintext of a message. A delimiter byte (0x02 for the final record) will
218 // be added, and then zero or more bytes of padding.
219 //
220 // TODO(peter): Add support for message padding if the GCMMessageCryptographer
221 // starts encrypting payloads for reasons other than testing.
222 std::string CreateRecord(const base::StringPiece& plaintext) override {
223 std::string record;
224 record.reserve(plaintext.size() + sizeof(uint8_t));
225 plaintext.AppendToString(&record);
226
227 record.append(sizeof(uint8_t), '\x02');
228 return record;
229 }
230
231 // The record padding in draft-ietf-webpush-encryption-08 is included at the
232 // end of the record. The length is not defined, but all padding bytes must be
233 // zero until the delimiter (0x02) is found.
234 bool ValidateAndRemovePadding(base::StringPiece& record) override {
235 size_t padding_length = 1;
236 for (; padding_length < record.size(); ++padding_length) {
237 size_t offset = record.size() - padding_length;
238
239 if (record[offset] == 0x02 /* padding delimiter octet */)
240 break;
241
242 if (record[offset] != 0x00 /* valid padding byte */)
243 return false;
244 }
245
246 record.remove_suffix(padding_length);
eroman 2017/05/16 21:03:27 Is this correct in the case where we don't loop?
Peter Beverloo 2017/05/18 17:10:21 I added a DCHECK to verify that record.size() >= 1
247 return true;
248 }
249
250 private:
251 DISALLOW_COPY_AND_ASSIGN(WebPushEncryptionDraft08);
252 };
253
160 } // namespace 254 } // namespace
161 255
162 const size_t GCMMessageCryptographer::kAuthenticationTagBytes = 16; 256 const size_t GCMMessageCryptographer::kAuthenticationTagBytes = 16;
163 const size_t GCMMessageCryptographer::kSaltSize = 16; 257 const size_t GCMMessageCryptographer::kSaltSize = 16;
164 258
165 GCMMessageCryptographer::GCMMessageCryptographer(Version version) { 259 GCMMessageCryptographer::GCMMessageCryptographer(Version version) {
166 switch (version) { 260 switch (version) {
167 case Version::DRAFT_03: 261 case Version::DRAFT_03:
168 encryption_scheme_ = base::MakeUnique<WebPushEncryptionDraft03>(); 262 encryption_scheme_ = base::MakeUnique<WebPushEncryptionDraft03>();
169 return; 263 return;
264 case Version::DRAFT_08:
265 encryption_scheme_ = base::MakeUnique<WebPushEncryptionDraft08>();
266 return;
170 } 267 }
171 268
172 NOTREACHED(); 269 NOTREACHED();
173 } 270 }
174 271
175 GCMMessageCryptographer::~GCMMessageCryptographer() = default; 272 GCMMessageCryptographer::~GCMMessageCryptographer() = default;
176 273
177 bool GCMMessageCryptographer::Encrypt( 274 bool GCMMessageCryptographer::Encrypt(
178 const base::StringPiece& recipient_public_key, 275 const base::StringPiece& recipient_public_key,
179 const base::StringPiece& sender_public_key, 276 const base::StringPiece& sender_public_key,
180 const base::StringPiece& ecdh_shared_secret, 277 const base::StringPiece& ecdh_shared_secret,
181 const base::StringPiece& auth_secret, 278 const base::StringPiece& auth_secret,
182 const base::StringPiece& salt, 279 const base::StringPiece& salt,
183 const base::StringPiece& plaintext, 280 const base::StringPiece& plaintext,
184 size_t* record_size, 281 size_t* record_size,
185 std::string* ciphertext) const { 282 std::string* ciphertext) const {
186 DCHECK_EQ(recipient_public_key.size(), 65u); 283 DCHECK_EQ(recipient_public_key.size(), 65u);
187 DCHECK_EQ(sender_public_key.size(), 65u); 284 DCHECK_EQ(sender_public_key.size(), 65u);
188 DCHECK_EQ(ecdh_shared_secret.size(), 32u); 285 DCHECK_EQ(ecdh_shared_secret.size(), 32u);
189 DCHECK_EQ(auth_secret.size(), 16u); 286 DCHECK_EQ(auth_secret.size(), 16u);
190 DCHECK_EQ(salt.size(), 16u); 287 DCHECK_EQ(salt.size(), 16u);
191 DCHECK(record_size); 288 DCHECK(record_size);
192 DCHECK(ciphertext); 289 DCHECK(ciphertext);
193 290
194 std::string prk = encryption_scheme_->DerivePseudoRandomKey( 291 std::string prk = encryption_scheme_->DerivePseudoRandomKey(
195 ecdh_shared_secret, auth_secret); 292 recipient_public_key, sender_public_key, ecdh_shared_secret, auth_secret);
196 293
197 std::string content_encryption_key = DeriveContentEncryptionKey( 294 std::string content_encryption_key = DeriveContentEncryptionKey(
198 recipient_public_key, sender_public_key, prk, salt); 295 recipient_public_key, sender_public_key, prk, salt);
199 std::string nonce = 296 std::string nonce =
200 DeriveNonce(recipient_public_key, sender_public_key, prk, salt); 297 DeriveNonce(recipient_public_key, sender_public_key, prk, salt);
201 298
202 std::string record = encryption_scheme_->CreateRecord(plaintext); 299 std::string record = encryption_scheme_->CreateRecord(plaintext);
203 std::string encrypted_record; 300 std::string encrypted_record;
204 301
205 if (!TransformRecord(Direction::ENCRYPT, record, content_encryption_key, 302 if (!TransformRecord(Direction::ENCRYPT, record, content_encryption_key,
(...skipping 22 matching lines...) Expand all
228 DCHECK_EQ(sender_public_key.size(), 65u); 325 DCHECK_EQ(sender_public_key.size(), 65u);
229 DCHECK_EQ(ecdh_shared_secret.size(), 32u); 326 DCHECK_EQ(ecdh_shared_secret.size(), 32u);
230 DCHECK_EQ(auth_secret.size(), 16u); 327 DCHECK_EQ(auth_secret.size(), 16u);
231 DCHECK_EQ(salt.size(), 16u); 328 DCHECK_EQ(salt.size(), 16u);
232 DCHECK(plaintext); 329 DCHECK(plaintext);
233 330
234 if (record_size <= 1) 331 if (record_size <= 1)
235 return false; 332 return false;
236 333
237 std::string prk = encryption_scheme_->DerivePseudoRandomKey( 334 std::string prk = encryption_scheme_->DerivePseudoRandomKey(
238 ecdh_shared_secret, auth_secret); 335 recipient_public_key, sender_public_key, ecdh_shared_secret, auth_secret);
239 336
240 std::string content_encryption_key = DeriveContentEncryptionKey( 337 std::string content_encryption_key = DeriveContentEncryptionKey(
241 recipient_public_key, sender_public_key, prk, salt); 338 recipient_public_key, sender_public_key, prk, salt);
242 339
243 std::string nonce = 340 std::string nonce =
244 DeriveNonce(recipient_public_key, sender_public_key, prk, salt); 341 DeriveNonce(recipient_public_key, sender_public_key, prk, salt);
245 342
246 // The |ciphertext| must be at least of size kAuthenticationTagBytes, which 343 // The |ciphertext| must be at least of size kAuthenticationTagBytes, which
247 // is the case when an empty message with a zero padding length has been 344 // is the case when an empty message with a zero padding length has been
248 // received. The |record_size| must be large enough to use only one record. 345 // received. The |record_size| must be large enough to use only one record.
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after
352 0 /* subkey_secret_bytes_to_generate */); 449 0 /* subkey_secret_bytes_to_generate */);
353 450
354 // draft-ietf-httpbis-encryption-encoding defines that the result should 451 // draft-ietf-httpbis-encryption-encoding defines that the result should
355 // be XOR'ed with the record's sequence number, however, Web Push encryption 452 // be XOR'ed with the record's sequence number, however, Web Push encryption
356 // is limited to a single record per draft-ietf-webpush-encryption. 453 // is limited to a single record per draft-ietf-webpush-encryption.
357 454
358 return hkdf.client_write_key().as_string(); 455 return hkdf.client_write_key().as_string();
359 } 456 }
360 457
361 } // namespace gcm 458 } // namespace gcm
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698