Crypt-OpenSSL-RSA
view release on metacpan or search on metacpan
EVP_PKEY_free(pkey);
return 0;
}
ok = PEM_write_bio_PrivateKey(bio, pkey, enc, pass, passlen, NULL, NULL);
EVP_PKEY_free(pkey);
return ok;
}
#endif
/* Pre-3.x helper for loading encrypted PKCS#8 DER private keys.
Placed BEFORE the EVP_PKEY->RSA compatibility macros so that
EVP_PKEY, EVP_PKEY_free, and EVP_PKEY_get1_RSA resolve to their
real OpenSSL symbols. */
#if OPENSSL_VERSION_NUMBER < 0x30000000L
static RSA* _load_pkcs8_der_key(BIO* bio, const char* passphrase)
{
EVP_PKEY* pkey;
RSA* rsa;
pkey = d2i_PKCS8PrivateKey_bio(bio, NULL, NULL, (void*)passphrase);
if (!pkey)
return NULL;
rsa = EVP_PKEY_get1_RSA(pkey);
EVP_PKEY_free(pkey);
return rsa;
}
#endif
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#define UNSIGNED_CHAR unsigned char
#define SIZE_T_INT size_t
#define SIZE_T_UNSIGNED_INT size_t
#define EVP_PKEY EVP_PKEY
#define EVP_PKEY_free(p) EVP_PKEY_free(p)
#define EVP_PKEY_get_size(p) EVP_PKEY_get_size(p)
#define PEM_read_bio_PrivateKey PEM_read_bio_PrivateKey
#define PEM_read_bio_RSAPublicKey PEM_read_bio_PUBKEY
#define PEM_read_bio_RSA_PUBKEY PEM_read_bio_PUBKEY
#define PEM_write_bio_PUBKEY(o,p) PEM_write_bio_PUBKEY(o,p)
#define PEM_write_bio_PrivateKey_traditional(m, n, o, p, q, r, s) PEM_write_bio_PrivateKey_traditional(m, n, o, p, q, r, s)
#else
#define UNSIGNED_CHAR char
#define SIZE_T_INT int
#define SIZE_T_UNSIGNED_INT unsigned int
#define EVP_PKEY RSA
#define EVP_PKEY_free(p) RSA_free(p)
#define EVP_PKEY_get_size(p) RSA_size(p)
#define PEM_read_bio_PrivateKey PEM_read_bio_RSAPrivateKey
#define PEM_read_bio_RSAPublicKey PEM_read_bio_RSAPublicKey
#define PEM_read_bio_RSA_PUBKEY PEM_read_bio_RSA_PUBKEY
#define PEM_write_bio_PUBKEY(o,p) PEM_write_bio_RSA_PUBKEY(o,p)
#define PEM_write_bio_PrivateKey_traditional(m, n, o, p, q, r, s) PEM_write_bio_RSAPrivateKey(m, n , o, p, q, r, s)
#endif
typedef struct
{
EVP_PKEY* rsa;
int padding;
int hashMode;
int is_private_key; /* cached once at construction; avoids per-call BIGNUM alloc on 3.x */
} rsaData;
/* Key names for the rsa hash structure */
#define KEY_KEY "_Key"
#define PADDING_KEY "_Padding"
#define HASH_KEY "_Hash_Mode"
#define PACKAGE_NAME "Crypt::OpenSSL::RSA"
#ifdef LIBRESSL_VERSION_NUMBER
#define OLD_CRUFTY_SSL_VERSION (OPENSSL_VERSION_NUMBER < 0x10100000L || LIBRESSL_VERSION_NUMBER < 0x03050000fL)
#else
#define OLD_CRUFTY_SSL_VERSION (OPENSSL_VERSION_NUMBER < 0x10100000L)
#endif
void croakSsl(char* p_file, int p_line)
{
const char* errorReason;
unsigned long last_err = 0;
unsigned long err;
/* Drain the error queue and use the last (most recent) error,
which is typically the most descriptive. This also prevents
stale errors from a previous eval-caught failure from leaking
into the next croak message. */
while ((err = ERR_get_error()) != 0) {
last_err = err;
}
errorReason = ERR_reason_error_string(last_err);
croak("%s:%d: OpenSSL error: %s", p_file, p_line,
errorReason ? errorReason : "(unknown error)");
}
#define CHECK_OPEN_SSL(p_result) if (!(p_result)) croakSsl(__FILE__, __LINE__);
#define CHECK_OPEN_SSL_BIO(p_result, bio) \
if (!(p_result)) { BIO_free(bio); croakSsl(__FILE__, __LINE__); }
#define PACKAGE_CROAK(p_message) croak("%s", (p_message))
#define CHECK_NEW(p_var, p_size, p_type) \
if (New(0, p_var, p_size, p_type) == NULL) \
{ PACKAGE_CROAK("unable to alloc buffer"); }
#define THROW(p_result) if (!(p_result)) { error = 1; goto err; }
char _is_private(rsaData* p_rsa)
{
return p_rsa->is_private_key;
}
static int _detect_private_key(EVP_PKEY* p_rsa)
{
#if OLD_CRUFTY_SSL_VERSION
return (p_rsa->d != NULL);
#else
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
BIGNUM* d = NULL;
EVP_PKEY_get_bn_param(p_rsa, OSSL_PKEY_PARAM_RSA_D, &d);
if (d) {
BN_clear_free(d);
return 1;
}
return 0;
#else
const BIGNUM* d = NULL;
RSA_get0_key(p_rsa, NULL, NULL, &d);
return (d != NULL);
#endif
#endif
}
SV* make_rsa_obj(SV* p_proto, EVP_PKEY* p_rsa)
{
rsaData* rsa;
CHECK_NEW(rsa, 1, rsaData);
rsa->rsa = p_rsa;
#ifdef SHA512_DIGEST_LENGTH
rsa->hashMode = NID_sha256;
#else
rsa->hashMode = NID_sha1;
#endif
rsa->padding = RSA_PKCS1_OAEP_PADDING;
rsa->is_private_key = _detect_private_key(p_rsa);
return sv_bless(
newRV_noinc(newSViv((IV) rsa)),
(SvROK(p_proto) ? SvSTASH(SvRV(p_proto)) : gv_stashsv(p_proto, 1)));
}
int get_digest_length(int hash_method)
{
switch(hash_method)
{
case NID_md5:
return MD5_DIGEST_LENGTH;
break;
case NID_sha1:
return SHA_DIGEST_LENGTH;
break;
#ifdef SHA512_DIGEST_LENGTH
case NID_sha224:
return SHA224_DIGEST_LENGTH;
break;
case NID_sha256:
return SHA256_DIGEST_LENGTH;
break;
case NID_sha384:
return SHA384_DIGEST_LENGTH;
break;
case NID_sha512:
return SHA512_DIGEST_LENGTH;
break;
#endif
case NID_ripemd160:
return RIPEMD160_DIGEST_LENGTH;
break;
#ifdef WHIRLPOOL_DIGEST_LENGTH
case NID_whirlpool:
return WHIRLPOOL_DIGEST_LENGTH;
break;
#endif
default:
croak("Unknown digest hash mode %u", hash_method);
break;
}
}
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_MD *get_md_bynid(int hash_method)
{
switch(hash_method)
{
case NID_md5:
return EVP_MD_fetch(NULL, "md5", NULL);
break;
case NID_sha1:
return EVP_MD_fetch(NULL, "sha1", NULL);
break;
#ifdef SHA512_DIGEST_LENGTH
case NID_sha224:
return EVP_MD_fetch(NULL, "sha224", NULL);
break;
case NID_sha256:
return EVP_MD_fetch(NULL, "sha256", NULL);
padding choice. OAEP and PSS are not valid here. */
if (p_rsa->padding == RSA_PKCS1_OAEP_PADDING) {
croak("OAEP padding is not supported for private_encrypt/public_decrypt. "
"Call use_no_padding() or use_pkcs1_padding() first.");
}
if (p_rsa->padding == RSA_PKCS1_PSS_PADDING) {
croak("PSS padding with private_encrypt/public_decrypt is not supported. "
"Use sign()/verify() for PSS signatures.");
}
}
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, (EVP_PKEY* )p_rsa->rsa, NULL);
THROW(ctx);
THROW(init_crypt(ctx) == 1);
THROW(EVP_PKEY_CTX_set_rsa_padding(ctx, p_rsa->padding) > 0);
THROW(p_crypt(ctx, NULL, &to_length, from, from_length) == 1);
Newx(to, to_length, UNSIGNED_CHAR);
THROW(to);
THROW(p_crypt(ctx, to, &to_length, from, from_length) == 1);
EVP_PKEY_CTX_free(ctx);
goto crypt_done;
err:
if (ctx) EVP_PKEY_CTX_free(ctx);
Safefree(to);
CHECK_OPEN_SSL(0);
crypt_done:
#else
size = EVP_PKEY_get_size(p_rsa->rsa);
CHECK_NEW(to, size, UNSIGNED_CHAR);
to_length = p_crypt(
from_length, from, (unsigned char*) to, p_rsa->rsa, p_rsa->padding);
#endif
if (to_length < 0)
{
Safefree(to);
CHECK_OPEN_SSL(0);
}
sv = newSVpv((char* ) to, to_length);
Safefree(to);
return sv;
}
MODULE = Crypt::OpenSSL::RSA PACKAGE = Crypt::OpenSSL::RSA
PROTOTYPES: DISABLE
BOOT:
#if OPENSSL_VERSION_NUMBER < 0x10100000L
# might introduce memory leak without calling EVP_cleanup() on exit
# see https://wiki.openssl.org/index.php/Library_Initialization
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
#else
# NOOP
#endif
SV*
_new_private_key_pem(proto, key_string_SV, passphrase_SV=&PL_sv_undef)
SV* proto;
SV* key_string_SV;
SV* passphrase_SV;
CODE:
RETVAL = make_rsa_obj(
proto, _load_rsa_key(key_string_SV, PEM_read_bio_PrivateKey, passphrase_SV));
OUTPUT:
RETVAL
SV*
_new_public_key_pkcs1(proto, key_string_SV)
SV* proto;
SV* key_string_SV;
CODE:
RETVAL = make_rsa_obj(
proto, _load_rsa_key(key_string_SV, PEM_read_bio_RSAPublicKey, &PL_sv_undef));
OUTPUT:
RETVAL
SV*
_new_public_key_x509(proto, key_string_SV)
SV* proto;
SV* key_string_SV;
CODE:
RETVAL = make_rsa_obj(
proto, _load_rsa_key(key_string_SV, PEM_read_bio_RSA_PUBKEY, &PL_sv_undef));
OUTPUT:
RETVAL
SV*
_new_public_key_x509_der(proto, key_string_SV)
SV* proto;
SV* key_string_SV;
PREINIT:
STRLEN keyStringLength;
char* keyString;
EVP_PKEY* pkey;
BIO* bio;
CODE:
keyString = SvPV(key_string_SV, keyStringLength);
CHECK_OPEN_SSL(bio = BIO_new_mem_buf(keyString, keyStringLength));
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
pkey = d2i_PUBKEY_bio(bio, NULL);
#else
pkey = d2i_RSA_PUBKEY_bio(bio, NULL);
#endif
BIO_free(bio);
CHECK_OPEN_SSL(pkey);
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
if (EVP_PKEY_get_base_id(pkey) != EVP_PKEY_RSA) {
EVP_PKEY_free(pkey);
croak("The key loaded is not an RSA key");
}
#endif
RETVAL = make_rsa_obj(proto, pkey);
OUTPUT:
RETVAL
SV*
_new_public_key_pkcs1_der(proto, key_string_SV)
SV* proto;
SV* key_string_SV;
PREINIT:
STRLEN keyStringLength;
char* keyString;
EVP_PKEY* pkey;
BIO* bio;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_DECODER_CTX* dctx;
#endif
CODE:
keyString = SvPV(key_string_SV, keyStringLength);
CHECK_OPEN_SSL(bio = BIO_new_mem_buf(keyString, keyStringLength));
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
pkey = NULL;
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", "type-specific",
"RSA", OSSL_KEYMGMT_SELECT_PUBLIC_KEY,
NULL, NULL);
if (!dctx) {
BIO_free(bio);
croakSsl(__FILE__, __LINE__);
}
if (!OSSL_DECODER_from_bio(dctx, bio)) {
OSSL_DECODER_CTX_free(dctx);
BIO_free(bio);
croakSsl(__FILE__, __LINE__);
}
OSSL_DECODER_CTX_free(dctx);
#else
pkey = d2i_RSAPublicKey_bio(bio, NULL);
#endif
BIO_free(bio);
CHECK_OPEN_SSL(pkey);
RETVAL = make_rsa_obj(proto, pkey);
OUTPUT:
RETVAL
SV*
_new_private_key_der(proto, key_string_SV, passphrase_SV=&PL_sv_undef)
SV* proto;
SV* key_string_SV;
SV* passphrase_SV;
PREINIT:
STRLEN keyStringLength;
char* keyString;
EVP_PKEY* pkey;
BIO* bio;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_DECODER_CTX* dctx;
#endif
CODE:
keyString = SvPV(key_string_SV, keyStringLength);
CHECK_OPEN_SSL(bio = BIO_new_mem_buf(keyString, keyStringLength));
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
pkey = NULL;
dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL,
"RSA", OSSL_KEYMGMT_SELECT_ALL,
NULL, NULL);
if (!dctx) {
BIO_free(bio);
croakSsl(__FILE__, __LINE__);
}
if (SvPOK(passphrase_SV)) {
STRLEN passlen;
unsigned char* pass = (unsigned char*)SvPV(passphrase_SV, passlen);
if (!OSSL_DECODER_CTX_set_passphrase(dctx, pass, passlen)) {
OSSL_DECODER_CTX_free(dctx);
BIO_free(bio);
croakSsl(__FILE__, __LINE__);
}
}
if (!OSSL_DECODER_from_bio(dctx, bio)) {
OSSL_DECODER_CTX_free(dctx);
BIO_free(bio);
croakSsl(__FILE__, __LINE__);
}
OSSL_DECODER_CTX_free(dctx);
#else
if (SvPOK(passphrase_SV)) {
char* passphrase = SvPV_nolen(passphrase_SV);
pkey = _load_pkcs8_der_key(bio, passphrase);
} else {
pkey = d2i_RSAPrivateKey_bio(bio, NULL);
}
#endif
BIO_free(bio);
CHECK_OPEN_SSL(pkey);
RETVAL = make_rsa_obj(proto, pkey);
OUTPUT:
RETVAL
void
DESTROY(p_rsa)
rsaData* p_rsa;
CODE:
EVP_PKEY_free(p_rsa->rsa);
Safefree(p_rsa);
SV*
get_private_key_string(p_rsa, passphrase_SV=&PL_sv_undef, cipher_name_SV=&PL_sv_undef)
rsaData* p_rsa;
SV* passphrase_SV;
SV* cipher_name_SV;
PREINIT:
BIO* stringBIO;
char* passphrase = NULL;
STRLEN passphraseLength = 0;
char* cipher_name;
const EVP_CIPHER* enc = NULL;
CODE:
if (!_is_private(p_rsa))
{
croak("Public keys cannot export private key strings");
}
if (SvPOK(cipher_name_SV) && !SvPOK(passphrase_SV)) {
croak("Passphrase is required for cipher");
}
if (SvPOK(passphrase_SV)) {
passphrase = SvPV(passphrase_SV, passphraseLength);
if (SvPOK(cipher_name_SV)) {
cipher_name = SvPV_nolen(cipher_name_SV);
}
else {
cipher_name = "des3";
}
enc = EVP_get_cipherbyname(cipher_name);
if (enc == NULL) {
croak("Unsupported cipher: %s", cipher_name);
}
}
CHECK_OPEN_SSL(stringBIO = BIO_new(BIO_s_mem()));
CHECK_OPEN_SSL_BIO(PEM_write_bio_PrivateKey_traditional(
stringBIO, p_rsa->rsa, enc, (unsigned char* ) passphrase, passphraseLength, NULL, NULL), stringBIO);
RETVAL = extractBioString(stringBIO);
OUTPUT:
RETVAL
SV*
get_private_key_pkcs8_string(p_rsa, passphrase_SV=&PL_sv_undef, cipher_name_SV=&PL_sv_undef)
rsaData* p_rsa;
SV* passphrase_SV;
SV* cipher_name_SV;
PREINIT:
BIO* stringBIO;
char* passphrase = NULL;
STRLEN passphraseLength = 0;
char* cipher_name;
const EVP_CIPHER* enc = NULL;
CODE:
if (SvPOK(cipher_name_SV) && !SvPOK(passphrase_SV)) {
croak("Passphrase is required for cipher");
}
if (SvPOK(passphrase_SV)) {
passphrase = SvPV(passphrase_SV, passphraseLength);
if (SvPOK(cipher_name_SV)) {
cipher_name = SvPV_nolen(cipher_name_SV);
}
else {
cipher_name = "des3";
}
enc = EVP_get_cipherbyname(cipher_name);
if (enc == NULL) {
croak("Unsupported cipher: %s", cipher_name);
}
}
CHECK_OPEN_SSL(stringBIO = BIO_new(BIO_s_mem()));
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
CHECK_OPEN_SSL_BIO(PEM_write_bio_PrivateKey(
stringBIO, p_rsa->rsa, enc, (unsigned char*) passphrase, passphraseLength, NULL, NULL), stringBIO);
#else
CHECK_OPEN_SSL_BIO(_write_pkcs8_pem(
stringBIO, p_rsa->rsa, enc, (unsigned char*) passphrase, passphraseLength), stringBIO);
#endif
RETVAL = extractBioString(stringBIO);
OUTPUT:
RETVAL
SV*
get_public_key_string(p_rsa)
rsaData* p_rsa;
PREINIT:
BIO* stringBIO;
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_ENCODER_CTX *ctx = NULL;
int error = 0;
#endif
CODE:
CHECK_OPEN_SSL(stringBIO = BIO_new(BIO_s_mem()));
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
ctx = OSSL_ENCODER_CTX_new_for_pkey(p_rsa->rsa, OSSL_KEYMGMT_SELECT_PUBLIC_KEY,
"PEM", "PKCS1", NULL);
THROW(ctx != NULL && OSSL_ENCODER_CTX_get_num_encoders(ctx));
THROW(OSSL_ENCODER_to_bio(ctx, stringBIO) == 1);
OSSL_ENCODER_CTX_free(ctx);
ctx = NULL;
( run in 0.730 second using v1.01-cache-2.11-cpan-e1769b4cff6 )