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