/******************************************************************************/
/* */
/* X r d C r y p t o S s l R S A . c c */
/* */
/* (c) 2004 by the Board of Trustees of the Leland Stanford, Jr., University */
/* Produced by Gerri Ganis for CERN */
/* */
/* This file is part of the XRootD software suite. */
/* */
/* XRootD is free software: you can redistribute it and/or modify it under */
/* the terms of the GNU Lesser General Public License as published by the */
/* Free Software Foundation, either version 3 of the License, or (at your */
/* option) any later version. */
/* */
/* XRootD is distributed in the hope that it will be useful, but WITHOUT */
/* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or */
/* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public */
/* License for more details. */
/* */
/* You should have received a copy of the GNU Lesser General Public License */
/* along with XRootD in a file called COPYING.LESSER (LGPL license) and file */
/* COPYING (GPL license). If not, see . */
/* */
/* The copyright holder's institutional names and contributor's names may not */
/* be used to endorse or promote products derived from this software without */
/* specific prior written permission of the institution or contributor. */
/******************************************************************************/
/* ************************************************************************** */
/* */
/* OpenSSL implementation of XrdCryptoRSA */
/* */
/* ************************************************************************** */
#include "XrdSut/XrdSutRndm.hh"
#include "XrdCrypto/XrdCryptosslAux.hh"
#include "XrdCrypto/XrdCryptosslTrace.hh"
#include "XrdCrypto/XrdCryptosslRSA.hh"
#include
#include
#include
#include
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static RSA *EVP_PKEY_get0_RSA(EVP_PKEY *pkey)
{
if (pkey->type != EVP_PKEY_RSA) {
return NULL;
}
return pkey->pkey.rsa;
}
static void RSA_get0_key(const RSA *r,
const BIGNUM **n, const BIGNUM **e, const BIGNUM **d)
{
if (n != NULL)
*n = r->n;
if (e != NULL)
*e = r->e;
if (d != NULL)
*d = r->d;
}
#endif
//_____________________________________________________________________________
XrdCryptosslRSA::XrdCryptosslRSA(int bits, int exp)
{
// Constructor
// Generate a RSA asymmetric key pair
// Length will be 'bits' bits (min 512, default 1024), public
// exponent `pubex` (default 65537).
EPNAME("RSA::XrdCryptosslRSA");
publen = -1;
prilen = -1;
// Create container, first
if (!(fEVP = EVP_PKEY_new())) {
DEBUG("cannot allocate new public key container");
return;
}
// Minimum is XrdCryptoMinRSABits
bits = (bits >= XrdCryptoMinRSABits) ? bits : XrdCryptoMinRSABits;
// If pubex is not odd, use default
if (!(exp & 1))
exp = XrdCryptoDefRSAExp; // 65537 (0x10001)
DEBUG("bits: "<0 use the first lpub bytes; otherwise use strlen(pub)
// bytes.
fEVP = 0;
publen = -1;
prilen = -1;
// Import key
ImportPublic(pub,lpub);
}
//_____________________________________________________________________________
XrdCryptosslRSA::XrdCryptosslRSA(EVP_PKEY *key, bool check)
{
// Constructor to import existing key
EPNAME("RSA::XrdCryptosslRSA_key");
fEVP = 0;
publen = -1;
prilen = -1;
// Create container, first
if (!key) {
DEBUG("no input key");
return;
}
if (check) {
// Check consistency
if (RSA_check_key(EVP_PKEY_get0_RSA(key)) != 0) {
fEVP = key;
// Update status
status = kComplete;
} else {
DEBUG("key contains inconsistent information");
}
} else {
// Accept in any case (for incomplete keys)
fEVP = key;
// Update status
status = kPublic;
}
}
//____________________________________________________________________________
XrdCryptosslRSA::XrdCryptosslRSA(const XrdCryptosslRSA &r) : XrdCryptoRSA()
{
// Copy Constructor
EPNAME("RSA::XrdCryptosslRSA_copy");
fEVP = 0;
publen = -1;
prilen = -1;
if (!r.fEVP) {
DEBUG("input key is empty");
return;
}
// If the given key is set, copy it via a bio
const BIGNUM *d;
RSA_get0_key(EVP_PKEY_get0_RSA(r.fEVP), NULL, NULL, &d);
bool publiconly = (d == 0);
//
// Bio for exporting the pub key
BIO *bcpy = BIO_new(BIO_s_mem());
if (bcpy) {
bool ok;
if (publiconly) {
// Write kref public key to BIO
ok = (PEM_write_bio_PUBKEY(bcpy, r.fEVP) != 0);
} else {
// Write kref private key to BIO
ok = (PEM_write_bio_PrivateKey(bcpy,r.fEVP,0,0,0,0,0) != 0);
}
if (ok) {
if (publiconly) {
// Read public key from BIO
if ((fEVP = PEM_read_bio_PUBKEY(bcpy, 0, 0, 0))) {
status = kPublic;
}
} else {
if ((fEVP = PEM_read_bio_PrivateKey(bcpy,0,0,0))) {
// Check consistency
if (RSA_check_key(EVP_PKEY_get0_RSA(fEVP)) != 0) {
// Update status
status = kComplete;
}
}
}
}
// Cleanup bio
BIO_free(bcpy);
}
}
//_____________________________________________________________________________
XrdCryptosslRSA::~XrdCryptosslRSA()
{
// Destructor
// Destroy the RSA asymmetric key pair
if (fEVP)
EVP_PKEY_free(fEVP);
fEVP = 0;
}
//_____________________________________________________________________________
int XrdCryptosslRSA::GetOutlen(int lin)
{
// Get minimal length of output buffer
int lcmax = RSA_size(EVP_PKEY_get0_RSA(fEVP)) - 42;
return ((lin / lcmax) + 1) * RSA_size(EVP_PKEY_get0_RSA(fEVP));
}
//_____________________________________________________________________________
int XrdCryptosslRSA::ImportPublic(const char *pub, int lpub)
{
// Import a public key
// Allocate a RSA key pair and fill the public part importing
// from string representation (pub) to internal representation.
// If lpub>0 use the first lpub bytes; otherwise use strlen(pub)
// bytes.
// Return 0 in case of success, -1 in case of failure
int rc = -1;
if (fEVP)
EVP_PKEY_free(fEVP);
fEVP = 0;
publen = -1;
prilen = -1;
// Temporary key
EVP_PKEY *keytmp = 0;
// Bio for exporting the pub key
BIO *bpub = BIO_new(BIO_s_mem());
// Check length
lpub = (lpub <= 0) ? strlen(pub) : lpub;
// Write key from pubexport to BIO
BIO_write(bpub,(void *)pub,lpub);
// Read pub key from BIO
if ((keytmp = PEM_read_bio_PUBKEY(bpub, 0, 0, 0))) {
fEVP = keytmp;
// Update status
status = kPublic;
rc = 0;
}
BIO_free(bpub);
return rc;
}
//_____________________________________________________________________________
int XrdCryptosslRSA::ImportPrivate(const char *pri, int lpri)
{
// Import a private key
// Fill the private part importing from string representation (pub) to
// internal representation.
// If lpub>0 use the first lpub bytes; otherwise use strlen(pub)
// bytes.
// Return 0 in case of success, -1 in case of failure
if (!fEVP)
return -1;
prilen = -1;
// Bio for exporting the pub key
BIO *bpri = BIO_new(BIO_s_mem());
// Check length
lpri = (lpri <= 0) ? strlen(pri) : lpri;
// Write key from private export to BIO
BIO_write(bpri,(void *)pri,lpri);
// Read private key from BIO
if (PEM_read_bio_PrivateKey(bpri, &fEVP, 0, 0)) {
// Update status
status = kComplete;
return 0;
}
return -1;
}
//_____________________________________________________________________________
void XrdCryptosslRSA::Dump()
{
// Dump some info about the key
EPNAME("RSA::Dump");
DEBUG("---------------------------------------");
DEBUG("address: "< 0 && ke <= (loutmax - lout)) {
int lc = (len > lcmax) ? lcmax : len ;
if ((lout = RSA_private_encrypt(lc, (unsigned char *)&in[kk],
(unsigned char *)&out[ke],
EVP_PKEY_get0_RSA(fEVP), RSA_PKCS1_PADDING)) < 0) {
char serr[120];
ERR_error_string(ERR_get_error(), serr);
DEBUG("error: " < 0 && ke > (loutmax - lout))
DEBUG("buffer truncated");
lout = ke;
// Return
return lout;
}
//_____________________________________________________________________________
int XrdCryptosslRSA::EncryptPublic(const char *in, int lin, char *out, int loutmax)
{
// Encrypt lin bytes at 'in' using the internal public key.
// The output buffer 'out' is allocated by the caller for max lout bytes.
// The number of meaningful bytes in out is returned in case of success
// (never larger that loutmax); -1 in case of error.
EPNAME("RSA::EncryptPublic");
// Make sure we got something to encrypt
if (!in || lin <= 0) {
DEBUG("input buffer undefined");
return -1;
}
// Make sure we got a buffer where to write
if (!out || loutmax <= 0) {
DEBUG("output buffer undefined");
return -1;
}
//
// Public encoding ...
int lcmax = RSA_size(EVP_PKEY_get0_RSA(fEVP)) - 42; // Magic number (= 2*sha1_outlen + 2)
int lout = 0;
int len = lin;
int kk = 0;
int ke = 0;
while (len > 0 && ke <= (loutmax - lout)) {
int lc = (len > lcmax) ? lcmax : len ;
if ((lout = RSA_public_encrypt(lc, (unsigned char *)&in[kk],
(unsigned char *)&out[ke],
EVP_PKEY_get0_RSA(fEVP), RSA_PKCS1_OAEP_PADDING)) < 0) {
char serr[120];
ERR_error_string(ERR_get_error(), serr);
DEBUG("error: " < 0 && ke > (loutmax - lout))
DEBUG("buffer truncated");
lout = ke;
// Return
return lout;
}
//_____________________________________________________________________________
int XrdCryptosslRSA::DecryptPrivate(const char *in, int lin, char *out, int loutmax)
{
// Decrypt lin bytes at 'in' using the internal private key
// The output buffer 'out' is allocated by the caller for max lout bytes.
// The number of meaningful bytes in out is returned in case of success
// (never larger that loutmax); -1 in case of error.
EPNAME("RSA::DecryptPrivate");
// Make sure we got something to decrypt
if (!in || lin <= 0) {
DEBUG("input buffer undefined");
return -1;
}
// Make sure we got a buffer where to write
if (!out || loutmax <= 0) {
DEBUG("output buffer undefined");
return -1;
}
int lout = 0;
int len = lin;
int lcmax = RSA_size(EVP_PKEY_get0_RSA(fEVP));
int kk = 0;
int ke = 0;
//
// Private decoding ...
while (len > 0 && ke <= (loutmax - lout)) {
if ((lout = RSA_private_decrypt(lcmax, (unsigned char *)&in[kk],
(unsigned char *)&out[ke],
EVP_PKEY_get0_RSA(fEVP), RSA_PKCS1_OAEP_PADDING)) < 0) {
char serr[120];
ERR_error_string(ERR_get_error(), serr);
DEBUG("error: " < 0 && ke > (loutmax - lout))
PRINT("buffer truncated");
lout = ke;
return lout;
}
//_____________________________________________________________________________
int XrdCryptosslRSA::DecryptPublic(const char *in, int lin, char *out, int loutmax)
{
// Decrypt lin bytes at 'in' using the internal public key
// The output buffer 'out' is allocated by the caller for max lout bytes.
// The number of meaningful bytes in out is returned in case of success
// (never larger that loutmax); -1 in case of error.
EPNAME("RSA::DecryptPublic");
// Make sure we got something to decrypt
if (!in || lin <= 0) {
DEBUG("input buffer undefined");
return -1;
}
// Make sure we got a buffer where to write
if (!out || loutmax <= 0) {
DEBUG("output buffer undefined");
return -1;
}
int lout = 0;
int len = lin;
int lcmax = RSA_size(EVP_PKEY_get0_RSA(fEVP));
int kk = 0;
int ke = 0;
//
// Private decoding ...
while (len > 0 && ke <= (loutmax - lout)) {
if ((lout = RSA_public_decrypt(lcmax, (unsigned char *)&in[kk],
(unsigned char *)&out[ke],
EVP_PKEY_get0_RSA(fEVP), RSA_PKCS1_PADDING)) < 0) {
char serr[120];
ERR_error_string(ERR_get_error(), serr);
PRINT("error: " < 0 && ke > (loutmax - lout))
PRINT("buffer truncated");
lout = ke;
return lout;
}