 Chromium Code Reviews
 Chromium Code Reviews Issue 2713673002:
  Separate out the scheme from the GCMMessageCryptographer  (Closed)
    
  
    Issue 2713673002:
  Separate out the scheme from the GCMMessageCryptographer  (Closed) 
  | 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 |