Files
xen/tools/vtpm_manager/crypto/rsa.c
T
shand@ubuntu.eng.hq.xensource.com f2a4d188c4 vTPM manager - Intel/IBM joint TPM Virtualization implementation for Xen.
This is a software implementation for people without h/w vTPM support. 

Signed-off-by: Vinnie Scarlata <vincent.r.scarlata@intel.com>
Signed-off-by: Joseph Cihula <joseph.cihula@intel.com>
Signed-off-by: Steven Hand <steven@xensource.com>
2005-08-30 11:39:25 -08:00

435 lines
13 KiB
C

// ===================================================================
//
// Copyright (c) 2005, Intel Corp.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Intel Corporation nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
// COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
// OF THE POSSIBILITY OF SUCH DAMAGE.
// ===================================================================
//
// rsa.c
//
// This file will handle all the TPM RSA crypto functionality
//
// ==================================================================
#include <string.h>
#include <openssl/crypto.h>
#include <openssl/evp.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include <stdio.h>
#include "tcg.h"
#include "buffer.h"
#include "crypto.h"
#include "log.h"
void Crypto_RSACreateKey( /*in*/ UINT32 keySize,
/*in*/ UINT32 pubExpSize,
/*in*/ BYTE *pubExp,
/*out*/ UINT32 *privExpSize,
/*out*/ BYTE *privExp,
/*out*/ UINT32 *modulusSize,
/*out*/ BYTE *modulus,
/*out*/ CRYPTO_INFO *keys) {
unsigned long e_value;
if (pubExpSize == 0) // Default e = 2^16+1
e_value = (0x01 << 16) + 1;
else {
// This is not supported, but the following line MIGHT work
// under then assumption that the format is BigNum compatable
// Though it's not in the spec, so who knows what it is.
// Forcing the default.
//BN_bin2bn(pubExp, pubExpSize, NULL);
e_value = (0x01 << 16) + 1;
}
RSA *rsa = RSA_generate_key(keySize, e_value, NULL, NULL);
if (keys) {
keys->keyInfo = rsa;
keys->algorithmID = CRYPTO_ALGORITH_RSA;
}
if (modulus) *modulusSize = BN_bn2bin(rsa->n, modulus);
if (privExp) *privExpSize = BN_bn2bin(rsa->d, privExp);
}
// Create a CRYPTO_INFO struct from the BYTE * key parts.
// If pubExp info is NULL, use TCG default.
// If there is a remainder while calculating the privExp, return FALSE.
void Crypto_RSABuildCryptoInfo( /*[IN]*/ UINT32 pubExpSize,
/*[IN]*/ BYTE *pubExp,
/*[IN]*/ UINT32 privExpSize,
/*[IN]*/ BYTE *privExp,
/*[IN]*/ UINT32 modulusSize,
/*[IN]*/ BYTE *modulus,
CRYPTO_INFO* cryptoInfo) {
cryptoInfo->keyInfo = RSA_new();
RSA *rsa = (RSA *) cryptoInfo->keyInfo;
rsa->e = BN_new();
if (pubExpSize == 0) { // Default e = 2^16+1
BN_set_bit(rsa->e, 16);
BN_set_bit(rsa->e, 0);
} else {
// This is not supported, but the following line MIGHT work
// under then assumption that the format is BigNum compatable
// Though it's not in the spec, so who knows what it is.
// Forcing the default.
//BN_bin2bn(pubExp, pubExpSize, NULL);
BN_set_bit(rsa->e, 16);
BN_set_bit(rsa->e, 0);
}
rsa->n = BN_bin2bn(modulus, modulusSize, NULL);
rsa->d = BN_bin2bn(privExp, privExpSize, NULL);
}
// Create a CRYPTO_INFO struct from the BYTE * key parts.
// If pubExp info is NULL, use TCG default.
// If there is a remainder while calculating the privExp, return FALSE.
void Crypto_RSABuildCryptoInfoPublic( /*[IN]*/ UINT32 pubExpSize,
/*[IN]*/ BYTE *pubExp,
/*[IN]*/ UINT32 modulusSize,
/*[IN]*/ BYTE *modulus,
CRYPTO_INFO* cryptoInfo) {
cryptoInfo->keyInfo = RSA_new();
RSA *rsa = (RSA *) cryptoInfo->keyInfo;
rsa->e = BN_new();
if (pubExpSize == 0) { // Default e = 2^16+1
BN_set_bit(rsa->e, 16);
BN_set_bit(rsa->e, 0);
} else {
// This is not supported, but the following line MIGHT work
// under then assumption that the format is BigNum compatable
// Though it's not in the spec, so who knows what it is.
// Forcing the default.
//BN_bin2bn(pubExp, pubExpSize, NULL);
BN_set_bit(rsa->e, 16);
BN_set_bit(rsa->e, 0);
}
rsa->n = BN_bin2bn(modulus, modulusSize, NULL);
}
int Crypto_RSAEnc( CRYPTO_INFO *key,
UINT32 inDataSize,
BYTE *inData,
/*out*/ UINT32 *outDataSize,
/*out*/ BYTE *outData) {
RSA *rsa = (RSA *) key->keyInfo;
UINT32 paddedDataSize = RSA_size (rsa);
BYTE *paddedData = (BYTE *)malloc(sizeof(BYTE) * paddedDataSize);
int rc;
if (paddedData == NULL)
return -1;
*outDataSize = 0;
switch (key->encScheme) {
case CRYPTO_ES_RSAESPKCSv15:
if (RSA_padding_add_PKCS1_type_2(paddedData, paddedDataSize, inData, inDataSize) <= 0) {
rc = -1;
goto abort_egress;
}
break;
case CRYPTO_ES_RSAESOAEP_SHA1_MGF1:
if (RSA_padding_add_PKCS1_OAEP(paddedData,paddedDataSize,inData,inDataSize, (BYTE *) OAEP_P,OAEP_P_SIZE) <= 0 ) {
rc = -1;
goto abort_egress;
}
break;
default:
rc = -1;
goto abort_egress;
}
rc = RSA_public_encrypt(paddedDataSize, paddedData, outData, rsa, RSA_NO_PADDING);
if (rc == -1)
goto abort_egress;
*outDataSize = rc;
if (rc > 0) rc = 0;
goto egress;
abort_egress:
egress:
if (paddedData)
free (paddedData);
return rc;
}
int Crypto_RSADec( CRYPTO_INFO *key,
UINT32 inDataSize,
BYTE *inData,
/*out*/ UINT32 *outDataSize,
/*out*/ BYTE *outData) {
RSA *rsa = (RSA *) key->keyInfo;
UINT32 paddedDataSize = RSA_size (rsa);
BYTE *paddedData = (BYTE *)malloc(sizeof(BYTE) * paddedDataSize);
int rc;
if (paddedData == NULL)
goto abort_egress;
rc = RSA_private_decrypt(inDataSize, inData, paddedData, rsa, RSA_NO_PADDING);
if (rc == -1) {
vtpmlogerror(VTPM_LOG_CRYPTO, "RSA_private_decrypt: %s\n", ERR_error_string(ERR_get_error(), NULL));
goto abort_egress;
}
paddedDataSize = rc;
switch (key->encScheme) {
case CRYPTO_ES_RSAESPKCSv15:
rc = RSA_padding_check_PKCS1_type_2 (outData, paddedDataSize,
paddedData + 1, paddedDataSize - 1,
RSA_size(rsa));
if (rc == -1) {
vtpmlogerror(VTPM_LOG_CRYPTO, "RSA_padding_check_PKCS1_type_2: %s\n",
ERR_error_string(ERR_get_error(), NULL));
goto abort_egress;
}
*outDataSize = rc;
break;
case CRYPTO_ES_RSAESOAEP_SHA1_MGF1:
rc = RSA_padding_check_PKCS1_OAEP(outData, paddedDataSize,
paddedData + 1, paddedDataSize - 1,
RSA_size(rsa),
(BYTE *) OAEP_P, OAEP_P_SIZE);
if (rc == -1) {
vtpmlogerror(VTPM_LOG_CRYPTO, "RSA_padding_check_PKCS1_OAEP: %s\n",
ERR_error_string(ERR_get_error(), NULL));
goto abort_egress;
}
*outDataSize = rc;
break;
default:
*outDataSize = 0;
}
free(paddedData); paddedData = NULL;
goto egress;
abort_egress:
if (paddedData)
free (paddedData);
return -1;
egress:
return 0;
}
// Signs either a SHA1 digest of a message or a DER encoding of a message
// Textual messages MUST be encoded or Hashed before sending into this function
// It will NOT SHA the message.
int Crypto_RSASign( CRYPTO_INFO *key,
UINT32 inDataSize,
BYTE *inData,
/*out*/ UINT32 *sigSize,
/*out*/ BYTE *sig) {
int status;
unsigned int intSigSize;
switch(key->sigScheme) {
case CRYPTO_SS_RSASSAPKCS1v15_SHA1:
status = RSA_sign(NID_sha1, inData, inDataSize, sig, &intSigSize, (RSA *) key->keyInfo);
break;
case CRYPTO_SS_RSASSAPKCS1v15_DER:
// status = Crypto_RSA_sign_DER(NID_md5_sha1, inData, inDataSize, sig, &intSigSize, key);
vtpmlogerror(VTPM_LOG_CRYPTO, "Crypto: Unimplemented sign type (%d)\n", key->sigScheme);
status = 0;
break;
default:
status = 0;
}
if (status == 0) {
*sigSize = 0;
vtpmlogerror(VTPM_LOG_CRYPTO, "%s\n", ERR_error_string(ERR_get_error(), NULL));
return -1;
}
*sigSize = (UINT32) intSigSize;
return 0;
}
bool Crypto_RSAVerify( CRYPTO_INFO *key,
UINT32 inDataSize,
BYTE *inData,
UINT32 sigSize,
BYTE *sig) {
int status;
switch(key->sigScheme){
case CRYPTO_SS_RSASSAPKCS1v15_SHA1:
status = RSA_verify(NID_sha1, inData, inDataSize, sig, sigSize, (RSA *) key->keyInfo);
break;
case CRYPTO_SS_RSASSAPKCS1v15_DER:
//status = Crypto_RSA_verify_DER(NID_md5_sha1, inData, inDataSize, sig, sigSize, key);
vtpmlogerror(VTPM_LOG_CRYPTO, "Crypto: Unimplemented sign type (%d)\n", key->sigScheme);
status = 0;
break;
default:
status = 0;
}
if (status)
return(1);
else {
vtpmlogerror(VTPM_LOG_CRYPTO, "RSA verify: %s\n", ERR_error_string(ERR_get_error(), NULL));
return(0);
}
}
// helper which packs everything into a BIO!
// packs the parameters first, then the private key, then the public key
// if *io_buf is NULL, allocate it here as needed. otherwise its size is in
// *io_buflen
TPM_RESULT Crypto_RSAPackCryptoInfo (const CRYPTO_INFO* cryptoInfo,
BYTE ** io_buf, UINT32 * io_buflen) {
TPM_RESULT status = TPM_SUCCESS;
BYTE * buf;
long len, outlen = *io_buflen;
const long PARAMSLEN = 3*sizeof(UINT32);
RSA *rsa = (RSA *) cryptoInfo->keyInfo;
BIO *mem = BIO_new(BIO_s_mem());
// write the openssl keys to the BIO
if ( i2d_RSAPrivateKey_bio (mem, rsa) == 0 ) {
ERR_print_errors_fp (stderr);
ERRORDIE (TPM_SIZE);
}
if ( i2d_RSAPublicKey_bio (mem, rsa) == 0 ) {
ERR_print_errors_fp (stderr);
ERRORDIE (TPM_SIZE);
}
// get the buffer out
len = BIO_get_mem_data (mem, &buf);
// see if we need to allocate a return buffer
if (*io_buf == NULL) {
*io_buf = (BYTE*) malloc (PARAMSLEN + len);
if (*io_buf == NULL)
ERRORDIE (TPM_SIZE);
} else { // *io_buf is already allocated
if (outlen < len + PARAMSLEN)
ERRORDIE (TPM_SIZE); // but not large enough!
}
// copy over the parameters (three UINT32's starting at algorithmID)
memcpy (*io_buf, &cryptoInfo->algorithmID, PARAMSLEN);
// copy over the DER keys
memcpy (*io_buf + PARAMSLEN, buf, len);
*io_buflen = len + PARAMSLEN;
goto egress;
abort_egress:
egress:
BIO_free (mem);
return status;
}
// sets up ci, and returns the number of bytes read in o_lenread
TPM_RESULT Crypto_RSAUnpackCryptoInfo (CRYPTO_INFO * ci,
BYTE * in, UINT32 len,
UINT32 * o_lenread) {
TPM_RESULT status = TPM_SUCCESS;
long l;
BIO *mem;
RSA *rsa;
// first load up the params
l = 3 * sizeof(UINT32);
memcpy (&ci->algorithmID, in, l);
len -= l;
in += l;
// and now the openssl keys, private first
mem = BIO_new_mem_buf (in, len);
if ( (rsa = d2i_RSAPrivateKey_bio (mem, NULL)) == NULL ) {
ERR_print_errors_fp (stderr);
ERRORDIE (TPM_BAD_PARAMETER);
}
// now use the same RSA object and fill in the private key
if ( d2i_RSAPublicKey_bio (mem, &rsa) == NULL ) {
ERR_print_errors_fp (stderr);
ERRORDIE (TPM_BAD_PARAMETER);
}
ci->keyInfo = rsa; // needs to be freed somehow later
// FIXME: havent figured out yet how to tell how many bytes were read in the
// above oprations! so o_lenread is not set
goto egress;
abort_egress:
egress:
BIO_free (mem);
return status;
}