Files
spark/src/lib/utils/crypto/PrivateKey.cpp
T

297 lines
9.0 KiB
C++

#ifndef _WIN32
#include "PrivateKey.h"
#include "CryptoInit.h"
#include "PublicKey.h"
#include "iostreambio.h"
#include "lib/logging/logging.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/helper_macros.h"
#include <cstring>
#include <tbb/spin_mutex.h>
namespace openssl {
#include <openssl/bio.h>
#include <openssl/bn.h>
#include <openssl/evp.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
} // namespace openssl
namespace bts {
namespace crypto {
struct PrivateKey::Pimp {
BTS_NO_COPY(Pimp);
const PasswordCallback *password;
PasswordCallback default_password;
PublicKeyPtr public_key;
Pimp(const PasswordCallback *pass)
: password(pass), default_password(), public_key() {
initialize();
if (!password)
password = &default_password;
}
~Pimp() {}
static void initialize() { CryptoInit::initialize(); }
};
int password_callback(char *buf, int size, int rwflag, void *u) {
UNUSED(rwflag);
std::string data;
if (u) {
PasswordCallback *pwd = reinterpret_cast<PasswordCallback *>(u);
pwd->request_password(data, data);
} else {
PasswordCallback ddefault;
ddefault.request_password(data, data);
}
int sz = data.size();
if ((int)data.size() > size)
sz = size;
std::memcpy(buf, data.c_str(), sz);
return sz;
}
const PublicKeyPtr PrivateKey::public_key() const {
if (!me->public_key)
me->public_key = PublicKey::create(*this);
return me->public_key;
}
const PrivateKeyPtr
PrivateKey::create(int bits, AlgoType::type at, const PasswordCallback *pass) {
openssl::EVP_PKEY *key = openssl::EVP_PKEY_new();
if (at == AlgoType::RSA) {
openssl::RSA *rsa = openssl::RSA_new();
openssl::BIGNUM *bn = openssl::BN_new();
openssl::BN_set_word(bn, RSA_F4);
int keyok = openssl::RSA_generate_key_ex(rsa, bits, bn, 0);
openssl::BN_free(bn);
bts_assert_msg(keyok != -1, "Unable to create key");
int i = openssl::EVP_PKEY_assign_RSA(key, rsa);
bts_assert_msg(1 == i, "Unable to create key");
} else
bts_assert_msg(false, "only support RSA");
return PrivateKeyPtr(new PrivateKey(key, pass));
}
const PrivateKeyPtr PrivateKey::read_file(
const std::string &filename, const PasswordCallback *pass) {
PrivateKey::Pimp::initialize();
PrivateKeyPtr ret;
openssl::BIO *bio = openssl::BIO_new_file(filename.c_str(), "r");
bts_assert_msg(bio, "can't load keyfile");
openssl::EVP_PKEY *key = openssl::PEM_read_bio_PrivateKey(
bio, 0, password_callback, (void *)pass);
if (!key)
LOG(Gen, Error, "Invalid private key passphrase");
else
ret.reset(new PrivateKey(key, pass));
return ret;
}
const PrivateKeyPtr PrivateKey::read_stream(
const std::istream &stream, const PasswordCallback *pass) {
PrivateKey::Pimp::initialize();
PrivateKeyPtr ret;
const_cast<std::istream &>(stream).seekg(0, std::ios::end);
int length = const_cast<std::istream &>(stream).tellg();
const_cast<std::istream &>(stream).seekg(0, std::ios::beg);
char *data = new char[length];
const_cast<std::istream &>(stream).read(data, length);
openssl::BIO *bio = openssl::BIO_new(openssl::BIO_s_mem());
BIO_write(bio, data, length);
delete[] data;
openssl::EVP_PKEY *key = openssl::PEM_read_bio_PrivateKey(
bio, 0, password_callback, (void *)pass);
if (!key)
LOG(Gen, Error, "Invalid private key passphrase");
else
ret.reset(new PrivateKey(key, pass));
return ret;
}
PrivateKey::PrivateKey(openssl::EVP_PKEY *key, const PasswordCallback *pass)
: me(new Pimp(pass)), _key(key) {}
PrivateKey::~PrivateKey() {
if (_key)
EVP_PKEY_free(_key);
}
typedef int encrypt_decrypt_init(openssl::EVP_PKEY_CTX *ctx);
typedef int encrypt_decrypt(
openssl::EVP_PKEY_CTX *ctx,
unsigned char *out,
size_t *outlen,
const unsigned char *in,
size_t inlen);
bool encrypt_decrypt_function(
const std::iostream &in_dat,
std::iostream &out_data,
encrypt_decrypt_init *init_function,
encrypt_decrypt *function,
openssl::EVP_PKEY *key,
bool encrypt) {
UNUSED(encrypt);
openssl::EVP_PKEY_CTX *ctx = openssl::EVP_PKEY_CTX_new(key, 0);
bts_assert(ctx);
if (init_function)
bts_assert(init_function(ctx));
bts_assert(openssl::EVP_PKEY_CTX_set_rsa_padding(ctx, RSA_PKCS1_PADDING));
char *name, *header;
unsigned char *in, *out;
long inlen;
size_t outlen;
int i = 0;
iostreambio biostream(const_cast<std::iostream *>(&in_dat));
if (!encrypt) {
auto bio = biostream.getBIO();
i = openssl::PEM_read_bio(bio, &name, &header, &in, &inlen);
} else {
std::iostream seeker(in_dat.rdbuf());
seeker.seekg(0, std::ios::end);
inlen = seeker.tellg();
seeker.seekg(0, std::ios::beg);
in = new unsigned char[inlen];
i = openssl::BIO_read(biostream.getBIO(), in, inlen);
}
// BIO_flush(biostream.getBIO());
BIO_free(biostream.getBIO());
bts_assert_msg(i > 0, "file not read, too short?");
bts_assert(function(ctx, NULL, &outlen, in, inlen)); // to get size
out = new unsigned char[outlen];
bts_assert(function(ctx, out, &outlen, in, inlen));
// if(encrypt)
// delete[] in;
std::string header_name = "BTS", header_header = "";
iostreambio biostream_out(const_cast<std::iostream *>(&out_data));
if (encrypt)
i = openssl::PEM_write_bio(
biostream_out.getBIO(), header_name.c_str(),
const_cast<char *>(header_header.c_str()), out, outlen);
else
i = openssl::BIO_write(biostream_out.getBIO(), out, outlen);
// BIO_flush(biostream_out.getBIO());
BIO_free(biostream_out.getBIO());
// delete[] out;
return (i > 0);
}
int private_encrypt_func(
openssl::EVP_PKEY_CTX *ctx,
unsigned char *out,
size_t *outlen,
const unsigned char *in,
size_t inlen) {
openssl::EVP_PKEY *pk = openssl::EVP_PKEY_CTX_get0_pkey(ctx);
auto rsa = openssl::EVP_PKEY_get0_RSA(pk);
if (out == 0) {
*outlen = openssl::RSA_size(rsa);
return -1;
} else {
return RSA_private_encrypt(
inlen, const_cast<unsigned char *>(in), out, rsa, RSA_PKCS1_PADDING);
}
}
int public_decrypt_func(
openssl::EVP_PKEY_CTX *ctx,
unsigned char *out,
size_t *outlen,
const unsigned char *in,
size_t inlen) {
openssl::EVP_PKEY *pk = openssl::EVP_PKEY_CTX_get0_pkey(ctx);
auto rsa = openssl::EVP_PKEY_get0_RSA(pk);
if (out == 0) {
*outlen = openssl::RSA_size(rsa);
return -1;
} else {
int i = RSA_public_decrypt(
inlen, const_cast<unsigned char *>(in), out, rsa, RSA_PKCS1_PADDING);
out[i] = '\0';
*outlen = i;
return i;
}
}
bool PrivateKey::private_encrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return encrypt_decrypt_function(
in_data, out_data, 0, private_encrypt_func, _key, true);
}
bool PrivateKey::private_decrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return encrypt_decrypt_function(
in_data, out_data, openssl::EVP_PKEY_decrypt_init,
openssl::EVP_PKEY_decrypt, _key, false);
}
bool PrivateKey::public_encrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return encrypt_decrypt_function(
in_data, out_data, openssl::EVP_PKEY_encrypt_init,
openssl::EVP_PKEY_encrypt, _key, true);
}
bool PrivateKey::public_decrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return encrypt_decrypt_function(
in_data, out_data, 0, public_decrypt_func, _key, false);
}
bool PrivateKey::encrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return private_encrypt(in_data, out_data);
}
bool PrivateKey::decrypt(
const std::iostream &in_data, std::iostream &out_data) const {
return private_decrypt(in_data, out_data);
}
void PrivateKey::write(openssl::BIO *bio, CipherType::type cipher_type) const {
// see rsa.c ln 213
const openssl::EVP_CIPHER *ciph =
openssl::EVP_get_cipherbyname(CipherType::name[(int)cipher_type]);
bts_assert_msg(
cipher_type == CipherType::None || ciph, "unknown cipher type");
int nbytes = openssl::PEM_write_bio_PKCS8PrivateKey(
bio, _key, ciph, 0, 0, password_callback, (void *)me->password);
UNUSED(nbytes);
int err = BIO_flush(bio);
UNUSED(err);
}
void PrivateKey::write_file(
const std::string &filename, CipherType::type cipher_type) const {
openssl::BIO *bio = openssl::BIO_new_file(filename.c_str(), "w+");
write(bio, cipher_type);
BIO_free(bio);
}
void PrivateKey::write_stream(
std::iostream &stream, CipherType::type cipher_type) const {
iostreambio biostream_out(&stream);
write(biostream_out.getBIO(), cipher_type);
BIO_free(biostream_out.getBIO());
}
} // namespace crypto
} // namespace bts
#endif