+++ /dev/null
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2004-2009 Sage Weil <sage@newdream.net>
- *
- * 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 <sstream>
-#include "Crypto.h"
-#ifdef USE_CRYPTOPP
-# include <cryptopp/modes.h>
-# include <cryptopp/aes.h>
-# include <cryptopp/filters.h>
-#elif defined(USE_NSS)
-# include <nspr.h>
-# include <nss.h>
-# include <pk11pub.h>
-#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 <errno.h>
-
-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<bufferptr>::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<bufferptr>::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;
- }
-}