0

crypto: remove crypto/encryptor

This interface is now fully unused, replaced by crypto/aes_cbc and
crypto/aes_ctr. All clients are gone and the remaining references are
some stray includes.

Bug: 372283556
Change-Id: I1d98abafa434f67e2ce3280d7adf8e5e19cb0d70
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/6191987
Reviewed-by: Nick Harper <nharper@chromium.org>
Reviewed-by: Mark Foltz <mfoltz@chromium.org>
Reviewed-by: Kyle Horimoto <khorimoto@chromium.org>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Elly FJ <ellyjones@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1434946}
This commit is contained in:
Elly
2025-03-19 11:01:45 -07:00
committed by Chromium LUCI CQ
parent 062cfc28ba
commit 6a647d4867
14 changed files with 1 additions and 909 deletions
chrome/browser
enterprise
connectors
device_trust
attestation
nearby_sharing
certificates
chromeos/ash/components/network/onc
components
crypto
media/filters

@ -12,10 +12,8 @@
#include "base/containers/span.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "crypto/encryptor.h"
#include "crypto/random.h"
#include "crypto/signature_verifier.h"
#include "crypto/symmetric_key.h"
#include "third_party/boringssl/src/include/openssl/bn.h"
#include "third_party/boringssl/src/include/openssl/bytestring.h"
#include "third_party/boringssl/src/include/openssl/evp.h"

