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.
4
5 #ifdef UNSAFE_BUFFERS_BUILD
6 // TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
7 #pragma allow_unsafe_buffers
8 #endif
9
10 #include "crypto/encryptor.h"
11
12 #include <stddef.h>
13 #include <stdint.h>
14
15 #include "base/logging.h"
16 #include "base/strings/string_util.h"
17 #include "crypto/openssl_util.h"
18 #include "crypto/symmetric_key.h"
19 #include "third_party/boringssl/src/include/openssl/aes.h"
20 #include "third_party/boringssl/src/include/openssl/evp.h"
21
22 namespace crypto {
23
24 namespace {
25
GetCipherForKey(const SymmetricKey * key)26 const EVP_CIPHER* GetCipherForKey(const SymmetricKey* key) {
27 switch (key->key().length()) {
28 case 16: return EVP_aes_128_cbc();
29 case 32: return EVP_aes_256_cbc();
30 default:
31 return nullptr;
32 }
33 }
34
35 } // namespace
36
37 /////////////////////////////////////////////////////////////////////////////
38 // Encryptor Implementation.
39
Encryptor()40 Encryptor::Encryptor() : key_(nullptr), mode_(CBC) {}
41
42 Encryptor::~Encryptor() = default;
43
Init(const SymmetricKey * key,Mode mode,std::string_view iv)44 bool Encryptor::Init(const SymmetricKey* key, Mode mode, std::string_view iv) {
45 return Init(key, mode, base::as_byte_span(iv));
46 }
47
Init(const SymmetricKey * key,Mode mode,base::span<const uint8_t> iv)48 bool Encryptor::Init(const SymmetricKey* key,
49 Mode mode,
50 base::span<const uint8_t> iv) {
51 DCHECK(key);
52 DCHECK(mode == CBC || mode == CTR);
53
54 if (mode == CBC && iv.size() != AES_BLOCK_SIZE)
55 return false;
56 // CTR mode passes the starting counter separately, via SetCounter().
57 if (mode == CTR && !iv.empty())
58 return false;
59
60 if (GetCipherForKey(key) == nullptr)
61 return false;
62
63 key_ = key;
64 mode_ = mode;
65 iv_.assign(iv.begin(), iv.end());
66 return true;
67 }
68
Encrypt(std::string_view plaintext,std::string * ciphertext)69 bool Encryptor::Encrypt(std::string_view plaintext, std::string* ciphertext) {
70 return CryptString(/*do_encrypt=*/true, plaintext, ciphertext);
71 }
72
Encrypt(base::span<const uint8_t> plaintext,std::vector<uint8_t> * ciphertext)73 bool Encryptor::Encrypt(base::span<const uint8_t> plaintext,
74 std::vector<uint8_t>* ciphertext) {
75 return CryptBytes(/*do_encrypt=*/true, plaintext, ciphertext);
76 }
77
Decrypt(std::string_view ciphertext,std::string * plaintext)78 bool Encryptor::Decrypt(std::string_view ciphertext, std::string* plaintext) {
79 return CryptString(/*do_encrypt=*/false, ciphertext, plaintext);
80 }
81
Decrypt(base::span<const uint8_t> ciphertext,std::vector<uint8_t> * plaintext)82 bool Encryptor::Decrypt(base::span<const uint8_t> ciphertext,
83 std::vector<uint8_t>* plaintext) {
84 return CryptBytes(/*do_encrypt=*/false, ciphertext, plaintext);
85 }
86
SetCounter(std::string_view counter)87 bool Encryptor::SetCounter(std::string_view counter) {
88 return SetCounter(base::as_byte_span(counter));
89 }
90
SetCounter(base::span<const uint8_t> counter)91 bool Encryptor::SetCounter(base::span<const uint8_t> counter) {
92 if (mode_ != CTR)
93 return false;
94 if (counter.size() != 16u)
95 return false;
96
97 iv_.assign(counter.begin(), counter.end());
98 return true;
99 }
100
CryptString(bool do_encrypt,std::string_view input,std::string * output)101 bool Encryptor::CryptString(bool do_encrypt,
102 std::string_view input,
103 std::string* output) {
104 std::string result(MaxOutput(do_encrypt, input.size()), '\0');
105 std::optional<size_t> len =
106 (mode_ == CTR) ? CryptCTR(do_encrypt, base::as_byte_span(input),
107 base::as_writable_byte_span(result))
108 : Crypt(do_encrypt, base::as_byte_span(input),
109 base::as_writable_byte_span(result));
110 if (!len)
111 return false;
112
113 result.resize(*len);
114 *output = std::move(result);
115 return true;
116 }
117
CryptBytes(bool do_encrypt,base::span<const uint8_t> input,std::vector<uint8_t> * output)118 bool Encryptor::CryptBytes(bool do_encrypt,
119 base::span<const uint8_t> input,
120 std::vector<uint8_t>* output) {
121 std::vector<uint8_t> result(MaxOutput(do_encrypt, input.size()));
122 std::optional<size_t> len = (mode_ == CTR)
123 ? CryptCTR(do_encrypt, input, result)
124 : Crypt(do_encrypt, input, result);
125 if (!len)
126 return false;
127
128 result.resize(*len);
129 *output = std::move(result);
130 return true;
131 }
132
MaxOutput(bool do_encrypt,size_t length)133 size_t Encryptor::MaxOutput(bool do_encrypt, size_t length) {
134 size_t result = length + ((do_encrypt && mode_ == CBC) ? 16 : 0);
135 CHECK_GE(result, length); // Overflow
136 return result;
137 }
138
Crypt(bool do_encrypt,base::span<const uint8_t> input,base::span<uint8_t> output)139 std::optional<size_t> Encryptor::Crypt(bool do_encrypt,
140 base::span<const uint8_t> input,
141 base::span<uint8_t> output) {
142 DCHECK(key_); // Must call Init() before En/De-crypt.
143
144 const EVP_CIPHER* cipher = GetCipherForKey(key_);
145 DCHECK(cipher); // Already handled in Init();
146
147 const std::string& key = key_->key();
148 DCHECK_EQ(EVP_CIPHER_iv_length(cipher), iv_.size());
149 DCHECK_EQ(EVP_CIPHER_key_length(cipher), key.size());
150
151 OpenSSLErrStackTracer err_tracer(FROM_HERE);
152 bssl::ScopedEVP_CIPHER_CTX ctx;
153 if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr,
154 reinterpret_cast<const uint8_t*>(key.data()),
155 iv_.data(), do_encrypt)) {
156 return std::nullopt;
157 }
158
159 // Encrypting needs a block size of space to allow for any padding.
160 CHECK_GE(output.size(), input.size() + (do_encrypt ? iv_.size() : 0));
161 int out_len;
162 if (!EVP_CipherUpdate(ctx.get(), output.data(), &out_len, input.data(),
163 input.size()))
164 return std::nullopt;
165
166 // Write out the final block plus padding (if any) to the end of the data
167 // just written.
168 int tail_len;
169 if (!EVP_CipherFinal_ex(ctx.get(), output.data() + out_len, &tail_len))
170 return std::nullopt;
171
172 out_len += tail_len;
173 DCHECK_LE(out_len, static_cast<int>(output.size()));
174 return out_len;
175 }
176
CryptCTR(bool do_encrypt,base::span<const uint8_t> input,base::span<uint8_t> output)177 std::optional<size_t> Encryptor::CryptCTR(bool do_encrypt,
178 base::span<const uint8_t> input,
179 base::span<uint8_t> output) {
180 if (iv_.size() != AES_BLOCK_SIZE) {
181 LOG(ERROR) << "Counter value not set in CTR mode.";
182 return std::nullopt;
183 }
184
185 AES_KEY aes_key;
186 if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key_->key().data()),
187 key_->key().size() * 8, &aes_key) != 0) {
188 return std::nullopt;
189 }
190
191 uint8_t ecount_buf[AES_BLOCK_SIZE] = { 0 };
192 unsigned int block_offset = 0;
193
194 // |output| must have room for |input|.
195 CHECK_GE(output.size(), input.size());
196 // Note AES_ctr128_encrypt() will update |iv_|. However, this method discards
197 // |ecount_buf| and |block_offset|, so this is not quite a streaming API.
198 AES_ctr128_encrypt(input.data(), output.data(), input.size(), &aes_key,
199 iv_.data(), ecount_buf, &block_offset);
200 return input.size();
201 }
202
203 } // namespace crypto
204