From d1b6096c79164f8a5830dee6b5f186c405cf73ab Mon Sep 17 00:00:00 2001 From: Sage Weil Date: Sun, 1 Feb 2015 15:05:54 -0800 Subject: [PATCH] auth: refactor crypto key context Cache all of the crypto key context in a new CryptoKeyHandler struct that is attached to CryptoKey. Signed-off-by: Sage Weil (cherry picked from commit 16b3515af3df439fba7d545587285b50e9814165) --- src/auth/Crypto.cc | 418 ++++++++++++++++--------------- src/auth/Crypto.h | 73 +++--- src/auth/cephx/CephxKeyServer.cc | 2 +- src/test/crypto.cc | 12 +- 4 files changed, 271 insertions(+), 234 deletions(-) diff --git a/src/auth/Crypto.cc b/src/auth/Crypto.cc index e63fc244702..b656d9d0cc2 100644 --- a/src/auth/Crypto.cc +++ b/src/auth/Crypto.cc @@ -62,8 +62,21 @@ uint64_t get_random(uint64_t min_val, uint64_t max_val) return r; } + // --------------------------------------------------- +class CryptoNoneKeyHandler : public CryptoKeyHandler { +public: + void encrypt(const bufferlist& in, + bufferlist& out, std::string &error) const { + out = in; + } + void decrypt(const bufferlist& in, + bufferlist& out, std::string &error) const { + out = in; + } +}; + class CryptoNone : public CryptoHandler { public: CryptoNone() { } @@ -71,39 +84,21 @@ public: int get_type() const { return CEPH_CRYPTO_NONE; } - int create(bufferptr& secret); - int validate_secret(bufferptr& secret); - void encrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const; - void decrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const; + int create(bufferptr& secret) { + return 0; + } + int validate_secret(const bufferptr& secret) { + return 0; + } + CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) { + return new CryptoNoneKeyHandler; + } }; -int CryptoNone::create(bufferptr& secret) -{ - return 0; -} - -int CryptoNone::validate_secret(bufferptr& secret) -{ - return 0; -} - -void CryptoNone::encrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const -{ - out = in; -} - -void CryptoNone::decrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const -{ - out = in; -} - // --------------------------------------------------- + class CryptoAES : public CryptoHandler { public: CryptoAES() { } @@ -112,139 +107,199 @@ public: return CEPH_CRYPTO_AES; } int create(bufferptr& secret); - int validate_secret(bufferptr& secret); - void encrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const; - void decrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const; + int validate_secret(const bufferptr& secret); + CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error); }; - #ifdef USE_CRYPTOPP # define AES_KEY_LEN ((size_t)CryptoPP::AES::DEFAULT_KEYLENGTH) # define AES_BLOCK_LEN ((size_t)CryptoPP::AES::BLOCKSIZE) + +class CryptoAESKeyHandler : public CryptoKeyHandler { +public: + int init(const bufferptr& s, ostringstream& err) { + secret = s; + return 0; + } + + void encrypt(const bufferlist& in, + bufferlist& out, std::string &error) const { + const unsigned char *key = (const unsigned char *)secret.c_str(); + + string ciphertext; + CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH); + CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( + aesEncryption, (const byte*)CEPH_AES_IV); + CryptoPP::StringSink *sink = new CryptoPP::StringSink(ciphertext); + CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, sink); + + for (std::list::const_iterator it = in.buffers().begin(); + it != in.buffers().end(); ++it) { + const unsigned char *in_buf = (const unsigned char *)it->c_str(); + stfEncryptor.Put(in_buf, it->length()); + } + try { + stfEncryptor.MessageEnd(); + } catch (CryptoPP::Exception& e) { + ostringstream oss; + oss << "encryptor.MessageEnd::Exception: " << e.GetWhat(); + error = oss.str(); + return; + } + out.append((const char *)ciphertext.c_str(), ciphertext.length()); + } + + void decrypt(const bufferlist& in, + bufferlist& out, std::string &error) const { + const unsigned char *key = (const unsigned char *)secret.c_str(); + + CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH); + CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( + aesDecryption, (const byte*)CEPH_AES_IV ); + + string decryptedtext; + CryptoPP::StringSink *sink = new CryptoPP::StringSink(decryptedtext); + CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, sink); + for (std::list::const_iterator it = in.buffers().begin(); + it != in.buffers().end(); ++it) { + const unsigned char *in_buf = (const unsigned char *)it->c_str(); + stfDecryptor.Put(in_buf, it->length()); + } + + try { + stfDecryptor.MessageEnd(); + } catch (CryptoPP::Exception& e) { + ostringstream oss; + oss << "decryptor.MessageEnd::Exception: " << e.GetWhat(); + error = oss.str(); + return; + } + + out.append((const char *)decryptedtext.c_str(), decryptedtext.length()); + } +}; + #elif USE_NSS // when we say AES, we mean AES-128 # define AES_KEY_LEN 16 # define AES_BLOCK_LEN 16 -static void nss_aes_operation(CK_ATTRIBUTE_TYPE op, const bufferptr& secret, - const bufferlist& in, bufferlist& out, std::string &error) +static void nss_aes_operation(CK_ATTRIBUTE_TYPE op, + const bufferptr& secret, + const bufferlist& in, bufferlist& out, + std::string& error) { - const CK_MECHANISM_TYPE mechanism = CKM_AES_CBC_PAD; - - // sample source said this has to be at least size of input + 8, - // but i see 15 still fail with SEC_ERROR_OUTPUT_LEN - bufferptr out_tmp(in.length()+16); - - bufferlist incopy; - + CK_MECHANISM_TYPE mechanism = CKM_AES_CBC_PAD; PK11SlotInfo *slot; + PK11SymKey *key; + SECItem *param; slot = PK11_GetBestSlot(mechanism, NULL); if (!slot) { - ostringstream oss; - oss << "cannot find NSS slot to use: " << PR_GetError(); - error = oss.str(); - goto err; + ostringstream err; + err << "cannot find NSS slot to use: " << PR_GetError(); + error = err.str(); + return; } SECItem keyItem; - keyItem.type = siBuffer; keyItem.data = (unsigned char*)secret.c_str(); keyItem.len = secret.length(); - - PK11SymKey *key; - key = PK11_ImportSymKey(slot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, &keyItem, NULL); if (!key) { - ostringstream oss; - oss << "cannot convert AES key for NSS: " << PR_GetError(); - error = oss.str(); - goto err_slot; + ostringstream err; + err << "cannot convert AES key for NSS: " << PR_GetError(); + error = err.str(); + return; } SECItem ivItem; - ivItem.type = siBuffer; // losing constness due to SECItem.data; IV should never be // modified, regardless ivItem.data = (unsigned char*)CEPH_AES_IV; ivItem.len = sizeof(CEPH_AES_IV); - SECItem *param; - param = PK11_ParamFromIV(mechanism, &ivItem); if (!param) { - ostringstream oss; - oss << "cannot set NSS IV param: " << PR_GetError(); - error = oss.str(); - goto err_key; + ostringstream err; + err << "cannot set NSS IV param: " << PR_GetError(); + error = err.str(); + return; } - PK11Context *ctx; - - ctx = PK11_CreateContextBySymKey(mechanism, op, key, param); - if (!ctx) { - ostringstream oss; - oss << "cannot create NSS context: " << PR_GetError(); - error = oss.str(); - goto err_param; - } + // sample source said this has to be at least size of input + 8, + // but i see 15 still fail with SEC_ERROR_OUTPUT_LEN + bufferptr out_tmp(in.length()+16); + bufferlist incopy; SECStatus ret; int written; unsigned char *in_buf; + PK11Context *ectx; + ectx = PK11_CreateContextBySymKey(mechanism, op, key, param); + assert(ectx); + incopy = in; // it's a shallow copy! in_buf = (unsigned char*)incopy.c_str(); - ret = PK11_CipherOp(ctx, + ret = PK11_CipherOp(ectx, (unsigned char*)out_tmp.c_str(), &written, out_tmp.length(), in_buf, in.length()); if (ret != SECSuccess) { ostringstream oss; oss << "NSS AES failed: " << PR_GetError(); error = oss.str(); - goto err_op; + return; } unsigned int written2; - ret = PK11_DigestFinal(ctx, (unsigned char*)out_tmp.c_str()+written, &written2, + ret = PK11_DigestFinal(ectx, + (unsigned char*)out_tmp.c_str()+written, &written2, out_tmp.length()-written); if (ret != SECSuccess) { ostringstream oss; oss << "NSS AES final round failed: " << PR_GetError(); error = oss.str(); - goto err_op; + return; } - out_tmp.set_length(written + written2); - out.append(out_tmp); - - PK11_DestroyContext(ctx, PR_TRUE); + PK11_DestroyContext(ectx, PR_TRUE); SECITEM_FreeItem(param, PR_TRUE); PK11_FreeSymKey(key); PK11_FreeSlot(slot); - return; - err_op: - PK11_DestroyContext(ctx, PR_TRUE); - err_param: - SECITEM_FreeItem(param, PR_TRUE); - err_key: - PK11_FreeSymKey(key); - err_slot: - PK11_FreeSlot(slot); - err: - ; + out_tmp.set_length(written + written2); + out.append(out_tmp); } +class CryptoAESKeyHandler : public CryptoKeyHandler { +public: + int init(const bufferptr& s, ostringstream& err) { + secret = s; + return 0; + } + + void encrypt(const bufferlist& in, + bufferlist& out, std::string &error) const { + nss_aes_operation(CKA_ENCRYPT, secret, in, out, error); + } + void decrypt(const bufferlist& in, + bufferlist& out, std::string &error) const { + nss_aes_operation(CKA_DECRYPT, secret, in, out, error); + } +}; + #else # error "No supported crypto implementation found." #endif + + +// ------------------------------------------------------------ + int CryptoAES::create(bufferptr& secret) { bufferlist bl; @@ -255,7 +310,7 @@ int CryptoAES::create(bufferptr& secret) return 0; } -int CryptoAES::validate_secret(bufferptr& secret) +int CryptoAES::validate_secret(const bufferptr& secret) { if (secret.length() < (size_t)AES_KEY_LEN) { return -EINVAL; @@ -264,140 +319,103 @@ int CryptoAES::validate_secret(bufferptr& secret) return 0; } -void CryptoAES::encrypt(const bufferptr& secret, const bufferlist& in, bufferlist& out, - std::string &error) const +CryptoKeyHandler *CryptoAES::get_key_handler(const bufferptr& secret, + string& error) { - if (secret.length() < AES_KEY_LEN) { - error = "key is too short"; - return; - } -#ifdef USE_CRYPTOPP - { - const unsigned char *key = (const unsigned char *)secret.c_str(); - - string ciphertext; - CryptoPP::AES::Encryption aesEncryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH); - CryptoPP::CBC_Mode_ExternalCipher::Encryption cbcEncryption( aesEncryption, (const byte*)CEPH_AES_IV ); - CryptoPP::StringSink *sink = new CryptoPP::StringSink(ciphertext); - CryptoPP::StreamTransformationFilter stfEncryptor(cbcEncryption, sink); - - for (std::list::const_iterator it = in.buffers().begin(); - it != in.buffers().end(); ++it) { - const unsigned char *in_buf = (const unsigned char *)it->c_str(); - stfEncryptor.Put(in_buf, it->length()); - } - try { - stfEncryptor.MessageEnd(); - } catch (CryptoPP::Exception& e) { - ostringstream oss; - oss << "encryptor.MessageEnd::Exception: " << e.GetWhat(); - error = oss.str(); - return; - } - out.append((const char *)ciphertext.c_str(), ciphertext.length()); + CryptoAESKeyHandler *ckh = new CryptoAESKeyHandler; + ostringstream oss; + if (ckh->init(secret, oss) < 0) { + error = oss.str(); + return NULL; } -#elif USE_NSS - nss_aes_operation(CKA_ENCRYPT, secret, in, out, error); -#else -# error "No supported crypto implementation found." -#endif + return ckh; } -void CryptoAES::decrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const -{ -#ifdef USE_CRYPTOPP - const unsigned char *key = (const unsigned char *)secret.c_str(); - - CryptoPP::AES::Decryption aesDecryption(key, CryptoPP::AES::DEFAULT_KEYLENGTH); - CryptoPP::CBC_Mode_ExternalCipher::Decryption cbcDecryption( aesDecryption, (const byte*)CEPH_AES_IV ); - string decryptedtext; - CryptoPP::StringSink *sink = new CryptoPP::StringSink(decryptedtext); - CryptoPP::StreamTransformationFilter stfDecryptor(cbcDecryption, sink); - for (std::list::const_iterator it = in.buffers().begin(); - it != in.buffers().end(); ++it) { - const unsigned char *in_buf = (const unsigned char *)it->c_str(); - stfDecryptor.Put(in_buf, it->length()); - } - try { - stfDecryptor.MessageEnd(); - } catch (CryptoPP::Exception& e) { - ostringstream oss; - oss << "decryptor.MessageEnd::Exception: " << e.GetWhat(); - error = oss.str(); - return; - } - out.append((const char *)decryptedtext.c_str(), decryptedtext.length()); -#elif USE_NSS - nss_aes_operation(CKA_DECRYPT, secret, in, out, error); -#else -# error "No supported crypto implementation found." -#endif -} +// -- // --------------------------------------------------- -int CryptoKey::set_secret(CephContext *cct, int type, bufferptr& s) -{ - this->type = type; - created = ceph_clock_now(cct); - CryptoHandler *h = cct->get_crypto_handler(type); - if (!h) { - lderr(cct) << "ERROR: cct->get_crypto_handler(type=" << type << ") returned NULL" << dendl; - return -EOPNOTSUPP; - } - int ret = h->validate_secret(s); - - if (ret < 0) - return ret; +void CryptoKey::encode(bufferlist& bl) const +{ + ::encode(type, bl); + ::encode(created, bl); + __u16 len = secret.length(); + ::encode(len, bl); + bl.append(secret); +} - secret = s; +void CryptoKey::decode(bufferlist::iterator& bl) +{ + ::decode(type, bl); + ::decode(created, bl); + __u16 len; + ::decode(len, bl); + bufferptr tmp; + bl.copy(len, tmp); + if (_set_secret(type, tmp) < 0) + throw buffer::malformed_input("malformed secret"); +} +int CryptoKey::set_secret(int type, const bufferptr& s, utime_t c) +{ + int r = _set_secret(type, s); + if (r < 0) + return r; + this->created = c; return 0; } -int CryptoKey::create(CephContext *cct, int t) +int CryptoKey::_set_secret(int t, const bufferptr& s) { - type = t; - created = ceph_clock_now(cct); - - CryptoHandler *h = cct->get_crypto_handler(type); - if (!h) { - lderr(cct) << "ERROR: cct->get_crypto_handler(type=" << type << ") returned NULL" << dendl; - return -EOPNOTSUPP; + if (s.length() == 0) { + secret = s; + ckh.reset(); + return 0; } - return h->create(secret); -} -void CryptoKey::encrypt(CephContext *cct, const bufferlist& in, bufferlist& out, std::string &error) const -{ - if (!ch || ch->get_type() != type) { - ch = cct->get_crypto_handler(type); - if (!ch) { - ostringstream oss; - oss << "CryptoKey::encrypt: key type " << type << " not supported."; - return; + CryptoHandler *ch = CryptoHandler::create(t); + if (ch) { + int ret = ch->validate_secret(s); + if (ret < 0) { + delete ch; + return ret; + } + string error; + ckh.reset(ch->get_key_handler(s, error)); + delete ch; + if (error.length()) { + return -EIO; } } - ch->encrypt(this->secret, in, out, error); + type = t; + secret = s; + return 0; } -void CryptoKey::decrypt(CephContext *cct, const bufferlist& in, bufferlist& out, std::string &error) const +int CryptoKey::create(CephContext *cct, int t) { - if (!ch || ch->get_type() != type) { - ch = cct->get_crypto_handler(type); - if (!ch) { - ostringstream oss; - oss << "CryptoKey::decrypt: key type " << type << " not supported."; - return; - } + CryptoHandler *ch = CryptoHandler::create(t); + if (!ch) { + if (cct) + lderr(cct) << "ERROR: cct->get_crypto_handler(type=" << t << ") returned NULL" << dendl; + return -EOPNOTSUPP; } - ch->decrypt(this->secret, in, out, error); + bufferptr s; + int r = ch->create(s); + delete ch; + if (r < 0) + return r; + + r = _set_secret(t, s); + if (r < 0) + return r; + created = ceph_clock_now(cct); + return r; } void CryptoKey::print(std::ostream &out) const diff --git a/src/auth/Crypto.h b/src/auth/Crypto.h index 1356b61f6c1..97b796165b6 100644 --- a/src/auth/Crypto.h +++ b/src/auth/Crypto.h @@ -17,6 +17,7 @@ #include "include/types.h" #include "include/utime.h" +#include "include/memory.h" #include "common/Formatter.h" #include "include/buffer.h" @@ -25,6 +26,22 @@ class CephContext; class CryptoHandler; +class CryptoKeyContext; + +/* + * some per-key context that is specific to a particular crypto backend + */ +class CryptoKeyHandler { +public: + bufferptr secret; + + virtual ~CryptoKeyHandler() {} + + virtual void encrypt(const bufferlist& in, + bufferlist& out, std::string &error) const = 0; + virtual void decrypt(const bufferlist& in, + bufferlist& out, std::string &error) const = 0; +}; /* * match encoding of struct ceph_secret @@ -33,38 +50,32 @@ class CryptoKey { protected: __u16 type; utime_t created; - bufferptr secret; + bufferptr secret; // must set this via set_secret()! + + // cache a pointer to the implementation-specific key handler, so we + // don't have to create it for every crypto operation. + mutable ceph::shared_ptr ckh; - // cache a pointer to the handler, so we don't have to look it up - // for each crypto operation - mutable CryptoHandler *ch; + int _set_secret(int type, const bufferptr& s); public: - CryptoKey() : type(0), ch(NULL) { } - CryptoKey(int t, utime_t c, bufferptr& s) : type(t), created(c), secret(s), ch(NULL) { } - - void encode(bufferlist& bl) const { - ::encode(type, bl); - ::encode(created, bl); - __u16 len = secret.length(); - ::encode(len, bl); - bl.append(secret); + CryptoKey() : type(0) { } + CryptoKey(int t, utime_t c, bufferptr& s) + : created(c) { + _set_secret(t, s); } - void decode(bufferlist::iterator& bl) { - ::decode(type, bl); - ::decode(created, bl); - __u16 len; - ::decode(len, bl); - bl.copy(len, secret); - secret.c_str(); // make sure it's a single buffer! + ~CryptoKey() { } + void encode(bufferlist& bl) const; + void decode(bufferlist::iterator& bl); + int get_type() const { return type; } utime_t get_created() const { return created; } void print(std::ostream& out) const; - int set_secret(CephContext *cct, int type, bufferptr& s); - bufferptr& get_secret() { return secret; } + int set_secret(int type, const bufferptr& s, utime_t created); + const bufferptr& get_secret() { return secret; } const bufferptr& get_secret() const { return secret; } void encode_base64(string& s) const { @@ -94,8 +105,14 @@ public: // -- int create(CephContext *cct, int type); - void encrypt(CephContext *cct, const bufferlist& in, bufferlist& out, std::string &error) const; - void decrypt(CephContext *cct, const bufferlist& in, bufferlist& out, std::string &error) const; + void encrypt(CephContext *cct, const bufferlist& in, bufferlist& out, + std::string &error) const { + ckh->encrypt(in, out, error); + } + void decrypt(CephContext *cct, const bufferlist& in, bufferlist& out, + std::string &error) const { + ckh->decrypt(in, out, error); + } void to_str(std::string& s) const; }; @@ -119,11 +136,9 @@ public: virtual ~CryptoHandler() {} virtual int get_type() const = 0; virtual int create(bufferptr& secret) = 0; - virtual int validate_secret(bufferptr& secret) = 0; - virtual void encrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const = 0; - virtual void decrypt(const bufferptr& secret, const bufferlist& in, - bufferlist& out, std::string &error) const = 0; + virtual int validate_secret(const bufferptr& secret) = 0; + virtual CryptoKeyHandler *get_key_handler(const bufferptr& secret, + string& error) = 0; static CryptoHandler *create(int type); }; diff --git a/src/auth/cephx/CephxKeyServer.cc b/src/auth/cephx/CephxKeyServer.cc index b2c0c672aed..81c0a66b679 100644 --- a/src/auth/cephx/CephxKeyServer.cc +++ b/src/auth/cephx/CephxKeyServer.cc @@ -268,7 +268,7 @@ bool KeyServer::generate_secret(CryptoKey& secret) if (crypto->create(bp) < 0) return false; - secret.set_secret(cct, CEPH_CRYPTO_AES, bp); + secret.set_secret(CEPH_CRYPTO_AES, bp, ceph_clock_now(NULL)); return true; } diff --git a/src/test/crypto.cc b/src/test/crypto.cc index 24d5c5a475d..be946211734 100644 --- a/src/test/crypto.cc +++ b/src/test/crypto.cc @@ -52,7 +52,8 @@ TEST(AES, Encrypt) { bufferlist cipher; std::string error; - h->encrypt(secret, plaintext, cipher, error); + CryptoKeyHandler *kh = h->get_key_handler(secret, error); + kh->encrypt(plaintext, cipher, error); ASSERT_EQ(error, ""); unsigned char want_cipher[] = { @@ -96,7 +97,8 @@ TEST(AES, Decrypt) { std::string error; bufferlist plaintext; - h->decrypt(secret, cipher, plaintext, error); + CryptoKeyHandler *kh = h->get_key_handler(secret, error); + kh->decrypt(cipher, plaintext, error); ASSERT_EQ(error, ""); ASSERT_EQ(sizeof(plaintext_s), plaintext.length()); @@ -128,7 +130,8 @@ TEST(AES, Loop) { CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES); std::string error; - h->encrypt(secret, plaintext, cipher, error); + CryptoKeyHandler *kh = h->get_key_handler(secret, error); + kh->encrypt(plaintext, cipher, error); ASSERT_EQ(error, ""); } plaintext.clear(); @@ -136,7 +139,8 @@ TEST(AES, Loop) { { CryptoHandler *h = g_ceph_context->get_crypto_handler(CEPH_CRYPTO_AES); std::string error; - h->decrypt(secret, cipher, plaintext, error); + CryptoKeyHandler *ckh = h->get_key_handler(secret, error); + ckh->decrypt(cipher, plaintext, error); ASSERT_EQ(error, ""); } } -- 2.47.3