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