std::size_t CryptoKeyHandler::encrypt_ext(
CephContext *cct,
+ uint32_t usage,
const CryptoKeyHandler::in_slice_t& in,
const CryptoKeyHandler::in_slice_t *confounder,
const CryptoKeyHandler::out_slice_t& out) const
}
ceph::bufferlist ciphertext;
std::string error;
- const int ret = encrypt_ext(cct, plaintext, (confounder ? &confounder_bl : nullptr), ciphertext, &error);
+ const int ret = encrypt_ext(cct, usage, plaintext, (confounder ? &confounder_bl : nullptr), ciphertext, &error);
if (ret != 0 || !error.empty()) {
throw std::runtime_error(std::move(error));
}
return todo_len;
}
-std::size_t CryptoKeyHandler::decrypt(
+std::size_t CryptoKeyHandler::decrypt_ext(
CephContext *cct,
+ uint32_t usage,
const CryptoKeyHandler::in_slice_t& in,
const CryptoKeyHandler::out_slice_t& out) const
{
ceph::bufferlist plaintext;
std::string error;
- const int ret = decrypt(cct, ciphertext, plaintext, &error);
+ const int ret = decrypt_ext(cct, usage, ciphertext, plaintext, &error);
if (ret != 0 || !error.empty()) {
throw std::runtime_error(std::move(error));
}
}
using CryptoKeyHandler::encrypt_ext;
- using CryptoKeyHandler::decrypt;
+ using CryptoKeyHandler::decrypt_ext;
- int encrypt_ext(CephContext *cct, const bufferlist& in, const bufferlist *confounder,
+ int encrypt_ext(CephContext *cct, uint32_t usage,
+ const bufferlist& in, const bufferlist *confounder,
bufferlist& out, std::string *error) const override {
out = in;
return 0;
}
- int decrypt(CephContext *cct, const bufferlist& in,
- bufferlist& out, std::string *error) const override {
+ int decrypt_ext(CephContext *cct, uint32_t usage, const bufferlist& in,
+ bufferlist& out, std::string *error) const override {
out = in;
return 0;
}
int validate_secret(const bufferptr& secret) override {
return 0;
}
- CryptoKeyHandler *get_key_handler_ext(const bufferptr& secret, uint32_t usage, string& error) override {
+ CryptoKeyHandler *get_key_handler_ext(const bufferptr& secret, const std::vector<uint32_t>& usages, string& error) override {
return new CryptoNoneKeyHandler;
}
};
}
int create(CryptoRandom *random, bufferptr& secret) override;
int validate_secret(const bufferptr& secret) override;
- CryptoKeyHandler *get_key_handler_ext(const bufferptr& secret, uint32_t usage /* unused */, string& error) override;
+ CryptoKeyHandler *get_key_handler_ext(const bufferptr& secret, const std::vector<uint32_t>& usages /* unused */, string& error) override;
};
// when we say AES, we mean AES-128
using CryptoKeyHandler::encrypt;
using CryptoKeyHandler::encrypt_ext;
+ using CryptoKeyHandler::decrypt_ext;
int init(const bufferptr& s, ostringstream& err) {
secret = s;
return 0;
}
- int encrypt_ext(CephContext *cct, const ceph::bufferlist& in,
+ int encrypt_ext(CephContext *cct, uint32_t usage,
+ const ceph::bufferlist& in,
const bufferlist *confounder /* ignored */,
ceph::bufferlist& out,
std::string* /* unused */) const override {
return 0;
}
- int decrypt(CephContext *cct, const ceph::bufferlist& in,
- ceph::bufferlist& out,
- std::string* /* unused */) const override {
+ int decrypt_ext(CephContext *cct, uint32_t usage,
+ const ceph::bufferlist& in,
+ ceph::bufferlist& out,
+ std::string* /* unused */) const override {
ldout(cct, 20) << "CryptoAESKeyHandler::decrypt()" << dendl;
// PKCS#7 padding enlarges even empty plain-text to take 16 bytes.
if (in.length() < AES_BLOCK_LEN || in.length() % AES_BLOCK_LEN) {
return main_encrypt_size + tail_encrypt_size;
}
- std::size_t decrypt(CephContext *cct, const in_slice_t& in,
- const out_slice_t& out) const override {
+ std::size_t decrypt_ext(CephContext *cct, uint32_t usage,
+ const in_slice_t& in,
+ const out_slice_t& out) const override {
if (in.length % AES_BLOCK_LEN != 0 || in.length < AES_BLOCK_LEN) {
throw std::runtime_error("input not aligned to AES_BLOCK_LEN");
} else if (out.buf == nullptr) {
}
CryptoKeyHandler *CryptoAES::get_key_handler_ext(const bufferptr& secret,
- uint32_t usage,
+ const std::vector<uint32_t>& usages,
string& error)
{
CryptoAESKeyHandler *ckh = new CryptoAESKeyHandler;
}
int create(CryptoRandom *random, bufferptr& secret) override;
int validate_secret(const bufferptr& secret) override;
- CryptoKeyHandler *get_key_handler_ext(const bufferptr& secret, uint32_t usage, string& error) override;
+ CryptoKeyHandler *get_key_handler_ext(const bufferptr& secret, const std::vector<uint32_t>& usages, string& error) override;
};
static constexpr const std::size_t AES256KRB5_KEY_LEN{32};
class CryptoAES256KRB5KeyHandler : public CryptoKeyHandler {
EVP_CIPHER *cipher{nullptr};
- ceph::bufferlist ki;
- const unsigned char *ki_raw;
- ceph::bufferlist ke;
- const unsigned char *ke_raw;
+ struct usage_keys {
+ ceph::bufferlist ki;
+ const unsigned char *ki_raw;
+ ceph::bufferlist ke;
+ const unsigned char *ke_raw;
+ };
+
+ usage_keys default_usage_keys;
+
+ using usage_keys_ref = std::shared_ptr<usage_keys>;
+
+ std::vector<usage_keys_ref> keys;
+
+ int do_init_usage_keys(uint32_t usage, usage_keys *uk, ostringstream& err) {
+ int r = calc_kx(secret, usage,
+ 0x55 /* Ki type */,
+ AES256KRB5_HASH_LEN /* 192 bit */,
+ uk->ki,
+ err);
+ if (r < 0) {
+ return r;
+ }
+ uk->ki_raw = reinterpret_cast<const unsigned char *>(uk->ki.c_str()); /* needed so that we can use ki in const methods */
+
+ r = calc_kx(secret, usage,
+ 0xAA /* Ke type */,
+ 32 /* 256 bit */,
+ uk->ke,
+ err);
+ if (r < 0) {
+ return r;
+ }
+ uk->ke_raw = reinterpret_cast<const unsigned char *>(uk->ke.c_str()); /* same reason as with ki */
+
+ return 0;
+ }
+
+ usage_keys *init_usage_keys(uint32_t usage) {
+ string err_str;
+ ostringstream err(err_str);
+
+ if (usage == default_usage) {
+ int r = do_init_usage_keys(usage, &default_usage_keys, err);
+ if (r < 0) {
+ return nullptr;
+ }
+ return &default_usage_keys;
+ }
+
+ if (usage >= keys.size()) {
+ keys.resize(usage + 8); /* so that we don't resize every time */
+ }
+
+ auto& uk = keys[usage];
+ if (!uk) {
+ uk = std::make_shared<usage_keys>();
+ }
+
+ int r = do_init_usage_keys(usage, uk.get(), err);
+ if (r < 0) {
+ return nullptr;
+ }
+ return uk.get();
+ }
+
+ const usage_keys *get_usage_keys(uint32_t usage) const {
+ if (usage == default_usage) {
+ return &default_usage_keys;
+ }
+
+ if (keys.size() <= usage) {
+ return nullptr;
+ }
+ auto& k_ref = keys[usage];
+ if (!k_ref) {
+ return nullptr;
+ }
+
+ return k_ref.get();
+ }
static void dump_buf(CephContext *cct, string title, const unsigned char *buf, int len)
{
int encrypt_AES256_CTS(CephContext *cct,
ceph::bufferlist& plaintext,
const unsigned char* iv, int iv_size,
+ uint32_t usage,
unsigned char *ciphertext,
int ciphertext_len) const {
if (!cipher) {
return -EIO;
}
- if (!EVP_EncryptInit_ex2(ctx, cipher, ke_raw, iv, params)) {
+ auto *uk = get_usage_keys(usage);
+ if (!uk) {
+ ldout(cct, 0) << "ERROR: usage keys is null, cannot encrypt" << dendl;
+ return -EIO;
+ }
+
+ if (!EVP_EncryptInit_ex2(ctx, cipher, uk->ke_raw, iv, params)) {
ldout(cct, 20) << "EVP_EncryptInit() failed" << dendl;
return -EIO;
}
}
using CryptoKeyHandler::encrypt_ext;
- using CryptoKeyHandler::decrypt;
+ using CryptoKeyHandler::decrypt_ext;
using CryptoKeyHandler::encrypt;
- int init(const ceph::bufferptr& s, uint32_t usage, ostringstream& err) {
+ int init(const ceph::bufferptr& s, const std::vector<uint32_t>& usages, ostringstream& err) {
cipher = EVP_CIPHER_fetch(NULL, "AES-256-CBC-CTS", NULL);
secret = s;
-
- int r = calc_kx(secret, usage,
- 0x55 /* Ki type */,
- AES256KRB5_HASH_LEN /* 192 bit */,
- ki,
- err);
- if (r < 0) {
- return r;
+ if (usages.size() > 0) {
+ default_usage = usages[0];
}
- ki_raw = reinterpret_cast<const unsigned char *>(ki.c_str()); /* needed so that we can use ki in const methods */
- r = calc_kx(secret, usage,
- 0xAA /* Ke type */,
- 32 /* 256 bit */,
- ke,
- err);
- if (r < 0) {
- return r;
+ for (auto usage : usages) {
+ auto *uk = init_usage_keys(usage);
+ if (!uk) {
+ return -EIO;
+ }
}
- ke_raw = reinterpret_cast<const unsigned char *>(ke.c_str()); /* same reason as with ki */
return 0;
}
- int encrypt_ext(CephContext *cct, const ceph::bufferlist& in,
+ int encrypt_ext(CephContext *cct, uint32_t usage,
+ const ceph::bufferlist& in,
const ceph::bufferlist *confounder,
ceph::bufferlist& out,
std::string* /* unused */) const override {
unsigned char iv[AES_BLOCK_LEN];
memset(iv, 0, sizeof(iv));
- int r = encrypt_AES256_CTS(cct, incopy, iv, sizeof(iv), (unsigned char *)aes_enc, aes_enc_len);
+ int r = encrypt_AES256_CTS(cct, incopy, iv, sizeof(iv), usage, (unsigned char *)aes_enc, aes_enc_len);
if (r < 0) {
return r;
}
char *hmac = out_tmp.c_str() + AES256KRB5_BLOCK_LEN + in.length();
+ auto *uk = get_usage_keys(usage);
+ if (!uk) {
+ ldout(cct, 0) << "ERROR: usage keys is null, cannot encrypt" << dendl;
+ return -EIO;
+ }
+
ostringstream err;
r = calc_hmac_sha384((const unsigned char *)aes_enc, aes_enc_len,
- ki_raw, ki.length(),
+ uk->ki_raw, uk->ki.length(),
iv, sizeof(iv),
hmac, AES256KRB5_HASH_LEN, err);
if (r < 0) {
return 0;
}
- int decrypt(CephContext *cct, const ceph::bufferlist& in,
- ceph::bufferlist& out,
- std::string* /* unused */) const override {
+ int decrypt_ext(CephContext *cct, uint32_t usage,
+ const ceph::bufferlist& in,
+ ceph::bufferlist& out,
+ std::string* /* unused */) const override {
ldout(cct, 20) << "CryptoAES256KRB5KeyHandler::decrypt()" << dendl;
if (in.length() < AES256KRB5_BLOCK_LEN + AES256KRB5_HASH_LEN) { /* minimum size: confounder + hmac */
unsigned char iv[AES_BLOCK_LEN];
memset(iv, 0, sizeof(iv));
+ auto *uk = get_usage_keys(usage);
+ if (!uk) {
+ ldout(cct, 0) << "ERROR: usage keys is null, cannot encrypt" << dendl;
+ return -EIO;
+ }
/* first need to compare hmac to calculated hmac */
char hmac[AES256KRB5_HASH_LEN];
ostringstream err;
int r = calc_hmac_sha384((const unsigned char *)indata.c_str(), indata.length(),
- ki_raw, ki.length(),
+ uk->ki_raw, uk->ki.length(),
iv, sizeof(iv),
hmac, sizeof(hmac), err);
if (r < 0) {
bufferptr tmp_out(indata.length());
r = decrypt_AES256_CTS(indata,
- ke_raw, iv, sizeof(iv),
+ uk->ke_raw, iv, sizeof(iv),
tmp_out);
if (r < 0) {
return r;
int encrypt(CephContext *cct, const ceph::bufferlist& in,
ceph::bufferlist& out,
std::string* unused) const override {
- return encrypt_ext(cct, in, nullptr, out, unused);
+ return encrypt_ext(cct, default_usage, in, nullptr, out, unused);
}
std::size_t enc_size(const in_slice_t& in,
}
CryptoKeyHandler *CryptoAES256KRB5::get_key_handler_ext(const bufferptr& secret,
- uint32_t usage,
+ const std::vector<uint32_t>& usages,
string& error)
{
CryptoAES256KRB5KeyHandler *ckh = new CryptoAES256KRB5KeyHandler;
ostringstream oss;
- if (ckh->init(secret, usage, oss) < 0) {
+ if (ckh->init(secret, usages, oss) < 0) {
error = oss.str();
delete ckh;
return NULL;
static_assert(BlockSizeT::value <= MAX_BLOCK_SIZE);
}
+ uint32_t default_usage{0};
+
virtual ~CryptoKeyHandler() {}
virtual int encrypt(CephContext *cct,
const ceph::buffer::list& in,
ceph::buffer::list& out, std::string *error) const {
- return encrypt_ext(cct, in, nullptr, out, error);
+ return encrypt_ext(cct, default_usage, in, nullptr, out, error);
+ }
+
+ virtual int encrypt_ext(CephContext *cct,
+ uint32_t usage,
+ const ceph::buffer::list& in,
+ ceph::buffer::list& out, std::string *error) const {
+ return encrypt_ext(cct, usage, in, nullptr, out, error);
}
/* should either used internally, or for unitests. Confounder should be nullptr otherwise */
virtual int encrypt_ext(CephContext *cct,
+ const ceph::buffer::list& in,
+ const ceph::buffer::list *confounder,
+ ceph::buffer::list& out, std::string *error) const {
+ return encrypt_ext(cct, default_usage, in, confounder, out, error);
+ }
+
+ virtual int encrypt_ext(CephContext *cct,
+ uint32_t usage,
const ceph::buffer::list& in,
const ceph::buffer::list *confounder,
ceph::buffer::list& out, std::string *error) const = 0;
virtual int decrypt(CephContext *cct,
const ceph::buffer::list& in,
- ceph::buffer::list& out, std::string *error) const = 0;
+ ceph::buffer::list& out, std::string *error) const {
+ return decrypt_ext(cct, default_usage, in, out, error);
+ }
+
+ virtual int decrypt_ext(CephContext *cct,
+ uint32_t usage,
+ const ceph::buffer::list& in,
+ ceph::buffer::list& out, std::string *error) const = 0;
virtual std::size_t enc_size(const in_slice_t& in,
const in_slice_t *confounder) const {
virtual std::size_t encrypt(CephContext *cct,
const in_slice_t& in,
const out_slice_t& out) const {
- return encrypt_ext(cct, in, nullptr, out);
+ return encrypt_ext(cct, default_usage, in, nullptr, out);
+ }
+ virtual std::size_t encrypt_ext(CephContext *cct,
+ uint32_t usage,
+ const in_slice_t& in,
+ const out_slice_t& out) const {
+ return encrypt_ext(cct, usage,
+ in, nullptr, out);
+ }
+ virtual std::size_t encrypt_ext(CephContext *cct,
+ const in_slice_t& in,
+ const in_slice_t *confounder,
+ const out_slice_t& out) const {
+ return encrypt_ext(cct, default_usage,
+ in, confounder, out);
}
virtual std::size_t encrypt_ext(CephContext *cct,
+ uint32_t usage,
const in_slice_t& in,
const in_slice_t *confounder,
const out_slice_t& out) const;
+
virtual std::size_t decrypt(CephContext *cct,
+ const in_slice_t& in,
+ const out_slice_t& out) const {
+ return decrypt_ext(cct, default_usage, in, out);
+ }
+ virtual std::size_t decrypt_ext(CephContext *cct,
+ uint32_t usage,
const in_slice_t& in,
const out_slice_t& out) const;
virtual int create(CryptoRandom *random, ceph::buffer::ptr& secret) = 0;
virtual int validate_secret(const ceph::buffer::ptr& secret) = 0;
virtual CryptoKeyHandler *get_key_handler_ext(const ceph::buffer::ptr& secret,
- uint32_t usage,
+ const std::vector<uint32_t>& usages,
std::string& error) = 0;
virtual CryptoKeyHandler *get_key_handler(const ceph::buffer::ptr& secret,
std::string& error) {
- return get_key_handler_ext(secret, 0, error);
+ return get_key_handler_ext(secret, {0}, error);
}
static CryptoHandler *create(int type);
bufferlist cipher;
std::string error;
- std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, t.usage, error));
- int r = kh->encrypt_ext(g_ceph_context, plaintext, &confounder, cipher, &error);
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, { t.usage }, error));
+ int r = kh->encrypt_ext(g_ceph_context, t.usage, plaintext, &confounder, cipher, &error);
ASSERT_EQ(r, 0);
ASSERT_EQ(error, "");
}
}
+TEST(AES256KRB5, EncryptUsage) {
+ for (int i = 0; !tv[i].secret.empty(); ++i) {
+ auto h = g_ceph_context->get_crypto_manager()->get_handler(CEPH_CRYPTO_AES256KRB5);
+
+ tvbl t(tv[i]);
+
+ auto& secret = t.secret;
+ auto& confounder = t.confounder;
+ auto& plaintext = t.plaintext;
+ auto& want_cipher = t.ciphertext;
+
+ bufferlist cipher;
+ std::string error;
+
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, { t.usage, t.usage + 1 }, error));
+ int r = kh->encrypt_ext(g_ceph_context, t.usage + 1, plaintext, &confounder, cipher, &error);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(error, "");
+
+ ASSERT_EQ(want_cipher.length(), cipher.length());
+
+ dump_buf("ENCRYPTED:", (unsigned char *)cipher.c_str(), cipher.length());
+ dump_buf("NOT EXPECTED:", (unsigned char *)want_cipher.c_str(), want_cipher.length());
+
+ int err;
+ err = memcmp(cipher.c_str(), want_cipher.c_str(), want_cipher.length());
+ ASSERT_NE(0, err);
+
+ /* use default usage */
+ cipher.clear();
+
+ r = kh->encrypt_ext(g_ceph_context, plaintext, &confounder, cipher, &error);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(error, "");
+
+ ASSERT_EQ(want_cipher.length(), cipher.length());
+
+ dump_buf("ENCRYPTED:", (unsigned char *)cipher.c_str(), cipher.length());
+ dump_buf("EXPECTED:", (unsigned char *)want_cipher.c_str(), want_cipher.length());
+
+ err = memcmp(cipher.c_str(), want_cipher.c_str(), want_cipher.length());
+ ASSERT_EQ(0, err);
+ }
+}
+
TEST(AES256KRB5, EncryptNoBl) {
for (int i = 0; !tv[i].secret.empty(); ++i) {
auto h = g_ceph_context->get_crypto_manager()->get_handler(CEPH_CRYPTO_AES256KRB5);
const CryptoKey::in_slice_t confounder_slice { confounder.length(), (const unsigned char *)confounder.c_str() };
std::string error;
- std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, t.usage, error));
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, { t.usage }, error));
const CryptoKey::in_slice_t plain_slice { plaintext.length(), (const unsigned char *)plaintext.c_str() };
std::string error;
bufferlist plaintext;
- std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, t.usage, error));
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, { t.usage }, error));
int r = kh->decrypt(g_ceph_context, ciphertext, plaintext, &error);
ASSERT_EQ(r, 0);
ASSERT_EQ(error, "");
}
}
+TEST(AES256KRB5, DecryptUsage) {
+ for (int i = 0; !tv[i].secret.empty(); ++i) {
+ auto h = g_ceph_context->get_crypto_manager()->get_handler(CEPH_CRYPTO_AES256KRB5);
+
+ tvbl t(tv[i]);
+
+ auto& secret = t.secret;
+ auto& want_plaintext = t.plaintext;
+ auto& ciphertext = t.ciphertext;
+
+ std::string error;
+ bufferlist plaintext;
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, { t.usage, t.usage + 1 }, error));
+
+ /* decrypt with the wrong usage should fail */
+ int r = kh->decrypt_ext(g_ceph_context, t.usage + 1, ciphertext, plaintext, &error);
+ ASSERT_NE(r, 0);
+
+ plaintext.clear();
+
+ /* correct usage this time */
+ r = kh->decrypt_ext(g_ceph_context, t.usage, ciphertext, plaintext, &error);
+ ASSERT_EQ(r, 0);
+ ASSERT_EQ(error, "");
+
+ dump_buf("DECRYPTED:", (unsigned char *)plaintext.c_str(), plaintext.length());
+ dump_buf("NOT EXPECTED:", (unsigned char *)want_plaintext.c_str(), want_plaintext.length());
+ ASSERT_EQ(want_plaintext.length(), plaintext.length());
+
+ int err = memcmp(plaintext.c_str(), want_plaintext.c_str(), plaintext.length());
+ ASSERT_EQ(0, err);
+ }
+}
+
TEST(AES256KRB5, DecryptNoBl) {
for (int i = 0; !tv[i].secret.empty(); ++i) {
auto h = g_ceph_context->get_crypto_manager()->get_handler(CEPH_CRYPTO_AES256KRB5);
unsigned char plaintext[plain_buf_size];
std::string error;
- std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, t.usage, error));
+ std::unique_ptr<CryptoKeyHandler> kh(h->get_key_handler_ext(secret, { t.usage }, error));
CryptoKey::in_slice_t cipher_slice { ciphertext.length(), (const unsigned char *)ciphertext.c_str() };
CryptoKey::out_slice_t plain_slice { plain_buf_size, plaintext };