peerplays-fc/src/crypto/pke.cpp
2019-08-20 11:25:15 +02:00

365 lines
12 KiB
C++
Executable file

#include <fc/crypto/pke.hpp>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#include <openssl/sha.h>
#include <openssl/pem.h>
#include <openssl/err.h>
#include <assert.h>
#include <fc/crypto/base64.hpp>
#include <fc/io/sstream.hpp>
#include <fc/io/stdio.hpp>
#include <fc/exception/exception.hpp>
#include <fc/variant.hpp>
namespace fc {
namespace detail {
class pke_impl
{
public:
pke_impl():rsa(nullptr){}
~pke_impl()
{
if( rsa != nullptr )
RSA_free(rsa);
}
RSA* rsa;
};
} // detail
public_key::operator bool()const { return !!my; }
private_key::operator bool()const { return !!my; }
public_key::public_key()
{}
public_key::public_key( const bytes& d )
:my( std::make_shared<detail::pke_impl>() )
{
string pem = "-----BEGIN RSA PUBLIC KEY-----\n";
auto b64 = fc::base64_encode( (const unsigned char*)d.data(), d.size() );
for( size_t i = 0; i < b64.size(); i += 64 )
pem += b64.substr( i, 64 ) + "\n";
pem += "-----END RSA PUBLIC KEY-----\n";
// fc::cerr<<pem;
BIO* mem = (BIO*)BIO_new_mem_buf( (void*)pem.c_str(), pem.size() );
my->rsa = PEM_read_bio_RSAPublicKey(mem, NULL, NULL, NULL );
BIO_free(mem);
}
public_key::public_key( const public_key& k )
:my(k.my)
{
}
public_key::public_key( public_key&& k )
:my(std::move(k.my))
{
}
public_key::~public_key() { }
public_key& public_key::operator=(const public_key& p )
{
my = p.my; return *this;
}
public_key& public_key::operator=( public_key&& p )
{
my = std::move(p.my); return *this;
}
bool public_key::verify( const sha1& digest, const array<char,2048/8>& sig )const
{
return 0 != RSA_verify( NID_sha1, (const uint8_t*)&digest, 20,
(uint8_t*)&sig, 2048/8, my->rsa );
}
bool public_key::verify( const sha1& digest, const signature& sig )const
{
assert( sig.size() == 2048/8 );
return 0 != RSA_verify( NID_sha1, (const uint8_t*)&digest, 20,
(uint8_t*)sig.data(), 2048/8, my->rsa );
}
bool public_key::verify( const sha256& digest, const signature& sig )const
{
assert( sig.size() == 2048/8 );
return 0 != RSA_verify( NID_sha256, (const uint8_t*)&digest, 32,
(uint8_t*)sig.data(), 2048/8, my->rsa );
}
bytes public_key::encrypt( const char* b, size_t l )const
{
FC_ASSERT( my && my->rsa );
bytes out( RSA_size(my->rsa) ); //, char(0) );
int rtn = RSA_public_encrypt( l,
(unsigned char*)b,
(unsigned char*)out.data(),
my->rsa, RSA_PKCS1_OAEP_PADDING );
if( rtn >= 0 ) {
out.resize(rtn);
return out;
}
FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
}
bytes public_key::encrypt( const bytes& in )const
{
FC_ASSERT( my && my->rsa );
bytes out( RSA_size(my->rsa) ); //, char(0) );
int rtn = RSA_public_encrypt( in.size(),
(unsigned char*)in.data(),
(unsigned char*)out.data(),
my->rsa, RSA_PKCS1_OAEP_PADDING );
fc::cerr<<"rtn: "<<rtn<<"\n";
if( rtn >= 0 ) {
out.resize(rtn);
return out;
}
FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
}
bytes public_key::decrypt( const bytes& in )const
{
FC_ASSERT( my && my->rsa );
bytes out( RSA_size(my->rsa) );//, char(0) );
int rtn = RSA_public_decrypt( in.size(),
(unsigned char*)in.data(),
(unsigned char*)out.data(),
my->rsa, RSA_PKCS1_OAEP_PADDING );
if( rtn >= 0 ) {
out.resize(rtn);
return out;
}
FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
}
bytes public_key::serialize()const
{
bytes ba;
if( !my ) { return ba; }
BIO *mem = BIO_new(BIO_s_mem());
int e = PEM_write_bio_RSAPublicKey( mem, my->rsa );
if( e != 1 )
{
BIO_free(mem);
FC_THROW_EXCEPTION( exception, "openssl: ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
}
char* dat;
uint32_t l = BIO_get_mem_data( mem, &dat );
fc::stringstream ss( string( dat, l ) );
fc::stringstream key;
fc::string tmp;
fc::getline( ss, tmp );
fc::getline( ss, tmp );
while( tmp.size() && tmp[0] != '-' )
{
key << tmp;
fc::getline( ss, tmp );
}
auto str = key.str();
str = fc::base64_decode( str );
ba = bytes( str.begin(), str.end() );
BIO_free(mem);
return ba;
}
private_key::private_key()
{
}
private_key::private_key( const bytes& d )
:my( std::make_shared<detail::pke_impl>() )
{
string pem = "-----BEGIN RSA PRIVATE KEY-----\n";
auto b64 = fc::base64_encode( (const unsigned char*)d.data(), d.size() );
for( size_t i = 0; i < b64.size(); i += 64 )
pem += b64.substr( i, 64 ) + "\n";
pem += "-----END RSA PRIVATE KEY-----\n";
// fc::cerr<<pem;
BIO* mem = (BIO*)BIO_new_mem_buf( (void*)pem.c_str(), pem.size() );
my->rsa = PEM_read_bio_RSAPrivateKey(mem, NULL, NULL, NULL );
BIO_free(mem);
FC_ASSERT( my->rsa, "read private key" );
}
private_key::private_key( const private_key& k )
:my(k.my)
{
}
private_key::private_key( private_key&& k )
:my(std::move(k.my) )
{
}
private_key::~private_key() { }
private_key& private_key::operator=(const private_key& p )
{
my = p.my; return *this;
}
private_key& private_key::operator=(private_key&& p )
{
my = std::move(p.my); return *this;
}
void private_key::sign( const sha1& digest, array<char,2048/8>& sig )const
{
FC_ASSERT( (size_t(RSA_size(my->rsa)) <= sizeof(sig)), "Invalid RSA size" );
uint32_t slen = 0;
if( 1 != RSA_sign( NID_sha1, (uint8_t*)&digest,
20, (unsigned char*)&sig, &slen, my->rsa ) )
{
FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
}
}
signature private_key::sign( const sha1& digest )const
{
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
signature sig;
sig.resize( RSA_size(my->rsa) );
uint32_t slen = 0;
if( 1 != RSA_sign( NID_sha1, (uint8_t*)digest.data(),
20, (unsigned char*)sig.data(), &slen, my->rsa ) )
{
FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
}
return sig;
}
signature private_key::sign( const sha256& digest )const
{
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
signature sig;
sig.resize( RSA_size(my->rsa) );
uint32_t slen = 0;
if( 1 != RSA_sign( NID_sha256, (uint8_t*)digest.data(),
32, (unsigned char*)sig.data(), &slen, my->rsa ) )
{
FC_THROW_EXCEPTION( exception, "rsa sign failed with ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
}
return sig;
}
bytes private_key::encrypt( const bytes& in )const
{
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
bytes out;
out.resize( RSA_size(my->rsa) );
int rtn = RSA_private_encrypt( in.size(),
(unsigned char*)in.data(),
(unsigned char*)out.data(),
my->rsa, RSA_PKCS1_OAEP_PADDING );
if( rtn >= 0 ) {
out.resize(rtn);
return out;
}
FC_THROW_EXCEPTION( exception, "encrypt failed" );
}
bytes private_key::decrypt( const char* in, size_t l )const
{
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
bytes out;
out.resize( RSA_size(my->rsa) );
int rtn = RSA_private_decrypt( l,
(unsigned char*)in,
(unsigned char*)out.data(),
my->rsa, RSA_PKCS1_OAEP_PADDING );
if( rtn >= 0 ) {
out.resize(rtn);
return out;
}
FC_THROW_EXCEPTION( exception, "decrypt failed" );
}
bytes private_key::decrypt( const bytes& in )const
{
if( !my ) FC_THROW_EXCEPTION( assert_exception, "!null" );
bytes out;
out.resize( RSA_size(my->rsa) );
int rtn = RSA_private_decrypt( in.size(),
(unsigned char*)in.data(),
(unsigned char*)out.data(),
my->rsa, RSA_PKCS1_OAEP_PADDING );
if( rtn >= 0 ) {
out.resize(rtn);
return out;
}
FC_THROW_EXCEPTION( exception, "decrypt failed" );
}
bytes private_key::serialize()const
{
bytes ba;
if( !my ) { return ba; }
BIO *mem = BIO_new(BIO_s_mem());
int e = PEM_write_bio_RSAPrivateKey( mem, my->rsa, NULL, NULL, 0, NULL, NULL );
if( e != 1 )
{
BIO_free(mem);
FC_THROW_EXCEPTION( exception, "Error writing private key, ${message}", ("message",fc::string(ERR_error_string( ERR_get_error(),NULL))) );
}
char* dat;
uint32_t l = BIO_get_mem_data( mem, &dat );
// return bytes( dat, dat + l );
stringstream ss( string( dat, l ) );
stringstream key;
string tmp;
fc::getline( ss, tmp );
fc::getline( ss, tmp );
while( tmp.size() && tmp[0] != '-' )
{
key << tmp;
fc::getline( ss, tmp );
}
auto str = key.str();
str = fc::base64_decode( str );
ba = bytes( str.begin(), str.end() );
// ba = bytes( dat, dat + l );
BIO_free(mem);
return ba;
}
void generate_key_pair( public_key& pub, private_key& priv )
{
static bool init = true;
if( init ) { ERR_load_crypto_strings(); init = false; }
pub.my = std::make_shared<detail::pke_impl>();
priv.my = pub.my;
pub.my->rsa = RSA_generate_key( 2048, 65537, NULL, NULL );
}
/** encodes the big int as base64 string, or a number */
void to_variant( const public_key& bi, variant& v, uint32_t max_depth )
{
v = bi.serialize();
}
/** decodes the big int as base64 string, or a number */
void from_variant( const variant& v, public_key& bi, uint32_t max_depth )
{
bi = public_key( v.as<std::vector<char> >(max_depth) );
}
/** encodes the big int as base64 string, or a number */
void to_variant( const private_key& bi, variant& v, uint32_t max_depth )
{
v = bi.serialize();
}
/** decodes the big int as base64 string, or a number */
void from_variant( const variant& v, private_key& bi, uint32_t max_depth )
{
bi = private_key( v.as<std::vector<char> >(max_depth) );
}
} // fc