X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=src%2Fceph%2Fsrc%2Fauth%2FCrypto.cc;fp=src%2Fceph%2Fsrc%2Fauth%2FCrypto.cc;h=0186b7b2255d670a218a8883751472164be403b9;hb=812ff6ca9fcd3e629e49d4328905f33eee8ca3f5;hp=0000000000000000000000000000000000000000;hpb=15280273faafb77777eab341909a3f495cf248d9;p=stor4nfv.git diff --git a/src/ceph/src/auth/Crypto.cc b/src/ceph/src/auth/Crypto.cc new file mode 100644 index 0000000..0186b7b --- /dev/null +++ b/src/ceph/src/auth/Crypto.cc @@ -0,0 +1,492 @@ +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2004-2009 Sage Weil + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include +#include "Crypto.h" +#ifdef USE_CRYPTOPP +# include +# include +# include +#elif defined(USE_NSS) +# include +# include +# include +#endif + +#include "include/assert.h" +#include "common/Clock.h" +#include "common/armor.h" +#include "common/ceph_crypto.h" +#include "common/hex.h" +#include "common/safe_io.h" +#include "include/ceph_fs.h" +#include "include/compat.h" +#include "common/Formatter.h" +#include "common/debug.h" +#include + +int get_random_bytes(char *buf, int len) +{ + int fd = TEMP_FAILURE_RETRY(::open("/dev/urandom", O_RDONLY)); + if (fd < 0) + return -errno; + int ret = safe_read_exact(fd, buf, len); + VOID_TEMP_FAILURE_RETRY(::close(fd)); + return ret; +} + +static int get_random_bytes(int len, bufferlist& bl) +{ + char buf[len]; + get_random_bytes(buf, len); + bl.append(buf, len); + return 0; +} + +uint64_t get_random(uint64_t min_val, uint64_t max_val) +{ + uint64_t r; + get_random_bytes((char *)&r, sizeof(r)); + r = min_val + r % (max_val - min_val + 1); + return r; +} + + +// --------------------------------------------------- + +class CryptoNoneKeyHandler : public CryptoKeyHandler { +public: + int encrypt(const bufferlist& in, + bufferlist& out, std::string *error) const override { + out = in; + return 0; + } + int decrypt(const bufferlist& in, + bufferlist& out, std::string *error) const override { + out = in; + return 0; + } +}; + +class CryptoNone : public CryptoHandler { +public: + CryptoNone() { } + ~CryptoNone() override {} + int get_type() const override { + return CEPH_CRYPTO_NONE; + } + int create(bufferptr& secret) override { + return 0; + } + int validate_secret(const bufferptr& secret) override { + return 0; + } + CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) override { + return new CryptoNoneKeyHandler; + } +}; + + +// --------------------------------------------------- + + +class CryptoAES : public CryptoHandler { +public: + CryptoAES() { } + ~CryptoAES() override {} + int get_type() const override { + return CEPH_CRYPTO_AES; + } + int create(bufferptr& secret) override; + int validate_secret(const bufferptr& secret) override; + CryptoKeyHandler *get_key_handler(const bufferptr& secret, string& error) override; +}; + +#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: + CryptoPP::AES::Encryption *enc_key; + CryptoPP::AES::Decryption *dec_key; + + CryptoAESKeyHandler() + : enc_key(NULL), + dec_key(NULL) {} + ~CryptoAESKeyHandler() { + delete enc_key; + delete dec_key; + } + + int init(const bufferptr& s, ostringstream& err) { + secret = s; + + enc_key = new CryptoPP::AES::Encryption( + (byte*)secret.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH); + dec_key = new CryptoPP::AES::Decryption( + (byte*)secret.c_str(), CryptoPP::AES::DEFAULT_KEYLENGTH); + + return 0; + } + + int encrypt(const bufferlist& in, + bufferlist& out, std::string *error) const { + string ciphertext; + CryptoPP::StringSink *sink = new CryptoPP::StringSink(ciphertext); + CryptoPP::CBC_Mode_ExternalCipher::Encryption cbc( + *enc_key, (const byte*)CEPH_AES_IV); + CryptoPP::StreamTransformationFilter stfEncryptor(cbc, 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) { + if (error) { + ostringstream oss; + oss << "encryptor.MessageEnd::Exception: " << e.GetWhat(); + *error = oss.str(); + } + return -1; + } + out.append((const char *)ciphertext.c_str(), ciphertext.length()); + return 0; + } + + int decrypt(const bufferlist& in, + bufferlist& out, std::string *error) const { + string decryptedtext; + CryptoPP::StringSink *sink = new CryptoPP::StringSink(decryptedtext); + CryptoPP::CBC_Mode_ExternalCipher::Decryption cbc( + *dec_key, (const byte*)CEPH_AES_IV ); + CryptoPP::StreamTransformationFilter stfDecryptor(cbc, 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) { + if (error) { + ostringstream oss; + oss << "decryptor.MessageEnd::Exception: " << e.GetWhat(); + *error = oss.str(); + } + return -1; + } + + out.append((const char *)decryptedtext.c_str(), decryptedtext.length()); + return 0; + } +}; + +#elif defined(USE_NSS) +// when we say AES, we mean AES-128 +# define AES_KEY_LEN 16 +# define AES_BLOCK_LEN 16 + +static int nss_aes_operation(CK_ATTRIBUTE_TYPE op, + CK_MECHANISM_TYPE mechanism, + PK11SymKey *key, + SECItem *param, + const bufferlist& in, bufferlist& out, + std::string *error) +{ + // 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(ectx, + (unsigned char*)out_tmp.c_str(), &written, out_tmp.length(), + in_buf, in.length()); + if (ret != SECSuccess) { + PK11_DestroyContext(ectx, PR_TRUE); + if (error) { + ostringstream oss; + oss << "NSS AES failed: " << PR_GetError(); + *error = oss.str(); + } + return -1; + } + + unsigned int written2; + ret = PK11_DigestFinal(ectx, + (unsigned char*)out_tmp.c_str()+written, &written2, + out_tmp.length()-written); + PK11_DestroyContext(ectx, PR_TRUE); + if (ret != SECSuccess) { + if (error) { + ostringstream oss; + oss << "NSS AES final round failed: " << PR_GetError(); + *error = oss.str(); + } + return -1; + } + + out_tmp.set_length(written + written2); + out.append(out_tmp); + return 0; +} + +class CryptoAESKeyHandler : public CryptoKeyHandler { + CK_MECHANISM_TYPE mechanism; + PK11SlotInfo *slot; + PK11SymKey *key; + SECItem *param; + +public: + CryptoAESKeyHandler() + : mechanism(CKM_AES_CBC_PAD), + slot(NULL), + key(NULL), + param(NULL) {} + ~CryptoAESKeyHandler() override { + SECITEM_FreeItem(param, PR_TRUE); + if (key) + PK11_FreeSymKey(key); + if (slot) + PK11_FreeSlot(slot); + } + + int init(const bufferptr& s, ostringstream& err) { + secret = s; + + slot = PK11_GetBestSlot(mechanism, NULL); + if (!slot) { + err << "cannot find NSS slot to use: " << PR_GetError(); + return -1; + } + + SECItem keyItem; + keyItem.type = siBuffer; + keyItem.data = (unsigned char*)secret.c_str(); + keyItem.len = secret.length(); + key = PK11_ImportSymKey(slot, mechanism, PK11_OriginUnwrap, CKA_ENCRYPT, + &keyItem, NULL); + if (!key) { + err << "cannot convert AES key for NSS: " << PR_GetError(); + return -1; + } + + 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); + + param = PK11_ParamFromIV(mechanism, &ivItem); + if (!param) { + err << "cannot set NSS IV param: " << PR_GetError(); + return -1; + } + + return 0; + } + + int encrypt(const bufferlist& in, + bufferlist& out, std::string *error) const override { + return nss_aes_operation(CKA_ENCRYPT, mechanism, key, param, in, out, error); + } + int decrypt(const bufferlist& in, + bufferlist& out, std::string *error) const override { + return nss_aes_operation(CKA_DECRYPT, mechanism, key, param, in, out, error); + } +}; + +#else +# error "No supported crypto implementation found." +#endif + + + +// ------------------------------------------------------------ + +int CryptoAES::create(bufferptr& secret) +{ + bufferlist bl; + int r = get_random_bytes(AES_KEY_LEN, bl); + if (r < 0) + return r; + secret = buffer::ptr(bl.c_str(), bl.length()); + return 0; +} + +int CryptoAES::validate_secret(const bufferptr& secret) +{ + if (secret.length() < (size_t)AES_KEY_LEN) { + return -EINVAL; + } + + return 0; +} + +CryptoKeyHandler *CryptoAES::get_key_handler(const bufferptr& secret, + string& error) +{ + CryptoAESKeyHandler *ckh = new CryptoAESKeyHandler; + ostringstream oss; + if (ckh->init(secret, oss) < 0) { + error = oss.str(); + delete ckh; + return NULL; + } + return ckh; +} + + + + +// -- + + +// --------------------------------------------------- + + +void CryptoKey::encode(bufferlist& bl) const +{ + ::encode(type, bl); + ::encode(created, bl); + __u16 len = secret.length(); + ::encode(len, bl); + bl.append(secret); +} + +void CryptoKey::decode(bufferlist::iterator& bl) +{ + ::decode(type, bl); + ::decode(created, bl); + __u16 len; + ::decode(len, bl); + bufferptr tmp; + bl.copy_deep(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::_set_secret(int t, const bufferptr& s) +{ + if (s.length() == 0) { + secret = s; + ckh.reset(); + return 0; + } + + 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; + } + } else { + return -EOPNOTSUPP; + } + type = t; + secret = s; + return 0; +} + +int CryptoKey::create(CephContext *cct, int t) +{ + CryptoHandler *ch = CryptoHandler::create(t); + if (!ch) { + if (cct) + lderr(cct) << "ERROR: cct->get_crypto_handler(type=" << t << ") returned NULL" << dendl; + return -EOPNOTSUPP; + } + 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(); + return r; +} + +void CryptoKey::print(std::ostream &out) const +{ + out << encode_base64(); +} + +void CryptoKey::to_str(std::string& s) const +{ + int len = secret.length() * 4; + char buf[len]; + hex2str(secret.c_str(), secret.length(), buf, len); + s = buf; +} + +void CryptoKey::encode_formatted(string label, Formatter *f, bufferlist &bl) +{ + f->open_object_section(label.c_str()); + f->dump_string("key", encode_base64()); + f->close_section(); + f->flush(bl); +} + +void CryptoKey::encode_plaintext(bufferlist &bl) +{ + bl.append(encode_base64()); +} + + +// ------------------ + +CryptoHandler *CryptoHandler::create(int type) +{ + switch (type) { + case CEPH_CRYPTO_NONE: + return new CryptoNone; + case CEPH_CRYPTO_AES: + return new CryptoAES; + default: + return NULL; + } +}