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> |
11 #include <sstream> | 11 #include <sstream> |
12 | 12 |
13 #include "base/logging.h" | 13 #include "base/logging.h" |
14 #include "base/macros.h" | |
15 #include "base/memory/ptr_util.h" | |
14 #include "base/numerics/safe_math.h" | 16 #include "base/numerics/safe_math.h" |
15 #include "base/strings/string_util.h" | 17 #include "base/strings/string_util.h" |
16 #include "base/sys_byteorder.h" | 18 #include "base/sys_byteorder.h" |
17 #include "crypto/hkdf.h" | 19 #include "crypto/hkdf.h" |
18 #include "third_party/boringssl/src/include/openssl/aead.h" | 20 #include "third_party/boringssl/src/include/openssl/aead.h" |
19 | 21 |
20 namespace gcm { | 22 namespace gcm { |
23 | |
21 namespace { | 24 namespace { |
22 | 25 |
23 // Size, in bytes, of the nonce for a record. This must be at least the size | 26 // Size, in bytes, of the nonce for a record. This must be at least the size |
24 // of a uint64_t, which is used to indicate the record sequence number. | 27 // of a uint64_t, which is used to indicate the record sequence number. |
25 const uint64_t kNonceSize = 12; | 28 const uint64_t kNonceSize = 12; |
26 | 29 |
27 // The default record size as defined by httpbis-encryption-encoding-06. | 30 // The default record size as defined by httpbis-encryption-encoding-06. |
28 const size_t kDefaultRecordSize = 4096; | 31 const size_t kDefaultRecordSize = 4096; |
29 | 32 |
30 // Key size, in bytes, of a valid AEAD_AES_128_GCM key. | 33 // Key size, in bytes, of a valid AEAD_AES_128_GCM key. |
31 const size_t kContentEncryptionKeySize = 16; | 34 const size_t kContentEncryptionKeySize = 16; |
32 | 35 |
33 // The BoringSSL functions used to seal (encrypt) and open (decrypt) a payload | 36 // The BoringSSL functions used to seal (encrypt) and open (decrypt) a payload |
34 // follow the same prototype, declared as follows. | 37 // follow the same prototype, declared as follows. |
35 using EVP_AEAD_CTX_TransformFunction = | 38 using EVP_AEAD_CTX_TransformFunction = |
36 int(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, | 39 int(const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, |
37 size_t max_out_len, const uint8_t *nonce, size_t nonce_len, | 40 size_t max_out_len, const uint8_t *nonce, size_t nonce_len, |
38 const uint8_t *in, size_t in_len, const uint8_t *ad, size_t ad_len); | 41 const uint8_t *in, size_t in_len, const uint8_t *ad, size_t ad_len); |
39 | 42 |
40 // Creates the info parameter for an HKDF value for the given |content_encoding| | 43 // Implementation of draft 03 of the Web Push Encryption standard: |
41 // in accordance with draft-thomson-http-encryption. | 44 // https://tools.ietf.org/html/draft-ietf-webpush-encryption-03 |
42 // | 45 // https://tools.ietf.org/html/draft-ietf-httpbis-encryption-encoding-02 |
43 // cek_info = "Content-Encoding: aesgcm" || 0x00 || context | 46 class WebPushEncryptionDraft03 |
44 // nonce_info = "Content-Encoding: nonce" || 0x00 || context | 47 : public GCMMessageCryptographer::EncryptionScheme { |
45 // | 48 public: |
46 // context = "P-256" || 0x00 || | 49 WebPushEncryptionDraft03() = default; |
47 // length(recipient_public) || recipient_public || | 50 ~WebPushEncryptionDraft03() override = default; |
48 // length(sender_public) || sender_public | |
49 // | |
50 // The length of the public keys must be written as a two octet unsigned integer | |
51 // in network byte order (big endian). | |
52 std::string InfoForContentEncoding( | |
53 const char* content_encoding, | |
54 const base::StringPiece& recipient_public_key, | |
55 const base::StringPiece& sender_public_key) { | |
56 DCHECK_EQ(recipient_public_key.size(), 65u); | |
57 DCHECK_EQ(sender_public_key.size(), 65u); | |
58 | 51 |
59 std::stringstream info_stream; | 52 // GCMMessageCryptographer::EncryptionScheme implementation. |
60 info_stream << "Content-Encoding: " << content_encoding << '\x00'; | 53 std::string DerivePseudoRandomKey( |
61 info_stream << "P-256" << '\x00'; | 54 const base::StringPiece& ecdh_shared_secret, |
55 const base::StringPiece& auth_secret) override { | |
56 std::stringstream info_stream; | |
57 info_stream << "Content-Encoding: auth" << '\x00'; | |
62 | 58 |
63 uint16_t local_len = | 59 crypto::HKDF hkdf(ecdh_shared_secret, auth_secret, info_stream.str(), |
64 base::HostToNet16(static_cast<uint16_t>(recipient_public_key.size())); | 60 32, /* key_bytes_to_generate */ |
65 info_stream.write(reinterpret_cast<char*>(&local_len), sizeof(local_len)); | 61 0, /* iv_bytes_to_generate */ |
66 info_stream << recipient_public_key; | 62 0 /* subkey_secret_bytes_to_generate */); |
67 | 63 |
68 uint16_t peer_len = | 64 return hkdf.client_write_key().as_string(); |
69 base::HostToNet16(static_cast<uint16_t>(sender_public_key.size())); | 65 } |
70 info_stream.write(reinterpret_cast<char*>(&peer_len), sizeof(peer_len)); | |
71 info_stream << sender_public_key; | |
72 | 66 |
73 return info_stream.str(); | 67 // Creates the info parameter for an HKDF value for the given |
74 } | 68 // |content_encoding| in accordance with draft-ietf-webpush-encryption-03. |
69 // | |
70 // cek_info = "Content-Encoding: aesgcm" || 0x00 || context | |
71 // nonce_info = "Content-Encoding: nonce" || 0x00 || context | |
72 // | |
73 // context = "P-256" || 0x00 || | |
74 // length(recipient_public) || recipient_public || | |
75 // length(sender_public) || sender_public | |
76 // | |
77 // The length of the public keys must be written as a two octet unsigned | |
78 // integer in network byte order (big endian). | |
79 std::string GenerateInfoForContentEncoding( | |
80 EncodingType type, | |
81 const base::StringPiece& recipient_public_key, | |
82 const base::StringPiece& sender_public_key) override { | |
83 std::stringstream info_stream; | |
84 info_stream << "Content-Encoding: "; | |
85 | |
86 switch (type) { | |
87 case EncodingType::CONTENT_ENCRYPTION_KEY: | |
88 info_stream << "aesgcm"; | |
89 break; | |
90 case EncodingType::NONCE: | |
91 info_stream << "nonce"; | |
92 break; | |
93 } | |
94 | |
95 info_stream << '\x00' << "P-256" << '\x00'; | |
96 | |
97 uint16_t local_len = | |
98 base::HostToNet16(static_cast<uint16_t>(recipient_public_key.size())); | |
99 info_stream.write(reinterpret_cast<char*>(&local_len), sizeof(local_len)); | |
100 info_stream << recipient_public_key; | |
101 | |
102 uint16_t peer_len = | |
103 base::HostToNet16(static_cast<uint16_t>(sender_public_key.size())); | |
104 info_stream.write(reinterpret_cast<char*>(&peer_len), sizeof(peer_len)); | |
105 info_stream << sender_public_key; | |
106 | |
107 return info_stream.str(); | |
108 } | |
109 | |
110 // draft-ietf-webpush-encryption-03 defines that the padding is included at | |
111 // the beginning of the message. The first two bytes, in network byte order, | |
112 // contain the length of the included padding. Then that exact number of bytes | |
113 // must follow as padding, all of which must have a zero value. | |
114 // | |
115 // TODO(peter): Add support for message padding if the GCMMessageCryptographer | |
116 // starts encrypting payloads for reasons other than testing. | |
117 std::string CreateRecord(const base::StringPiece& plaintext) override { | |
118 std::string record; | |
119 record.reserve(sizeof(uint16_t) + plaintext.size()); | |
120 record.append(sizeof(uint16_t), '\x00'); | |
121 | |
122 plaintext.AppendToString(&record); | |
123 return record; | |
124 } | |
125 | |
126 // The record padding in draft-ietf-webpush-encryption-03 is included at the | |
127 // beginning of the record. The first two bytes indicate the length of the | |
128 // padding. All padding bytes immediately follow, and must be set to zero. | |
129 bool ValidateAndRemovePadding(base::StringPiece& record) override { | |
130 // Records must be at least two octets in size (to hold the padding). | |
131 // Records that are smaller, i.e. a single octet, are invalid. | |
132 if (record.size() < sizeof(uint16_t)) | |
133 return false; | |
134 | |
135 // Records contain a two-byte, big-endian padding length followed by zero to | |
136 // 65535 bytes of padding. Padding bytes must be zero but, since AES-GCM | |
137 // authenticates the plaintext, checking and removing padding need not be | |
138 // done in constant-time. | |
139 uint16_t padding_length = (static_cast<uint8_t>(record[0]) << 8) | | |
140 static_cast<uint8_t>(record[1]); | |
141 record.remove_prefix(sizeof(uint16_t)); | |
142 | |
143 if (padding_length > record.size()) { | |
144 return false; | |
145 } | |
146 | |
147 for (size_t i = 0; i < padding_length; ++i) { | |
148 if (record[i] != 0) | |
149 return false; | |
150 } | |
151 | |
152 record.remove_prefix(padding_length); | |
153 return true; | |
154 } | |
155 | |
156 private: | |
157 DISALLOW_COPY_AND_ASSIGN(WebPushEncryptionDraft03); | |
158 }; | |
75 | 159 |
76 } // namespace | 160 } // namespace |
77 | 161 |
78 const size_t GCMMessageCryptographer::kAuthenticationTagBytes = 16; | 162 const size_t GCMMessageCryptographer::kAuthenticationTagBytes = 16; |
79 const size_t GCMMessageCryptographer::kSaltSize = 16; | 163 const size_t GCMMessageCryptographer::kSaltSize = 16; |
80 | 164 |
81 GCMMessageCryptographer::GCMMessageCryptographer( | 165 GCMMessageCryptographer::GCMMessageCryptographer(Version version) { |
166 switch (version) { | |
martijnc
2017/03/28 16:26:36
nit: I think
switch (version) {
case Version::D
Peter Beverloo
2017/04/07 17:48:21
Not having a `default` clause gives us compile-tim
| |
167 case Version::DRAFT_03: | |
168 encryption_scheme_ = base::MakeUnique<WebPushEncryptionDraft03>(); | |
169 return; | |
170 } | |
171 | |
172 NOTREACHED(); | |
173 } | |
174 | |
175 GCMMessageCryptographer::~GCMMessageCryptographer() = default; | |
176 | |
177 bool GCMMessageCryptographer::Encrypt( | |
82 const base::StringPiece& recipient_public_key, | 178 const base::StringPiece& recipient_public_key, |
83 const base::StringPiece& sender_public_key, | 179 const base::StringPiece& sender_public_key, |
84 const std::string& auth_secret) | 180 const base::StringPiece& ecdh_shared_secret, |
85 : content_encryption_key_info_( | 181 const base::StringPiece& auth_secret, |
86 InfoForContentEncoding("aesgcm", recipient_public_key, | 182 const base::StringPiece& salt, |
87 sender_public_key)), | 183 const base::StringPiece& plaintext, |
88 nonce_info_( | 184 size_t* record_size, |
89 InfoForContentEncoding("nonce", recipient_public_key, | 185 std::string* ciphertext) const { |
90 sender_public_key)), | 186 DCHECK_EQ(recipient_public_key.size(), 65u); |
91 auth_secret_(auth_secret) { | 187 DCHECK_EQ(sender_public_key.size(), 65u); |
92 } | 188 DCHECK(record_size); |
189 DCHECK(ciphertext); | |
93 | 190 |
94 GCMMessageCryptographer::~GCMMessageCryptographer() {} | 191 // TODO(peter): DCHECK the lengths of |ecdh_shared_secret|, |auth_secret| and |
95 | 192 // |salt|. |
96 bool GCMMessageCryptographer::Encrypt(const base::StringPiece& plaintext, | |
97 const base::StringPiece& ikm, | |
98 const base::StringPiece& salt, | |
99 size_t* record_size, | |
100 std::string* ciphertext) const { | |
101 DCHECK(ciphertext); | |
102 DCHECK(record_size); | |
103 | 193 |
104 if (salt.size() != kSaltSize) | 194 if (salt.size() != kSaltSize) |
105 return false; | 195 return false; |
106 | 196 |
107 std::string prk = DerivePseudoRandomKey(ikm); | 197 std::string prk = encryption_scheme_->DerivePseudoRandomKey( |
198 ecdh_shared_secret, auth_secret); | |
108 | 199 |
109 std::string content_encryption_key = DeriveContentEncryptionKey(prk, salt); | 200 std::string content_encryption_key = DeriveContentEncryptionKey( |
110 std::string nonce = DeriveNonce(prk, salt); | 201 recipient_public_key, sender_public_key, prk, salt); |
202 std::string nonce = | |
203 DeriveNonce(recipient_public_key, sender_public_key, prk, salt); | |
111 | 204 |
112 // Prior to the plaintext, draft-thomson-http-encryption has a two-byte | 205 std::string record = encryption_scheme_->CreateRecord(plaintext); |
113 // padding length followed by zero to 65535 bytes of padding. There is no need | 206 std::string encrypted_record; |
114 // for payloads created by Chrome to be padded so the padding length is set to | |
115 // zero. | |
116 std::string record; | |
117 record.reserve(sizeof(uint16_t) + plaintext.size()); | |
118 record.append(sizeof(uint16_t), '\0'); | |
119 | 207 |
120 plaintext.AppendToString(&record); | 208 if (!TransformRecord(Direction::ENCRYPT, record, content_encryption_key, |
121 | 209 nonce, &encrypted_record)) { |
122 std::string encrypted_record; | |
123 if (!EncryptDecryptRecordInternal(ENCRYPT, record, content_encryption_key, | |
124 nonce, &encrypted_record)) { | |
125 return false; | 210 return false; |
126 } | 211 } |
127 | 212 |
128 // The advertised record size must be at least one more than the padded | 213 // The advertised record size must be at least one more than the padded |
129 // plaintext to ensure only one record. | 214 // plaintext to ensure only one record. |
130 *record_size = std::max(kDefaultRecordSize, record.size() + 1); | 215 *record_size = std::max(kDefaultRecordSize, record.size() + 1); |
131 | 216 |
132 ciphertext->swap(encrypted_record); | 217 ciphertext->swap(encrypted_record); |
133 return true; | 218 return true; |
134 } | 219 } |
135 | 220 |
136 bool GCMMessageCryptographer::Decrypt(const base::StringPiece& ciphertext, | 221 bool GCMMessageCryptographer::Decrypt( |
137 const base::StringPiece& ikm, | 222 const base::StringPiece& recipient_public_key, |
138 const base::StringPiece& salt, | 223 const base::StringPiece& sender_public_key, |
139 size_t record_size, | 224 const base::StringPiece& ecdh_shared_secret, |
140 std::string* plaintext) const { | 225 const base::StringPiece& auth_secret, |
226 const base::StringPiece& salt, | |
227 const base::StringPiece& ciphertext, | |
228 size_t record_size, | |
229 std::string* plaintext) const { | |
230 DCHECK_EQ(recipient_public_key.size(), 65u); | |
231 DCHECK_EQ(sender_public_key.size(), 65u); | |
141 DCHECK(plaintext); | 232 DCHECK(plaintext); |
142 | 233 |
143 if (salt.size() != kSaltSize || record_size <= 1) | 234 // TODO(peter): DCHECK the lengths of |ecdh_shared_secret|, |auth_secret| and |
235 // |salt|. | |
236 | |
237 if (record_size <= 1) | |
144 return false; | 238 return false; |
145 | 239 |
146 // The |ciphertext| must be at least of size kAuthenticationTagBytes plus | 240 std::string prk = encryption_scheme_->DerivePseudoRandomKey( |
147 // len(uint16) to hold the message's padding length, which is the case when an | 241 ecdh_shared_secret, auth_secret); |
148 // empty message with a zero padding length has been received. Per | 242 |
149 // https://tools.ietf.org/html/draft-thomson-http-encryption-02#section-3, the | 243 std::string content_encryption_key = DeriveContentEncryptionKey( |
150 // |record_size| parameter must be large enough to use only one record. | 244 recipient_public_key, sender_public_key, prk, salt); |
151 if (ciphertext.size() < sizeof(uint16_t) + kAuthenticationTagBytes || | 245 |
246 std::string nonce = | |
247 DeriveNonce(recipient_public_key, sender_public_key, prk, salt); | |
248 | |
249 // The |ciphertext| must be at least of size kAuthenticationTagBytes, which | |
250 // is the case when an empty message with a zero padding length has been | |
251 // received. The |record_size| must be large enough to use only one record. | |
252 // https://tools.ietf.org/html/draft-ietf-httpbis-encryption-encoding-03#secti on-2 | |
253 if (ciphertext.size() < kAuthenticationTagBytes || | |
152 ciphertext.size() > record_size + kAuthenticationTagBytes) { | 254 ciphertext.size() > record_size + kAuthenticationTagBytes) { |
153 return false; | 255 return false; |
154 } | 256 } |
155 | 257 |
156 std::string prk = DerivePseudoRandomKey(ikm); | |
157 | |
158 std::string content_encryption_key = DeriveContentEncryptionKey(prk, salt); | |
159 std::string nonce = DeriveNonce(prk, salt); | |
160 | |
161 std::string decrypted_record_string; | 258 std::string decrypted_record_string; |
162 if (!EncryptDecryptRecordInternal(DECRYPT, ciphertext, content_encryption_key, | 259 if (!TransformRecord(Direction::DECRYPT, ciphertext, content_encryption_key, |
163 nonce, &decrypted_record_string)) { | 260 nonce, &decrypted_record_string)) { |
164 return false; | 261 return false; |
165 } | 262 } |
166 | 263 |
167 DCHECK(!decrypted_record_string.empty()); | 264 DCHECK(!decrypted_record_string.empty()); |
168 | 265 |
169 base::StringPiece decrypted_record(decrypted_record_string); | 266 base::StringPiece decrypted_record(decrypted_record_string); |
170 | 267 if (!encryption_scheme_->ValidateAndRemovePadding(decrypted_record)) |
171 // Records must be at least two octets in size (to hold the padding). Records | |
172 // that are smaller, i.e. a single octet, are invalid. | |
173 if (decrypted_record.size() < sizeof(uint16_t)) | |
174 return false; | 268 return false; |
175 | 269 |
176 // Records contain a two-byte, big-endian padding length followed by zero to | |
177 // 65535 bytes of padding. Padding bytes must be zero but, since AES-GCM | |
178 // authenticates the plaintext, checking and removing padding need not be done | |
179 // in constant-time. | |
180 uint16_t padding_length = (static_cast<uint8_t>(decrypted_record[0]) << 8) | | |
181 static_cast<uint8_t>(decrypted_record[1]); | |
182 decrypted_record.remove_prefix(sizeof(uint16_t)); | |
183 | |
184 if (padding_length > decrypted_record.size()) { | |
185 return false; | |
186 } | |
187 | |
188 for (size_t i = 0; i < padding_length; ++i) { | |
189 if (decrypted_record[i] != 0) | |
190 return false; | |
191 } | |
192 | |
193 decrypted_record.remove_prefix(padding_length); | |
194 decrypted_record.CopyToString(plaintext); | 270 decrypted_record.CopyToString(plaintext); |
195 return true; | 271 return true; |
196 } | 272 } |
197 | 273 |
198 bool GCMMessageCryptographer::EncryptDecryptRecordInternal( | 274 bool GCMMessageCryptographer::TransformRecord(Direction direction, |
199 Mode mode, | 275 const base::StringPiece& input, |
200 const base::StringPiece& input, | 276 const base::StringPiece& key, |
201 const base::StringPiece& key, | 277 const base::StringPiece& nonce, |
202 const base::StringPiece& nonce, | 278 std::string* output) const { |
203 std::string* output) const { | |
204 DCHECK(output); | 279 DCHECK(output); |
205 | 280 |
206 const EVP_AEAD* aead = EVP_aead_aes_128_gcm(); | 281 const EVP_AEAD* aead = EVP_aead_aes_128_gcm(); |
207 | 282 |
208 EVP_AEAD_CTX context; | 283 EVP_AEAD_CTX context; |
209 if (!EVP_AEAD_CTX_init(&context, aead, | 284 if (!EVP_AEAD_CTX_init(&context, aead, |
210 reinterpret_cast<const uint8_t*>(key.data()), | 285 reinterpret_cast<const uint8_t*>(key.data()), |
211 key.size(), EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr)) { | 286 key.size(), EVP_AEAD_DEFAULT_TAG_LENGTH, nullptr)) { |
212 return false; | 287 return false; |
213 } | 288 } |
214 | 289 |
215 base::CheckedNumeric<size_t> maximum_output_length(input.size()); | 290 base::CheckedNumeric<size_t> maximum_output_length(input.size()); |
216 if (mode == ENCRYPT) | 291 if (direction == Direction::ENCRYPT) |
217 maximum_output_length += kAuthenticationTagBytes; | 292 maximum_output_length += kAuthenticationTagBytes; |
218 | 293 |
219 // WriteInto requires the buffer to finish with a NULL-byte. | 294 // WriteInto requires the buffer to finish with a NULL-byte. |
220 maximum_output_length += 1; | 295 maximum_output_length += 1; |
221 | 296 |
222 size_t output_length = 0; | 297 size_t output_length = 0; |
223 uint8_t* raw_output = reinterpret_cast<uint8_t*>( | 298 uint8_t* raw_output = reinterpret_cast<uint8_t*>( |
224 base::WriteInto(output, maximum_output_length.ValueOrDie())); | 299 base::WriteInto(output, maximum_output_length.ValueOrDie())); |
225 | 300 |
226 EVP_AEAD_CTX_TransformFunction* transform_function = | 301 EVP_AEAD_CTX_TransformFunction* transform_function = |
227 mode == ENCRYPT ? EVP_AEAD_CTX_seal : EVP_AEAD_CTX_open; | 302 direction == Direction::ENCRYPT ? EVP_AEAD_CTX_seal : EVP_AEAD_CTX_open; |
228 | 303 |
229 if (!transform_function( | 304 if (!transform_function( |
230 &context, raw_output, &output_length, output->size(), | 305 &context, raw_output, &output_length, output->size(), |
231 reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(), | 306 reinterpret_cast<const uint8_t*>(nonce.data()), nonce.size(), |
232 reinterpret_cast<const uint8_t*>(input.data()), input.size(), | 307 reinterpret_cast<const uint8_t*>(input.data()), input.size(), |
233 nullptr, 0)) { | 308 nullptr, 0)) { |
234 EVP_AEAD_CTX_cleanup(&context); | 309 EVP_AEAD_CTX_cleanup(&context); |
235 return false; | 310 return false; |
236 } | 311 } |
237 | 312 |
238 EVP_AEAD_CTX_cleanup(&context); | 313 EVP_AEAD_CTX_cleanup(&context); |
239 | 314 |
240 base::CheckedNumeric<size_t> expected_output_length(input.size()); | 315 base::CheckedNumeric<size_t> expected_output_length(input.size()); |
241 if (mode == ENCRYPT) | 316 if (direction == Direction::ENCRYPT) |
242 expected_output_length += kAuthenticationTagBytes; | 317 expected_output_length += kAuthenticationTagBytes; |
243 else | 318 else |
244 expected_output_length -= kAuthenticationTagBytes; | 319 expected_output_length -= kAuthenticationTagBytes; |
245 | 320 |
246 DCHECK_EQ(expected_output_length.ValueOrDie(), output_length); | 321 DCHECK_EQ(expected_output_length.ValueOrDie(), output_length); |
247 | 322 |
248 output->resize(output_length); | 323 output->resize(output_length); |
249 return true; | 324 return true; |
250 } | 325 } |
251 | 326 |
252 std::string GCMMessageCryptographer::DerivePseudoRandomKey( | 327 std::string GCMMessageCryptographer::DeriveContentEncryptionKey( |
253 const base::StringPiece& ikm) const { | 328 const base::StringPiece& recipient_public_key, |
254 if (allow_empty_auth_secret_for_tests_ && auth_secret_.empty()) | 329 const base::StringPiece& sender_public_key, |
255 return ikm.as_string(); | 330 const base::StringPiece& ecdh_shared_secret, |
331 const base::StringPiece& salt) const { | |
332 std::string content_encryption_key_info = | |
333 encryption_scheme_->GenerateInfoForContentEncoding( | |
334 EncryptionScheme::EncodingType::CONTENT_ENCRYPTION_KEY, | |
335 recipient_public_key, sender_public_key); | |
256 | 336 |
257 CHECK(!auth_secret_.empty()); | 337 crypto::HKDF hkdf(ecdh_shared_secret, salt, content_encryption_key_info, |
258 | 338 kContentEncryptionKeySize, 0, /* iv_bytes_to_generate */ |
259 std::stringstream info_stream; | 339 0 /* subkey_secret_bytes_to_generate */); |
260 info_stream << "Content-Encoding: auth" << '\x00'; | |
261 | |
262 crypto::HKDF hkdf(ikm, auth_secret_, | |
263 info_stream.str(), | |
264 32, /* key_bytes_to_generate */ | |
265 0, /* iv_bytes_to_generate */ | |
266 0 /* subkey_secret_bytes_to_generate */); | |
267 | |
268 return hkdf.client_write_key().as_string(); | |
269 } | |
270 | |
271 std::string GCMMessageCryptographer::DeriveContentEncryptionKey( | |
272 const base::StringPiece& prk, | |
273 const base::StringPiece& salt) const { | |
274 crypto::HKDF hkdf(prk, salt, | |
275 content_encryption_key_info_, | |
276 kContentEncryptionKeySize, | |
277 0, /* iv_bytes_to_generate */ | |
278 0 /* subkey_secret_bytes_to_generate */); | |
279 | 340 |
280 return hkdf.client_write_key().as_string(); | 341 return hkdf.client_write_key().as_string(); |
281 } | 342 } |
282 | 343 |
283 std::string GCMMessageCryptographer::DeriveNonce( | 344 std::string GCMMessageCryptographer::DeriveNonce( |
284 const base::StringPiece& prk, | 345 const base::StringPiece& recipient_public_key, |
346 const base::StringPiece& sender_public_key, | |
347 const base::StringPiece& ecdh_shared_secret, | |
285 const base::StringPiece& salt) const { | 348 const base::StringPiece& salt) const { |
286 crypto::HKDF hkdf(prk, salt, | 349 std::string nonce_info = encryption_scheme_->GenerateInfoForContentEncoding( |
287 nonce_info_, | 350 EncryptionScheme::EncodingType::NONCE, recipient_public_key, |
288 kNonceSize, | 351 sender_public_key); |
289 0, /* iv_bytes_to_generate */ | |
290 0 /* subkey_secret_bytes_to_generate */); | |
291 | 352 |
292 // draft-thomson-http-encryption defines that the result should be XOR'ed with | 353 crypto::HKDF hkdf(ecdh_shared_secret, salt, nonce_info, kNonceSize, |
293 // the record's sequence number, however, Web Push encryption is limited to a | 354 0, /* iv_bytes_to_generate */ |
294 // single record per draft-ietf-webpush-encryption. | 355 0 /* subkey_secret_bytes_to_generate */); |
356 | |
357 // draft-ietf-httpbis-encryption-encoding defines that the result should | |
358 // be XOR'ed with the record's sequence number, however, Web Push encryption | |
359 // is limited to a single record per draft-ietf-webpush-encryption. | |
295 | 360 |
296 return hkdf.client_write_key().as_string(); | 361 return hkdf.client_write_key().as_string(); |
297 } | 362 } |
298 | 363 |
299 } // namespace gcm | 364 } // namespace gcm |
OLD | NEW |