123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // Copyright (c) 2012 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "crypto/encryptor.h"
- #include <stddef.h>
- #include <stdint.h>
- #include "base/logging.h"
- #include "base/strings/string_util.h"
- #include "base/sys_byteorder.h"
- #include "crypto/openssl_util.h"
- #include "crypto/symmetric_key.h"
- #include "third_party/boringssl/src/include/openssl/aes.h"
- #include "third_party/boringssl/src/include/openssl/evp.h"
- namespace crypto {
- namespace {
- const EVP_CIPHER* GetCipherForKey(const SymmetricKey* key) {
- switch (key->key().length()) {
- case 16: return EVP_aes_128_cbc();
- case 32: return EVP_aes_256_cbc();
- default:
- return nullptr;
- }
- }
- } // namespace
- /////////////////////////////////////////////////////////////////////////////
- // Encryptor Implementation.
- Encryptor::Encryptor() : key_(nullptr), mode_(CBC) {}
- Encryptor::~Encryptor() = default;
- bool Encryptor::Init(const SymmetricKey* key, Mode mode, base::StringPiece iv) {
- return Init(key, mode, base::as_bytes(base::make_span(iv)));
- }
- bool Encryptor::Init(const SymmetricKey* key,
- Mode mode,
- base::span<const uint8_t> iv) {
- DCHECK(key);
- DCHECK(mode == CBC || mode == CTR);
- EnsureOpenSSLInit();
- if (mode == CBC && iv.size() != AES_BLOCK_SIZE)
- return false;
- // CTR mode passes the starting counter separately, via SetCounter().
- if (mode == CTR && !iv.empty())
- return false;
- if (GetCipherForKey(key) == nullptr)
- return false;
- key_ = key;
- mode_ = mode;
- iv_.assign(iv.begin(), iv.end());
- return true;
- }
- bool Encryptor::Encrypt(base::StringPiece plaintext, std::string* ciphertext) {
- return CryptString(/*do_encrypt=*/true, plaintext, ciphertext);
- }
- bool Encryptor::Encrypt(base::span<const uint8_t> plaintext,
- std::vector<uint8_t>* ciphertext) {
- return CryptBytes(/*do_encrypt=*/true, plaintext, ciphertext);
- }
- bool Encryptor::Decrypt(base::StringPiece ciphertext, std::string* plaintext) {
- return CryptString(/*do_encrypt=*/false, ciphertext, plaintext);
- }
- bool Encryptor::Decrypt(base::span<const uint8_t> ciphertext,
- std::vector<uint8_t>* plaintext) {
- return CryptBytes(/*do_encrypt=*/false, ciphertext, plaintext);
- }
- bool Encryptor::SetCounter(base::StringPiece counter) {
- return SetCounter(base::as_bytes(base::make_span(counter)));
- }
- bool Encryptor::SetCounter(base::span<const uint8_t> counter) {
- if (mode_ != CTR)
- return false;
- if (counter.size() != 16u)
- return false;
- iv_.assign(counter.begin(), counter.end());
- return true;
- }
- bool Encryptor::CryptString(bool do_encrypt,
- base::StringPiece input,
- std::string* output) {
- std::string result(MaxOutput(do_encrypt, input.size()), '\0');
- absl::optional<size_t> len =
- (mode_ == CTR)
- ? CryptCTR(do_encrypt, base::as_bytes(base::make_span(input)),
- base::as_writable_bytes(base::make_span(result)))
- : Crypt(do_encrypt, base::as_bytes(base::make_span(input)),
- base::as_writable_bytes(base::make_span(result)));
- if (!len)
- return false;
- result.resize(*len);
- *output = std::move(result);
- return true;
- }
- bool Encryptor::CryptBytes(bool do_encrypt,
- base::span<const uint8_t> input,
- std::vector<uint8_t>* output) {
- std::vector<uint8_t> result(MaxOutput(do_encrypt, input.size()));
- absl::optional<size_t> len = (mode_ == CTR)
- ? CryptCTR(do_encrypt, input, result)
- : Crypt(do_encrypt, input, result);
- if (!len)
- return false;
- result.resize(*len);
- *output = std::move(result);
- return true;
- }
- size_t Encryptor::MaxOutput(bool do_encrypt, size_t length) {
- size_t result = length + ((do_encrypt && mode_ == CBC) ? 16 : 0);
- CHECK_GE(result, length); // Overflow
- return result;
- }
- absl::optional<size_t> Encryptor::Crypt(bool do_encrypt,
- base::span<const uint8_t> input,
- base::span<uint8_t> output) {
- DCHECK(key_); // Must call Init() before En/De-crypt.
- const EVP_CIPHER* cipher = GetCipherForKey(key_);
- DCHECK(cipher); // Already handled in Init();
- const std::string& key = key_->key();
- DCHECK_EQ(EVP_CIPHER_iv_length(cipher), iv_.size());
- DCHECK_EQ(EVP_CIPHER_key_length(cipher), key.size());
- OpenSSLErrStackTracer err_tracer(FROM_HERE);
- bssl::ScopedEVP_CIPHER_CTX ctx;
- if (!EVP_CipherInit_ex(ctx.get(), cipher, nullptr,
- reinterpret_cast<const uint8_t*>(key.data()),
- iv_.data(), do_encrypt)) {
- return absl::nullopt;
- }
- // Encrypting needs a block size of space to allow for any padding.
- CHECK_GE(output.size(), input.size() + (do_encrypt ? iv_.size() : 0));
- int out_len;
- if (!EVP_CipherUpdate(ctx.get(), output.data(), &out_len, input.data(),
- input.size()))
- return absl::nullopt;
- // Write out the final block plus padding (if any) to the end of the data
- // just written.
- int tail_len;
- if (!EVP_CipherFinal_ex(ctx.get(), output.data() + out_len, &tail_len))
- return absl::nullopt;
- out_len += tail_len;
- DCHECK_LE(out_len, static_cast<int>(output.size()));
- return out_len;
- }
- absl::optional<size_t> Encryptor::CryptCTR(bool do_encrypt,
- base::span<const uint8_t> input,
- base::span<uint8_t> output) {
- if (iv_.size() != AES_BLOCK_SIZE) {
- LOG(ERROR) << "Counter value not set in CTR mode.";
- return absl::nullopt;
- }
- AES_KEY aes_key;
- if (AES_set_encrypt_key(reinterpret_cast<const uint8_t*>(key_->key().data()),
- key_->key().size() * 8, &aes_key) != 0) {
- return absl::nullopt;
- }
- uint8_t ecount_buf[AES_BLOCK_SIZE] = { 0 };
- unsigned int block_offset = 0;
- // |output| must have room for |input|.
- CHECK_GE(output.size(), input.size());
- // Note AES_ctr128_encrypt() will update |iv_|. However, this method discards
- // |ecount_buf| and |block_offset|, so this is not quite a streaming API.
- AES_ctr128_encrypt(input.data(), output.data(), input.size(), &aes_key,
- iv_.data(), ecount_buf, &block_offset);
- return input.size();
- }
- } // namespace crypto
|