@ -9,10 +9,8 @@
#include "base/logging.h"
#include "base/rand_util.h"
#include "chrome/browser/nearby_sharing/certificates/constants.h"
#include "crypto/encryptor.h"
#include "crypto/hkdf.h"
#include "crypto/sha2.h"
#include "crypto/symmetric_key.h"
bool IsNearbyShareCertificateExpired(base::Time current_time,
base::Time not_after,

@ -45,9 +45,7 @@
#include "components/url_formatter/url_fixer.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "crypto/encryptor.h"
#include "crypto/hmac.h"
#include "crypto/symmetric_key.h"
#include "net/base/host_port_pair.h"
#include "net/base/proxy_server.h"
#include "net/base/proxy_string_util.h"

@ -14,8 +14,6 @@
#include "base/logging.h"
#include "base/rand_util.h"
#include "base/strings/string_util.h"
#include "crypto/encryptor.h"
#include "crypto/symmetric_key.h"
namespace error_page {
namespace {

@ -23,7 +23,6 @@
#include "base/strings/string_util.h"
#include "components/dbus/thread_linux/dbus_thread_linux.h"
#include "components/os_crypt/async/common/algorithm.mojom.h"
#include "crypto/encryptor.h"
#include "crypto/kdf.h"
#include "dbus/message.h"
#include "dbus/object_path.h"

@ -22,7 +22,6 @@
#include "components/dbus/utils/check_for_service_and_start.h"
#include "components/dbus/utils/name_has_owner.h"
#include "components/os_crypt/async/browser/key_provider.h"
#include "crypto/encryptor.h"
#include "dbus/bus.h"
#include "dbus/object_path.h"
#include "dbus/object_proxy.h"

@ -15,7 +15,6 @@
#include "build/branding_buildflags.h"
#include "components/dbus/properties/types.h"
#include "components/dbus/utils/name_has_owner.h"
#include "crypto/encryptor.h"
#include "dbus/message.h"
#include "dbus/mock_bus.h"
#include "dbus/mock_object_proxy.h"

@ -22,9 +22,7 @@
#include "components/os_crypt/sync/key_storage_linux.h"
#include "components/os_crypt/sync/os_crypt_metrics.h"
#include "crypto/aes_cbc.h"
#include "crypto/encryptor.h"
#include "crypto/kdf.h"
#include "crypto/symmetric_key.h"
namespace {

@ -28,8 +28,6 @@ component("crypto") {
"ec_signature_creator.h",
"ec_signature_creator_impl.cc",
"ec_signature_creator_impl.h",
"encryptor.cc",
"encryptor.h",
"features.cc",
"features.h",
"hash.cc",
@ -179,7 +177,6 @@ test("crypto_unittests") {
"aes_ctr_unittest.cc",
"ec_private_key_unittest.cc",
"ec_signature_creator_unittest.cc",
"encryptor_unittest.cc",
"hash_unittest.cc",
"hmac_unittest.cc",
"kdf_unittest.cc",

@ -1,203 +0,0 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "crypto/encryptor.h"
#include <stddef.h>
#include <stdint.h>
#include "base/logging.h"
#include "base/strings/string_util.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, std::string_view iv) {
return Init(key, mode, base::as_byte_span(iv));
}
bool Encryptor::Init(const SymmetricKey* key,
Mode mode,
base::span<const uint8_t> iv) {
DCHECK(key);
DCHECK(mode == CBC || mode == CTR);
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(std::string_view 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(std::string_view 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(std::string_view counter) {
return SetCounter(base::as_byte_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,
std::string_view input,
std::string* output) {
std::string result(MaxOutput(do_encrypt, input.size()), '\0');
std::optional<size_t> len =
(mode_ == CTR) ? CryptCTR(do_encrypt, base::as_byte_span(input),
base::as_writable_byte_span(result))
: Crypt(do_encrypt, base::as_byte_span(input),
base::as_writable_byte_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()));
std::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;
}
std::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 std::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 std::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 std::nullopt;
out_len += tail_len;
DCHECK_LE(out_len, static_cast<int>(output.size()));
return out_len;
}
std::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 std::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 std::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

@ -1,103 +0,0 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CRYPTO_ENCRYPTOR_H_
#define CRYPTO_ENCRYPTOR_H_
#include <stddef.h>
#include <stdint.h>
#include <memory>
#include <optional>
#include <string>
#include <string_view>
#include "base/containers/span.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "crypto/crypto_export.h"
namespace crypto {
class SymmetricKey;
// This class implements encryption without authentication, which is usually
// unsafe. Prefer crypto::Aead for new code. If using this class, prefer the
// base::span and std::vector overloads over the std::string_view and
// std::string overloads.
class CRYPTO_EXPORT Encryptor {
public:
enum Mode {
CBC,
CTR,
};
Encryptor();
~Encryptor();
// Initializes the encryptor using |key| and |iv|. Returns false if either the
// key or the initialization vector cannot be used.
//
// If |mode| is CBC, |iv| must not be empty; if it is CTR, then |iv| must be
// empty.
bool Init(const SymmetricKey* key, Mode mode, std::string_view iv);
bool Init(const SymmetricKey* key, Mode mode, base::span<const uint8_t> iv);
// Encrypts |plaintext| into |ciphertext|. |plaintext| may only be empty if
// the mode is CBC.
bool Encrypt(std::string_view plaintext, std::string* ciphertext);
bool Encrypt(base::span<const uint8_t> plaintext,
std::vector<uint8_t>* ciphertext);
// Decrypts |ciphertext| into |plaintext|. |ciphertext| must not be empty.
//
// WARNING: In CBC mode, Decrypt() returns false if it detects the padding
// in the decrypted plaintext is wrong. Padding errors can result from
// tampered ciphertext or a wrong decryption key. But successful decryption
// does not imply the authenticity of the data. The caller of Decrypt()
// must either authenticate the ciphertext before decrypting it, or take
// care to not report decryption failure. Otherwise it could inadvertently
// be used as a padding oracle to attack the cryptosystem.
bool Decrypt(std::string_view ciphertext, std::string* plaintext);
bool Decrypt(base::span<const uint8_t> ciphertext,
std::vector<uint8_t>* plaintext);
// Sets the counter value when in CTR mode. Currently only 128-bits
// counter value is supported.
//
// Returns true only if update was successful.
bool SetCounter(std::string_view counter);
bool SetCounter(base::span<const uint8_t> counter);
// TODO(albertb): Support streaming encryption.
private:
raw_ptr<const SymmetricKey, DanglingUntriaged> key_;
Mode mode_;
bool CryptString(bool do_encrypt,
std::string_view input,
std::string* output);
bool CryptBytes(bool do_encrypt,
base::span<const uint8_t> input,
std::vector<uint8_t>* output);
// On success, these helper functions return the number of bytes written to
// |output|.
size_t MaxOutput(bool do_encrypt, size_t length);
std::optional<size_t> Crypt(bool do_encrypt,
base::span<const uint8_t> input,
base::span<uint8_t> output);
std::optional<size_t> CryptCTR(bool do_encrypt,
base::span<const uint8_t> input,
base::span<uint8_t> output);
// In CBC mode, the IV passed to Init(). In CTR mode, the counter value passed
// to SetCounter().
std::vector<uint8_t> iv_;
};
} // namespace crypto
#endif // CRYPTO_ENCRYPTOR_H_

@ -1,584 +0,0 @@
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/351564777): Remove this and convert code to safer constructs.
#pragma allow_unsafe_buffers
#endif
#include "crypto/encryptor.h"
#include <stddef.h>
#include <array>
#include <memory>
#include <string>
#include "base/containers/heap_array.h"
#include "base/containers/span.h"
#include "base/strings/string_number_conversions.h"
#include "crypto/symmetric_key.h"
#include "testing/gtest/include/gtest/gtest.h"
// PBKDF2-HMAC-SHA1("password", "saltiest", 1000 iterations)
constexpr auto kTestKey = std::to_array<uint8_t>({
0xd9, 0xb7, 0x6d, 0x65, 0x3b, 0x0b, 0x25, 0xd7, 0xa8, 0xce, 0xed,
0xba, 0x98, 0xee, 0xe5, 0x09, 0x53, 0x2a, 0xa7, 0x84, 0xe4, 0x44,
0x72, 0x30, 0x03, 0x99, 0x34, 0x51, 0xa9, 0x8a, 0x74, 0x53,
});
TEST(EncryptorTest, EncryptDecrypt) {
crypto::Encryptor encryptor;
// The IV must be exactly as long as the cipher block size.
std::string iv("the iv: 16 bytes");
EXPECT_EQ(16U, iv.size());
crypto::SymmetricKey key(kTestKey);
EXPECT_TRUE(encryptor.Init(&key, crypto::Encryptor::CBC, iv));
std::string plaintext("this is the plaintext");
std::string ciphertext;
EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
EXPECT_LT(0U, ciphertext.size());
std::string decrypted;
EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
// Repeat the test with the bytes API.
std::vector<uint8_t> plaintext_vec(plaintext.begin(), plaintext.end());
std::vector<uint8_t> ciphertext_vec;
EXPECT_TRUE(encryptor.Encrypt(plaintext_vec, &ciphertext_vec));
EXPECT_LT(0U, ciphertext_vec.size());
std::vector<uint8_t> decrypted_vec;
EXPECT_TRUE(encryptor.Decrypt(ciphertext_vec, &decrypted_vec));
EXPECT_EQ(plaintext_vec, decrypted_vec);
}
TEST(EncryptorTest, DecryptWrongKey) {
crypto::SymmetricKey key(kTestKey);
// A wrong key that can be detected by implementations that validate every
// byte in the padding.
// PBKDF2-HMAC-SHA1("wrongword", "sweetest", 1000 iterations)
constexpr auto kWrongKey = std::to_array<uint8_t>({
0x88, 0xea, 0x6c, 0x27, 0xbd, 0xcc, 0x56, 0xde, 0x31, 0xb6, 0x68,
0x85, 0x61, 0x5a, 0xd8, 0xdb, 0xe4, 0x82, 0xfc, 0xa9, 0xfa, 0x5b,
0x1b, 0xb1, 0x4f, 0x31, 0xb9, 0xe3, 0x04, 0xd4, 0x67, 0x58,
});
crypto::SymmetricKey wrong_key(kWrongKey);
// A wrong key that can't be detected by any implementation. The password
// "wrongword;" would also work.
// PBKDF2-HMAC-SHA1("wrongword+", "sweetest", 1000 iterations)
constexpr auto kWrongKey2 = std::to_array<uint8_t>({
0x3b, 0x5c, 0x1e, 0x70, 0x7f, 0x31, 0xbf, 0x8e, 0xc8, 0x45, 0xd3,
0x04, 0x20, 0xa4, 0x21, 0xfb, 0xd2, 0x21, 0x11, 0x44, 0x7b, 0xca,
0x48, 0xf5, 0xeb, 0xf4, 0xd7, 0xca, 0xa8, 0x18, 0xfc, 0x37,
});
crypto::SymmetricKey wrong_key2(kWrongKey2);
// A wrong key that can be detected by all implementations.
// PBKDF2-HMAC-SHA1("wrongwordx", "sweetest", 1000 iterations)
constexpr auto kWrongKey3 = std::to_array<uint8_t>({
0x37, 0xde, 0x34, 0x4b, 0x76, 0xf2, 0x52, 0x3e, 0xde, 0xd0, 0x4b,
0xc9, 0x5a, 0x83, 0x51, 0xc0, 0x5a, 0xf5, 0x37, 0xfa, 0xce, 0x7d,
0x51, 0xa8, 0xd8, 0xae, 0xfc, 0x1c, 0x0b, 0xb7, 0xe9, 0x3f,
});
crypto::SymmetricKey wrong_key3(kWrongKey3);
crypto::Encryptor encryptor;
// The IV must be exactly as long as the cipher block size.
std::string iv("the iv: 16 bytes");
EXPECT_EQ(16U, iv.size());
EXPECT_TRUE(encryptor.Init(&key, crypto::Encryptor::CBC, iv));
std::string plaintext("this is the plaintext");
std::string ciphertext;
EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
static const auto expected_ciphertext = std::to_array<unsigned char>({
0x7D, 0x67, 0x5B, 0x53, 0xE6, 0xD8, 0x0F, 0x27, 0x74, 0xB1, 0x90,
0xFE, 0x6E, 0x58, 0x4A, 0xA0, 0x0E, 0x35, 0xE3, 0x01, 0xC0, 0xFE,
0x9A, 0xD8, 0x48, 0x1D, 0x42, 0xB0, 0xBA, 0x21, 0xB2, 0x0C,
});
ASSERT_EQ(std::size(expected_ciphertext), ciphertext.size());
for (size_t i = 0; i < ciphertext.size(); ++i) {
ASSERT_EQ(expected_ciphertext[i],
static_cast<unsigned char>(ciphertext[i]));
}
std::string decrypted;
// This wrong key causes the last padding byte to be 5, which is a valid
// padding length, and the second to last padding byte to be 137, which is
// invalid. If an implementation simply uses the last padding byte to
// determine the padding length without checking every padding byte,
// Encryptor::Decrypt() will still return true. This is the case for NSS
// (crbug.com/124434).
crypto::Encryptor decryptor;
EXPECT_TRUE(decryptor.Init(&wrong_key, crypto::Encryptor::CBC, iv));
EXPECT_FALSE(decryptor.Decrypt(ciphertext, &decrypted));
// This demonstrates that not all wrong keys can be detected by padding
// error. This wrong key causes the last padding byte to be 1, which is
// a valid padding block of length 1.
crypto::Encryptor decryptor2;
EXPECT_TRUE(decryptor2.Init(&wrong_key2, crypto::Encryptor::CBC, iv));
EXPECT_TRUE(decryptor2.Decrypt(ciphertext, &decrypted));
// This wrong key causes the last padding byte to be 253, which should be
// rejected by all implementations.
crypto::Encryptor decryptor3;
EXPECT_TRUE(decryptor3.Init(&wrong_key3, crypto::Encryptor::CBC, iv));
EXPECT_FALSE(decryptor3.Decrypt(ciphertext, &decrypted));
}
namespace {
// From NIST SP 800-38a test cast:
// - F.5.1 CTR-AES128.Encrypt
// - F.5.6 CTR-AES256.Encrypt
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
const unsigned char kAES128CTRKey[] = {
0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};
const unsigned char kAES256CTRKey[] = {
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe,
0x2b, 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7,
0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
};
const unsigned char kAESCTRInitCounter[] = {
0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
};
const unsigned char kAESCTRPlaintext[] = {
// Block #1
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96,
0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
// Block #2
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c,
0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
// Block #3
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11,
0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
// Block #4
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17,
0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
};
const unsigned char kAES128CTRCiphertext[] = {
// Block #1
0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26,
0x1b, 0xef, 0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
// Block #2
0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff,
0x86, 0x17, 0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
// Block #3
0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e,
0x5b, 0x4f, 0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
// Block #4
0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1,
0x79, 0x21, 0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
};
const unsigned char kAES256CTRCiphertext[] = {
// Block #1
0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5,
0xb7, 0xa7, 0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
// Block #2
0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a,
0xca, 0x84, 0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
// Block #3
0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c,
0xe8, 0x70, 0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
// Block #4
0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6,
0x13, 0xc2, 0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6
};
void TestAESCTREncrypt(
const unsigned char* key, size_t key_size,
const unsigned char* init_counter, size_t init_counter_size,
const unsigned char* plaintext, size_t plaintext_size,
const unsigned char* ciphertext, size_t ciphertext_size) {
std::string key_str(reinterpret_cast<const char*>(key), key_size);
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key_str));
ASSERT_TRUE(sym_key.get());
crypto::Encryptor encryptor;
EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CTR, ""));
std::string_view init_counter_str(reinterpret_cast<const char*>(init_counter),
init_counter_size);
std::string_view plaintext_str(reinterpret_cast<const char*>(plaintext),
plaintext_size);
EXPECT_TRUE(encryptor.SetCounter(init_counter_str));
std::string encrypted;
EXPECT_TRUE(encryptor.Encrypt(plaintext_str, &encrypted));
EXPECT_EQ(ciphertext_size, encrypted.size());
EXPECT_EQ(0, memcmp(encrypted.data(), ciphertext, encrypted.size()));
std::string decrypted;
EXPECT_TRUE(encryptor.SetCounter(init_counter_str));
EXPECT_TRUE(encryptor.Decrypt(encrypted, &decrypted));
EXPECT_EQ(plaintext_str, decrypted);
// Repeat the test with the bytes API.
EXPECT_TRUE(
encryptor.SetCounter(base::span(init_counter, init_counter_size)));
std::vector<uint8_t> encrypted_vec;
EXPECT_TRUE(
encryptor.Encrypt(base::span(plaintext, plaintext_size), &encrypted_vec));
EXPECT_EQ(ciphertext_size, encrypted_vec.size());
EXPECT_EQ(0, memcmp(encrypted_vec.data(), ciphertext, encrypted_vec.size()));
std::vector<uint8_t> decrypted_vec;
EXPECT_TRUE(
encryptor.SetCounter(base::span(init_counter, init_counter_size)));
EXPECT_TRUE(encryptor.Decrypt(encrypted_vec, &decrypted_vec));
EXPECT_EQ(std::vector<uint8_t>(plaintext, plaintext + plaintext_size),
decrypted_vec);
}
void TestAESCTRMultipleDecrypt(
const unsigned char* key, size_t key_size,
const unsigned char* init_counter, size_t init_counter_size,
const unsigned char* plaintext, size_t plaintext_size,
const unsigned char* ciphertext, size_t ciphertext_size) {
std::string key_str(reinterpret_cast<const char*>(key), key_size);
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key_str));
ASSERT_TRUE(sym_key.get());
crypto::Encryptor encryptor;
EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CTR, ""));
// Counter is set only once.
EXPECT_TRUE(encryptor.SetCounter(std::string_view(
reinterpret_cast<const char*>(init_counter), init_counter_size)));
std::string ciphertext_str(reinterpret_cast<const char*>(ciphertext),
ciphertext_size);
auto kTestDecryptSizes = std::to_array<int>({32, 16, 8});
int offset = 0;
for (size_t i = 0; i < std::size(kTestDecryptSizes); ++i) {
std::string decrypted;
size_t len = kTestDecryptSizes[i];
EXPECT_TRUE(
encryptor.Decrypt(ciphertext_str.substr(offset, len), &decrypted));
EXPECT_EQ(len, decrypted.size());
EXPECT_EQ(0, memcmp(decrypted.data(), plaintext + offset, len));
offset += len;
}
}
} // namespace
TEST(EncryptorTest, EncryptAES128CTR) {
TestAESCTREncrypt(kAES128CTRKey, std::size(kAES128CTRKey), kAESCTRInitCounter,
std::size(kAESCTRInitCounter), kAESCTRPlaintext,
std::size(kAESCTRPlaintext), kAES128CTRCiphertext,
std::size(kAES128CTRCiphertext));
}
TEST(EncryptorTest, EncryptAES256CTR) {
TestAESCTREncrypt(kAES256CTRKey, std::size(kAES256CTRKey), kAESCTRInitCounter,
std::size(kAESCTRInitCounter), kAESCTRPlaintext,
std::size(kAESCTRPlaintext), kAES256CTRCiphertext,
std::size(kAES256CTRCiphertext));
}
TEST(EncryptorTest, EncryptAES128CTR_MultipleDecrypt) {
TestAESCTRMultipleDecrypt(kAES128CTRKey, std::size(kAES128CTRKey),
kAESCTRInitCounter, std::size(kAESCTRInitCounter),
kAESCTRPlaintext, std::size(kAESCTRPlaintext),
kAES128CTRCiphertext,
std::size(kAES128CTRCiphertext));
}
TEST(EncryptorTest, EncryptAES256CTR_MultipleDecrypt) {
TestAESCTRMultipleDecrypt(kAES256CTRKey, std::size(kAES256CTRKey),
kAESCTRInitCounter, std::size(kAESCTRInitCounter),
kAESCTRPlaintext, std::size(kAESCTRPlaintext),
kAES256CTRCiphertext,
std::size(kAES256CTRCiphertext));
}
TEST(EncryptorTest, EncryptDecryptCTR) {
std::unique_ptr<crypto::SymmetricKey> key(
crypto::SymmetricKey::GenerateRandomKey(crypto::SymmetricKey::AES, 128));
EXPECT_TRUE(key.get());
const std::string kInitialCounter = "0000000000000000";
crypto::Encryptor encryptor;
EXPECT_TRUE(encryptor.Init(key.get(), crypto::Encryptor::CTR, ""));
EXPECT_TRUE(encryptor.SetCounter(kInitialCounter));
std::string plaintext("normal plaintext of random length");
std::string ciphertext;
EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
EXPECT_LT(0U, ciphertext.size());
std::string decrypted;
EXPECT_TRUE(encryptor.SetCounter(kInitialCounter));
EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
plaintext = "0123456789012345";
EXPECT_TRUE(encryptor.SetCounter(kInitialCounter));
EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
EXPECT_LT(0U, ciphertext.size());
EXPECT_TRUE(encryptor.SetCounter(kInitialCounter));
EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
}
// TODO(wtc): add more known-answer tests. Test vectors are available from
// http://www.ietf.org/rfc/rfc3602
// http://csrc.nist.gov/publications/nistpubs/800-38a/sp800-38a.pdf
// http://gladman.plushost.co.uk/oldsite/AES/index.php
// http://csrc.nist.gov/groups/STM/cavp/documents/aes/KAT_AES.zip
// NIST SP 800-38A test vector F.2.5 CBC-AES256.Encrypt.
TEST(EncryptorTest, EncryptAES256CBC) {
// From NIST SP 800-38a test cast F.2.5 CBC-AES256.Encrypt.
static const auto kRawKey = std::to_array<unsigned char>({
0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae,
0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61,
0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4,
});
static const auto kRawIv = std::to_array<unsigned char>(
{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b,
0x0c, 0x0d, 0x0e, 0x0f});
static const auto kRawPlaintext = std::to_array<unsigned char>(
{// Block #1
0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e, 0x11,
0x73, 0x93, 0x17, 0x2a,
// Block #2
0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f, 0xac,
0x45, 0xaf, 0x8e, 0x51,
// Block #3
0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1, 0x19,
0x1a, 0x0a, 0x52, 0xef,
// Block #4
0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41, 0x7b,
0xe6, 0x6c, 0x37, 0x10});
static const auto kRawCiphertext = std::to_array<unsigned char>(
{// Block #1
0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e, 0xab, 0xfb,
0x5f, 0x7b, 0xfb, 0xd6,
// Block #2
0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f, 0x77, 0x7b,
0xc6, 0x70, 0x2c, 0x7d,
// Block #3
0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30, 0xe2, 0x63,
0x04, 0x23, 0x14, 0x61,
// Block #4
0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c, 0x19, 0x07,
0x8c, 0x6a, 0x9d, 0x1b,
// PKCS #5 padding, encrypted.
0x3f, 0x46, 0x17, 0x96, 0xd6, 0xb0, 0xd6, 0xb2, 0xe0, 0xc2, 0xa7, 0x2b,
0x4d, 0x80, 0xe6, 0x44});
std::string key(reinterpret_cast<const char*>(kRawKey.data()),
sizeof(kRawKey));
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key));
ASSERT_TRUE(sym_key.get());
crypto::Encryptor encryptor;
// The IV must be exactly as long a the cipher block size.
std::string iv(reinterpret_cast<const char*>(kRawIv.data()), sizeof(kRawIv));
EXPECT_EQ(16U, iv.size());
EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv));
std::string plaintext(reinterpret_cast<const char*>(kRawPlaintext.data()),
sizeof(kRawPlaintext));
std::string ciphertext;
EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
EXPECT_EQ(sizeof(kRawCiphertext), ciphertext.size());
EXPECT_EQ(
0, memcmp(ciphertext.data(), kRawCiphertext.data(), ciphertext.size()));
std::string decrypted;
EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
}
// Expected output derived from the NSS implementation.
TEST(EncryptorTest, EncryptAES128CBCRegression) {
std::string key = "128=SixteenBytes";
std::string iv = "Sweet Sixteen IV";
std::string plaintext = "Plain text with a g-clef U+1D11E \360\235\204\236";
std::string expected_ciphertext_hex =
"D4A67A0BA33C30F207344D81D1E944BBE65587C3D7D9939A"
"C070C62B9C15A3EA312EA4AD1BC7929F4D3C16B03AD5ADA8";
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key));
ASSERT_TRUE(sym_key.get());
crypto::Encryptor encryptor;
// The IV must be exactly as long a the cipher block size.
EXPECT_EQ(16U, iv.size());
EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv));
std::string ciphertext;
EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext));
std::string decrypted;
EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decrypted));
EXPECT_EQ(plaintext, decrypted);
}
// Symmetric keys with an unsupported size should be rejected. Whether they are
// rejected by SymmetricKey::Import or Encryptor::Init depends on the platform.
TEST(EncryptorTest, UnsupportedKeySize) {
std::string key = "7 = bad";
std::string iv = "Sweet Sixteen IV";
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key));
if (!sym_key.get())
return;
crypto::Encryptor encryptor;
// The IV must be exactly as long as the cipher block size.
EXPECT_EQ(16U, iv.size());
EXPECT_FALSE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv));
}
TEST(EncryptorTest, UnsupportedIV) {
std::string key = "128=SixteenBytes";
std::string iv = "OnlyForteen :(";
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key));
ASSERT_TRUE(sym_key.get());
crypto::Encryptor encryptor;
EXPECT_FALSE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv));
}
TEST(EncryptorTest, EmptyEncryptCBC) {
std::string key = "128=SixteenBytes";
std::string iv = "Sweet Sixteen IV";
std::string plaintext;
std::string expected_ciphertext_hex = "8518B8878D34E7185E300D0FCC426396";
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key));
ASSERT_TRUE(sym_key.get());
crypto::Encryptor encryptor;
// The IV must be exactly as long as the cipher block size.
EXPECT_EQ(16U, iv.size());
EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv));
std::string ciphertext;
EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext));
std::string decrypted;
EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decrypted));
EXPECT_EQ(decrypted, plaintext);
// Decrypting the empty string should fail. Our formulation of CBC expects a
// full block of padding for CBC.
EXPECT_FALSE(encryptor.Decrypt(std::string(), &decrypted));
// Repeat the test with the byte-based API.
EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv));
std::vector<uint8_t> ciphertext_bytes;
EXPECT_TRUE(
encryptor.Encrypt(base::span<const uint8_t>(), &ciphertext_bytes));
EXPECT_EQ(expected_ciphertext_hex, base::HexEncode(ciphertext_bytes));
std::vector<uint8_t> decrypted_bytes;
EXPECT_TRUE(encryptor.Decrypt(ciphertext_bytes, &decrypted_bytes));
EXPECT_EQ(decrypted_bytes.size(), 0u);
// Decrypting the empty string should fail. Our formulation of CBC expects a
// full block of padding for CBC.
EXPECT_FALSE(
encryptor.Decrypt(base::span<const uint8_t>(), &decrypted_bytes));
}
TEST(EncryptorTest, EmptyEncryptCTR) {
std::string key = "128=SixteenBytes";
std::string iv = "Sweet Sixteen IV";
std::string plaintext;
std::string expected_ciphertext;
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key));
ASSERT_TRUE(sym_key.get());
crypto::Encryptor encryptor;
EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CTR, ""));
ASSERT_TRUE(encryptor.SetCounter(iv));
std::string ciphertext;
EXPECT_TRUE(encryptor.Encrypt(plaintext, &ciphertext));
EXPECT_EQ(expected_ciphertext, ciphertext);
std::string decrypted;
EXPECT_TRUE(encryptor.Decrypt(ciphertext, &decrypted));
EXPECT_EQ(decrypted, plaintext);
// Repeat the test with the byte-based API.
ASSERT_TRUE(encryptor.SetCounter(iv));
std::vector<uint8_t> ciphertext_bytes;
EXPECT_TRUE(
encryptor.Encrypt(base::span<const uint8_t>(), &ciphertext_bytes));
EXPECT_EQ(ciphertext_bytes.size(), 0u);
std::vector<uint8_t> decrypted_bytes;
EXPECT_TRUE(encryptor.Decrypt(base::span<const uint8_t>(), &decrypted_bytes));
EXPECT_EQ(decrypted_bytes.size(), 0u);
}
TEST(EncryptorTest, CipherTextNotMultipleOfBlockSize) {
std::string key = "128=SixteenBytes";
std::string iv = "Sweet Sixteen IV";
std::unique_ptr<crypto::SymmetricKey> sym_key(
crypto::SymmetricKey::Import(crypto::SymmetricKey::AES, key));
ASSERT_TRUE(sym_key.get());
crypto::Encryptor encryptor;
// The IV must be exactly as long a the cipher block size.
EXPECT_EQ(16U, iv.size());
EXPECT_TRUE(encryptor.Init(sym_key.get(), crypto::Encryptor::CBC, iv));
// Use a separately allocated array to improve the odds of the memory tools
// catching invalid accesses.
//
// Otherwise when using std::string as the other tests do, accesses several
// bytes off the end of the buffer may fall inside the reservation of
// the string and not be detected.
auto ciphertext = base::HeapArray<char>::Uninit(1);
std::string plaintext;
EXPECT_FALSE(
encryptor.Decrypt(base::as_string_view(ciphertext), &plaintext));
}

@ -26,8 +26,7 @@ namespace crypto {
// TODO(https://issues.chromium.org/issues/370724578): get rid of this.
class CRYPTO_EXPORT SymmetricKey {
public:
// Defines the algorithm that a key will be used with. See also
// class Encryptor.
// Defines the algorithm that a key will be used with.
enum Algorithm {
AES,
};

@ -13,7 +13,6 @@
#include "base/task/sequenced_task_runner.h"
#include "base/threading/sequence_bound.h"
#include "base/time/time.h"
#include "crypto/encryptor.h"
#include "media/base/media_export.h"
#include "media/base/media_log.h"
#include "media/base/media_track.h"