[email protected] | 9eb7b11b | 2012-03-28 20:19:31 | [diff] [blame] | 1 | // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 2 | // Use of this source code is governed by a BSD-style license that can be |
| 3 | // found in the LICENSE file. |
| 4 | |
| 5 | #include "crypto/encryptor.h" |
| 6 | |
avi | dd373b8b | 2015-12-21 21:34:43 | [diff] [blame] | 7 | #include <stddef.h> |
| 8 | #include <stdint.h> |
| 9 | |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 10 | #include "base/logging.h" |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 11 | #include "base/strings/string_util.h" |
[email protected] | 0e6f619 | 2011-12-28 23:18:21 | [diff] [blame] | 12 | #include "base/sys_byteorder.h" |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 13 | #include "crypto/openssl_util.h" |
| 14 | #include "crypto/symmetric_key.h" |
tfarina | 29a3a174 | 2016-10-28 18:47:33 | [diff] [blame] | 15 | #include "third_party/boringssl/src/include/openssl/aes.h" |
| 16 | #include "third_party/boringssl/src/include/openssl/evp.h" |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 17 | |
| 18 | namespace crypto { |
| 19 | |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 20 | namespace { |
| 21 | |
Chris Mumford | ea3b6c19 | 2017-06-09 18:33:13 | [diff] [blame] | 22 | const EVP_CIPHER* GetCipherForKey(const SymmetricKey* key) { |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 23 | switch (key->key().length()) { |
| 24 | case 16: return EVP_aes_128_cbc(); |
| 25 | case 32: return EVP_aes_256_cbc(); |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 26 | default: |
| 27 | return nullptr; |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 28 | } |
| 29 | } |
| 30 | |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 31 | } // namespace |
| 32 | |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 33 | ///////////////////////////////////////////////////////////////////////////// |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 34 | // Encryptor Implementation. |
| 35 | |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 36 | Encryptor::Encryptor() : key_(nullptr), mode_(CBC) {} |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 37 | |
Chris Watkins | a850a30 | 2017-11-30 03:53:49 | [diff] [blame] | 38 | Encryptor::~Encryptor() = default; |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 39 | |
David Benjamin | cda45eb | 2017-11-06 18:16:52 | [diff] [blame] | 40 | bool Encryptor::Init(const SymmetricKey* key, Mode mode, base::StringPiece iv) { |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 41 | return Init(key, mode, base::as_bytes(base::make_span(iv))); |
| 42 | } |
| 43 | |
| 44 | bool Encryptor::Init(const SymmetricKey* key, |
| 45 | Mode mode, |
| 46 | base::span<const uint8_t> iv) { |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 47 | DCHECK(key); |
| 48 | DCHECK(mode == CBC || mode == CTR); |
| 49 | |
| 50 | EnsureOpenSSLInit(); |
| 51 | if (mode == CBC && iv.size() != AES_BLOCK_SIZE) |
| 52 | return false; |
David Benjamin | 47feaaff | 2020-06-16 22:54:46 | [diff] [blame] | 53 | // CTR mode passes the starting counter separately, via SetCounter(). |
| 54 | if (mode == CTR && !iv.empty()) |
| 55 | return false; |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 56 | |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 57 | if (GetCipherForKey(key) == nullptr) |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 58 | return false; |
| 59 | |
| 60 | key_ = key; |
| 61 | mode_ = mode; |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 62 | iv_.assign(iv.begin(), iv.end()); |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 63 | return true; |
| 64 | } |
| 65 | |
David Benjamin | cda45eb | 2017-11-06 18:16:52 | [diff] [blame] | 66 | bool Encryptor::Encrypt(base::StringPiece plaintext, std::string* ciphertext) { |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 67 | return CryptString(/*do_encrypt=*/true, plaintext, ciphertext); |
| 68 | } |
| 69 | |
| 70 | bool Encryptor::Encrypt(base::span<const uint8_t> plaintext, |
| 71 | std::vector<uint8_t>* ciphertext) { |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 72 | return CryptBytes(/*do_encrypt=*/true, plaintext, ciphertext); |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 73 | } |
| 74 | |
David Benjamin | cda45eb | 2017-11-06 18:16:52 | [diff] [blame] | 75 | bool Encryptor::Decrypt(base::StringPiece ciphertext, std::string* plaintext) { |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 76 | return CryptString(/*do_encrypt=*/false, ciphertext, plaintext); |
| 77 | } |
| 78 | |
| 79 | bool Encryptor::Decrypt(base::span<const uint8_t> ciphertext, |
| 80 | std::vector<uint8_t>* plaintext) { |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 81 | return CryptBytes(/*do_encrypt=*/false, ciphertext, plaintext); |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 82 | } |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 83 | |
David Benjamin | cda45eb | 2017-11-06 18:16:52 | [diff] [blame] | 84 | bool Encryptor::SetCounter(base::StringPiece counter) { |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 85 | return SetCounter(base::as_bytes(base::make_span(counter))); |
| 86 | } |
| 87 | |
| 88 | bool Encryptor::SetCounter(base::span<const uint8_t> counter) { |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 89 | if (mode_ != CTR) |
| 90 | return false; |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 91 | if (counter.size() != 16u) |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 92 | return false; |
| 93 | |
David Benjamin | 47feaaff | 2020-06-16 22:54:46 | [diff] [blame] | 94 | iv_.assign(counter.begin(), counter.end()); |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 95 | return true; |
| 96 | } |
| 97 | |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 98 | bool Encryptor::CryptString(bool do_encrypt, |
| 99 | base::StringPiece input, |
| 100 | std::string* output) { |
David Benjamin | a11a432 | 2022-06-07 15:50:51 | [diff] [blame] | 101 | std::string result(MaxOutput(do_encrypt, input.size()), '\0'); |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 102 | absl::optional<size_t> len = |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 103 | (mode_ == CTR) |
| 104 | ? CryptCTR(do_encrypt, base::as_bytes(base::make_span(input)), |
David Benjamin | a11a432 | 2022-06-07 15:50:51 | [diff] [blame] | 105 | base::as_writable_bytes(base::make_span(result))) |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 106 | : Crypt(do_encrypt, base::as_bytes(base::make_span(input)), |
David Benjamin | a11a432 | 2022-06-07 15:50:51 | [diff] [blame] | 107 | base::as_writable_bytes(base::make_span(result))); |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 108 | if (!len) |
| 109 | return false; |
| 110 | |
| 111 | result.resize(*len); |
| 112 | *output = std::move(result); |
| 113 | return true; |
| 114 | } |
| 115 | |
| 116 | bool Encryptor::CryptBytes(bool do_encrypt, |
| 117 | base::span<const uint8_t> input, |
| 118 | std::vector<uint8_t>* output) { |
| 119 | std::vector<uint8_t> result(MaxOutput(do_encrypt, input.size())); |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 120 | absl::optional<size_t> len = (mode_ == CTR) |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 121 | ? CryptCTR(do_encrypt, input, result) |
| 122 | : Crypt(do_encrypt, input, result); |
| 123 | if (!len) |
| 124 | return false; |
| 125 | |
| 126 | result.resize(*len); |
| 127 | *output = std::move(result); |
| 128 | return true; |
| 129 | } |
| 130 | |
| 131 | size_t Encryptor::MaxOutput(bool do_encrypt, size_t length) { |
| 132 | size_t result = length + ((do_encrypt && mode_ == CBC) ? 16 : 0); |
| 133 | CHECK_GE(result, length); // Overflow |
| 134 | return result; |
| 135 | } |
| 136 | |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 137 | absl::optional<size_t> Encryptor::Crypt(bool do_encrypt, |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 138 | base::span<const uint8_t> input, |
| 139 | base::span<uint8_t> output) { |
| 140 | DCHECK(key_); // Must call Init() before En/De-crypt. |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 141 | |
| 142 | const EVP_CIPHER* cipher = GetCipherForKey(key_); |
| 143 | DCHECK(cipher); // Already handled in Init(); |
| 144 | |
| 145 | const std::string& key = key_->key(); |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 146 | DCHECK_EQ(EVP_CIPHER_iv_length(cipher), iv_.size()); |
| 147 | DCHECK_EQ(EVP_CIPHER_key_length(cipher), key.size()); |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 148 | |
David Benjamin | 47feaaff | 2020-06-16 22:54:46 | [diff] [blame] | 149 | OpenSSLErrStackTracer err_tracer(FROM_HERE); |
| 150 | bssl::ScopedEVP_CIPHER_CTX ctx; |
rsleevi | ffe5a13 | 2016-06-28 01:51:52 | [diff] [blame] | 151 | if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr, |
| 152 | reinterpret_cast<const uint8_t*>(key.data()), |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 153 | iv_.data(), do_encrypt)) { |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 154 | return absl::nullopt; |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 155 | } |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 156 | |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 157 | // Encrypting needs a block size of space to allow for any padding. |
| 158 | CHECK_GE(output.size(), input.size() + (do_encrypt ? iv_.size() : 0)); |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 159 | int out_len; |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 160 | if (!EVP_CipherUpdate(ctx.get(), output.data(), &out_len, input.data(), |
| 161 | input.size())) |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 162 | return absl::nullopt; |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 163 | |
| 164 | // Write out the final block plus padding (if any) to the end of the data |
| 165 | // just written. |
| 166 | int tail_len; |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 167 | if (!EVP_CipherFinal_ex(ctx.get(), output.data() + out_len, &tail_len)) |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 168 | return absl::nullopt; |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 169 | |
| 170 | out_len += tail_len; |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 171 | DCHECK_LE(out_len, static_cast<int>(output.size())); |
| 172 | return out_len; |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 173 | } |
| 174 | |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 175 | absl::optional<size_t> Encryptor::CryptCTR(bool do_encrypt, |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 176 | base::span<const uint8_t> input, |
| 177 | base::span<uint8_t> output) { |
David Benjamin | 47feaaff | 2020-06-16 22:54:46 | [diff] [blame] | 178 | if (iv_.size() != AES_BLOCK_SIZE) { |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 179 | LOG(ERROR) << "Counter value not set in CTR mode."; |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 180 | return absl::nullopt; |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | AES_KEY aes_key; |
| 184 | if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key_->key().data()), |
| 185 | key_->key().size() * 8, &aes_key) != 0) { |
Anton Bikineev | a3f961db | 2021-05-15 17:56:12 | [diff] [blame] | 186 | return absl::nullopt; |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 187 | } |
| 188 | |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 189 | uint8_t ecount_buf[AES_BLOCK_SIZE] = { 0 }; |
| 190 | unsigned int block_offset = 0; |
| 191 | |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 192 | // |output| must have room for |input|. |
| 193 | CHECK_GE(output.size(), input.size()); |
David Benjamin | 47feaaff | 2020-06-16 22:54:46 | [diff] [blame] | 194 | // Note AES_ctr128_encrypt() will update |iv_|. However, this method discards |
| 195 | // |ecount_buf| and |block_offset|, so this is not quite a streaming API. |
| 196 | AES_ctr128_encrypt(input.data(), output.data(), input.size(), &aes_key, |
| 197 | iv_.data(), ecount_buf, &block_offset); |
David Benjamin | 3efdcb7 | 2020-06-16 22:33:09 | [diff] [blame] | 198 | return input.size(); |
svaldez | 22de42fe | 2016-04-21 19:42:22 | [diff] [blame] | 199 | } |
| 200 | |
[email protected] | 2377cdee | 2011-06-24 20:46:06 | [diff] [blame] | 201 | } // namespace crypto |