OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |