• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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