1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "crypto/encryptor.h"
10 #include "base/logging.h"
11 #include "base/strings/string_util.h"
12 #include "base/sys_byteorder.h"
13 #include "crypto/openssl_util.h"
14 #include "crypto/symmetric_key.h"
15 #include "third_party/boringssl/src/include/openssl/aes.h"
16 #include "third_party/boringssl/src/include/openssl/evp.h"
22 const EVP_CIPHER* GetCipherForKey(const SymmetricKey* key) {
23 switch (key->key().length()) {
24 case 16: return EVP_aes_128_cbc();
25 case 32: return EVP_aes_256_cbc();
33 /////////////////////////////////////////////////////////////////////////////
34 // Encryptor Implementation.
36 Encryptor::Encryptor() : key_(nullptr), mode_(CBC) {}
38 Encryptor::~Encryptor() = default;
40 bool Encryptor::Init(const SymmetricKey* key, Mode mode, std::string_view iv) {
41 return Init(key, mode, base::as_bytes(base::make_span(iv)));
44 bool Encryptor::Init(const SymmetricKey* key,
46 base::span<const uint8_t> iv) {
48 DCHECK(mode == CBC || mode == CTR);
51 if (mode == CBC && iv.size() != AES_BLOCK_SIZE)
53 // CTR mode passes the starting counter separately, via SetCounter().
54 if (mode == CTR && !iv.empty())
57 if (GetCipherForKey(key) == nullptr)
62 iv_.assign(iv.begin(), iv.end());
66 bool Encryptor::Encrypt(std::string_view plaintext, std::string* ciphertext) {
67 return CryptString(/*do_encrypt=*/true, plaintext, ciphertext);
70 bool Encryptor::Encrypt(base::span<const uint8_t> plaintext,
71 std::vector<uint8_t>* ciphertext) {
72 return CryptBytes(/*do_encrypt=*/true, plaintext, ciphertext);
75 bool Encryptor::Decrypt(std::string_view ciphertext, std::string* plaintext) {
76 return CryptString(/*do_encrypt=*/false, ciphertext, plaintext);
79 bool Encryptor::Decrypt(base::span<const uint8_t> ciphertext,
80 std::vector<uint8_t>* plaintext) {
81 return CryptBytes(/*do_encrypt=*/false, ciphertext, plaintext);
84 bool Encryptor::SetCounter(std::string_view counter) {
85 return SetCounter(base::as_bytes(base::make_span(counter)));
88 bool Encryptor::SetCounter(base::span<const uint8_t> counter) {
91 if (counter.size() != 16u)
94 iv_.assign(counter.begin(), counter.end());
98 bool Encryptor::CryptString(bool do_encrypt,
99 std::string_view input,
100 std::string* output) {
101 std::string result(MaxOutput(do_encrypt, input.size()), '\0');
102 absl::optional<size_t> len =
104 ? CryptCTR(do_encrypt, base::as_bytes(base::make_span(input)),
105 base::as_writable_bytes(base::make_span(result)))
106 : Crypt(do_encrypt, base::as_bytes(base::make_span(input)),
107 base::as_writable_bytes(base::make_span(result)));
112 *output = std::move(result);
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()));
120 absl::optional<size_t> len = (mode_ == CTR)
121 ? CryptCTR(do_encrypt, input, result)
122 : Crypt(do_encrypt, input, result);
127 *output = std::move(result);
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
137 absl::optional<size_t> Encryptor::Crypt(bool do_encrypt,
138 base::span<const uint8_t> input,
139 base::span<uint8_t> output) {
140 DCHECK(key_); // Must call Init() before En/De-crypt.
142 const EVP_CIPHER* cipher = GetCipherForKey(key_);
143 DCHECK(cipher); // Already handled in Init();
145 const std::string& key = key_->key();
146 DCHECK_EQ(EVP_CIPHER_iv_length(cipher), iv_.size());
147 DCHECK_EQ(EVP_CIPHER_key_length(cipher), key.size());
149 OpenSSLErrStackTracer err_tracer(FROM_HERE);
150 bssl::ScopedEVP_CIPHER_CTX ctx;
151 if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr,
152 reinterpret_cast<const uint8_t*>(key.data()),
153 iv_.data(), do_encrypt)) {
154 return absl::nullopt;
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));
160 if (!EVP_CipherUpdate(ctx.get(), output.data(), &out_len, input.data(),
162 return absl::nullopt;
164 // Write out the final block plus padding (if any) to the end of the data
167 if (!EVP_CipherFinal_ex(ctx.get(), output.data() + out_len, &tail_len))
168 return absl::nullopt;
171 DCHECK_LE(out_len, static_cast<int>(output.size()));
175 absl::optional<size_t> Encryptor::CryptCTR(bool do_encrypt,
176 base::span<const uint8_t> input,
177 base::span<uint8_t> output) {
178 if (iv_.size() != AES_BLOCK_SIZE) {
179 LOG(ERROR) << "Counter value not set in CTR mode.";
180 return absl::nullopt;
184 if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key_->key().data()),
185 key_->key().size() * 8, &aes_key) != 0) {
186 return absl::nullopt;
189 uint8_t ecount_buf[AES_BLOCK_SIZE] = { 0 };
190 unsigned int block_offset = 0;
192 // |output| must have room for |input|.
193 CHECK_GE(output.size(), input.size());
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);
201 } // namespace crypto