#include "RSA_DSA_keys.h"
#include <opensslUtils/opensslUtils.h>
#include <opensslUtils/opensslAsn1.h>
#include <security_cdsa_utilities/cssmdata.h>
#include <AppleCSPSession.h>
#include <AppleCSPUtils.h>
#include <assert.h>
#include <security_utilities/debugging.h>
#include "RSA_DSA_utils.h"
#include <YarrowConnection.h>
#include <security_asn1/SecNssCoder.h>
#define RSA_PUB_EXPONENT 0x10001
#define rsaKeyDebug(args...) secinfo("rsaKey", ## args)
RSABinaryKey::RSABinaryKey(RSA *rsaKey)
: mRsaKey(rsaKey),
mOaep(false),
mLabel(Allocator::standard())
{
}
RSABinaryKey::~RSABinaryKey()
{
if(mRsaKey) {
RSA_free(mRsaKey);
mRsaKey = NULL;
}
}
void RSABinaryKey::setOaep(
const CSSM_DATA &label)
{
mLabel.copy(label);
mOaep = true;
}
void RSABinaryKey::generateKeyBlob(
Allocator &allocator,
CssmData &blob,
CSSM_KEYBLOB_FORMAT &format,
AppleCSPSession &session,
const CssmKey *paramKey,
CSSM_KEYATTR_FLAGS &attrFlags)
{
bool isPub;
CSSM_RETURN crtn;
switch(mKeyHeader.KeyClass) {
case CSSM_KEYCLASS_PUBLIC_KEY:
isPub = true;
switch(format) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE:
format = RSA_PUB_KEY_FORMAT; break;
case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
if(mOaep) {
format = CSSM_KEYBLOB_RAW_FORMAT_X509;
}
else {
format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
}
break;
case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
case CSSM_KEYBLOB_RAW_FORMAT_X509:
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
break;
default:
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
}
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
isPub = false;
switch(format) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE: format = RSA_PRIV_KEY_FORMAT;
break;
case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
if(mOaep) {
format = CSSM_KEYBLOB_RAW_FORMAT_X509;
}
else {
format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1;
}
isPub = true;
break;
case CSSM_KEYBLOB_RAW_FORMAT_PKCS1:
case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
break;
default:
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
}
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
CssmAutoData encodedKey(allocator);
if(mOaep) {
CSSM_DATA label = mLabel;
if(isPub) {
crtn = RSAOAEPPublicKeyEncode(mRsaKey, &label, encodedKey);
}
else {
crtn = RSAOAEPPrivateKeyEncode(mRsaKey, &label, encodedKey);
}
}
else {
if(isPub) {
crtn = RSAPublicKeyEncode(mRsaKey, format, descData(), encodedKey);
}
else {
crtn = RSAPrivateKeyEncode(mRsaKey, format, descData(), encodedKey);
}
}
if(crtn) {
CssmError::throwMe(crtn);
}
blob = encodedKey.release();
}
void RSAKeyPairGenContext::generate(
const Context &context,
CssmKey &pubKey,
CssmKey &privKey)
{
RSABinaryKey *pubBinKey = new RSABinaryKey();
RSABinaryKey *privBinKey = new RSABinaryKey();
try {
AppleKeyPairGenContext::generate(context,
session(),
pubKey,
pubBinKey,
privKey,
privBinKey);
}
catch (...) {
delete pubBinKey;
delete privBinKey;
throw;
}
}
void RSAKeyPairGenContext::generate(const Context &context, uint32, CssmData ¶ms, uint32 &attrCount, Context::Attr * &attrs) {
CssmError::throwMe(CSSM_ERRCODE_FUNCTION_NOT_IMPLEMENTED);
}
void RSAKeyPairGenContext::generate(
const Context &context,
BinaryKey &pubBinKey,
BinaryKey &privBinKey,
uint32 &keyBits)
{
RSABinaryKey &rPubBinKey =
dynamic_cast<RSABinaryKey &>(pubBinKey);
RSABinaryKey &rPrivBinKey =
dynamic_cast<RSABinaryKey &>(privBinKey);
keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
if(keyBits > rsaMaxKeySize()) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
}
rPrivBinKey.mRsaKey = RSA_generate_key(keyBits,
RSA_PUB_EXPONENT,
NULL, NULL);
if(rPrivBinKey.mRsaKey == NULL) {
rsaKeyDebug("RSA_generate_key returned NULL");
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); }
rPubBinKey.mRsaKey = RSA_new();
if(rPrivBinKey.mRsaKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
RSA *pub = rPubBinKey.mRsaKey;
RSA *priv = rPrivBinKey.mRsaKey;
pub->n = BN_dup(priv->n);
pub->e = BN_dup(priv->e);
if((pub->n == NULL) || (pub->e == NULL)) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
}
RSAKeyInfoProvider::RSAKeyInfoProvider(
const CssmKey &cssmKey,
AppleCSPSession &session) :
CSPKeyInfoProvider(cssmKey, session)
{
}
CSPKeyInfoProvider *RSAKeyInfoProvider::provider(
const CssmKey &cssmKey,
AppleCSPSession &session)
{
switch(cssmKey.algorithm()) {
case CSSM_ALGID_RSA:
case CSSM_ALGMODE_PKCS1_EME_OAEP:
break;
default:
return NULL;
}
switch(cssmKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
case CSSM_KEYCLASS_PRIVATE_KEY:
break;
default:
return NULL;
}
return new RSAKeyInfoProvider(cssmKey, session);
}
void RSAKeyInfoProvider::CssmKeyToBinary(
CssmKey *paramKey, CSSM_KEYATTR_FLAGS &attrFlags, BinaryKey **binKey)
{
*binKey = NULL;
RSA *rsaKey = NULL;
CSSM_DATA label = {0, NULL};
rsaKey = rawCssmKeyToRsa(mKey, label);
RSABinaryKey *rsaBinKey = new RSABinaryKey(rsaKey);
*binKey = rsaBinKey;
if(label.Data) {
rsaBinKey->setOaep(label);
free(label.Data);
}
}
void RSAKeyInfoProvider::QueryKeySizeInBits(
CSSM_KEY_SIZE &keySize)
{
RSA *rsaKey = NULL;
CSSM_DATA label = {0, NULL};
if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
}
rsaKey = rawCssmKeyToRsa(mKey, label);
keySize.LogicalKeySizeInBits = RSA_size(rsaKey) * 8;
keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits;
RSA_free(rsaKey);
if(label.Data) {
free(label.Data);
}
}
bool RSAKeyInfoProvider::getHashableBlob(
Allocator &allocator,
CssmData &blob) {
assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
bool useAsIs = false;
switch(mKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
if(mKey.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_PKCS1) {
useAsIs = true;
}
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
break;
default:
assert(0);
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
if(useAsIs) {
const CssmData &keyBlob = CssmData::overlay(mKey.KeyData);
copyCssmData(keyBlob, blob, allocator);
return true;
}
return false;
}
DSABinaryKey::DSABinaryKey(DSA *dsaKey)
: mDsaKey(dsaKey)
{
}
DSABinaryKey::~DSABinaryKey()
{
if(mDsaKey) {
DSA_free(mDsaKey);
mDsaKey = NULL;
}
}
void DSABinaryKey::generateKeyBlob(
Allocator &allocator,
CssmData &blob,
CSSM_KEYBLOB_FORMAT &format,
AppleCSPSession &session,
const CssmKey *paramKey,
CSSM_KEYATTR_FLAGS &attrFlags)
{
bool isPub;
CSSM_RETURN crtn;
switch(mKeyHeader.KeyClass) {
case CSSM_KEYCLASS_PUBLIC_KEY:
isPub = true;
switch(format) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE:
format = DSA_PUB_KEY_FORMAT; break;
case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
case CSSM_KEYBLOB_RAW_FORMAT_X509:
case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
break;
default:
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
}
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
isPub = false;
switch(format) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE:
format = DSA_PRIV_KEY_FORMAT; break;
case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
dsaKeyPrivToPub(mDsaKey);
isPub = true;
break;
case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
break;
default:
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
}
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
DSA *dsaToEncode = mDsaKey;
DSA *dsaUpgrade = NULL;
if(isPub &&
(mDsaKey->p == NULL) &&
(paramKey != NULL)) {
dsaUpgrade = DSA_new();
if(dsaUpgrade == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
dsaUpgrade->pub_key = BN_dup(mDsaKey->pub_key);
crtn = dsaGetParamsFromKey(dsaUpgrade, *paramKey, session);
if(crtn) {
DSA_free(dsaUpgrade);
CssmError::throwMe(crtn);
}
dsaToEncode = dsaUpgrade;
attrFlags &= ~CSSM_KEYATTR_PARTIAL;
}
if(!isPub && (dsaToEncode->pub_key == NULL)) {
dsaKeyPrivToPub(dsaToEncode);
}
CssmAutoData encodedKey(allocator);
if(isPub) {
crtn = DSAPublicKeyEncode(dsaToEncode, format, descData(), encodedKey);
}
else {
crtn = DSAPrivateKeyEncode(dsaToEncode, format, descData(), encodedKey);
}
if(dsaUpgrade != NULL) {
DSA_free(dsaUpgrade);
}
if(crtn) {
CssmError::throwMe(crtn);
}
blob = encodedKey.release();
}
void DSAKeyPairGenContext::generate(
const Context &context,
CssmKey &pubKey,
CssmKey &privKey)
{
DSABinaryKey *pubBinKey = new DSABinaryKey();
DSABinaryKey *privBinKey = new DSABinaryKey();
try {
AppleKeyPairGenContext::generate(context,
session(),
pubKey,
pubBinKey,
privKey,
privBinKey);
}
catch (...) {
delete pubBinKey;
delete privBinKey;
throw;
}
}
void DSAKeyPairGenContext::generate(
const Context &context,
BinaryKey &pubBinKey,
BinaryKey &privBinKey,
uint32 &keyBits)
{
DSABinaryKey &rPubBinKey =
dynamic_cast<DSABinaryKey &>(pubBinKey);
DSABinaryKey &rPrivBinKey =
dynamic_cast<DSABinaryKey &>(privBinKey);
keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
if(keyBits > DSA_MAX_KEY_SIZE) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
}
CssmData *paramData = context.get<CssmData>(CSSM_ATTRIBUTE_ALG_PARAMS);
NSS_DSAAlgParams algParams;
SecNssCoder coder; if(paramData != NULL) {
CSSM_RETURN crtn = DSADecodeAlgParams(algParams, paramData->Data,
(unsigned)paramData->Length, coder);
if(crtn) {
CssmError::throwMe(crtn);
}
}
else {
dsaGenParams(keyBits, NULL, 0, algParams, coder);
}
rPrivBinKey.mDsaKey = DSA_new();
if(rPrivBinKey.mDsaKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
DSA *dsaKey = rPrivBinKey.mDsaKey;
dsaKey->p = cssmDataToBn(algParams.p);
dsaKey->q = cssmDataToBn(algParams.q);
dsaKey->g = cssmDataToBn(algParams.g);
int irtn = DSA_generate_key(dsaKey);
if(!irtn) {
throwRsaDsa("DSA_generate_key");
}
rPubBinKey.mDsaKey = DSA_new();
if(rPrivBinKey.mDsaKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
DSA *pub = rPubBinKey.mDsaKey;
DSA *priv = rPrivBinKey.mDsaKey;
pub->p = BN_dup(priv->p);
pub->q = BN_dup(priv->q);
pub->g = BN_dup(priv->g);
pub->pub_key = BN_dup(priv->pub_key);
if((pub->p == NULL) || (pub->q == NULL) || (pub->g == NULL) ||
(pub->pub_key == NULL)) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
}
void DSAKeyPairGenContext::generate(
const Context &context,
uint32 bitSize,
CssmData ¶ms,
uint32 &attrCount,
Context::Attr * &attrs)
{
void *seed = NULL;
unsigned seedLen = 0;
CssmData *seedData = context.get<CssmData>(CSSM_ATTRIBUTE_SEED);
if(seedData) {
seed = seedData->data();
seedLen = (unsigned)seedData->length();
}
NSS_DSAAlgParams algParams;
SecNssCoder coder;
dsaGenParams(bitSize, seed, seedLen, algParams, coder);
CssmAutoData aDerData(session());
DSAEncodeAlgParams(algParams, aDerData);
CSSM_DATA_PTR derData = (CSSM_DATA_PTR)session().malloc(sizeof(CSSM_DATA));
*derData = aDerData.release();
freeGenAttrs();
mGenAttrs = (Context::Attr *)session().malloc(sizeof(Context::Attr));
mGenAttrs->AttributeType = CSSM_ATTRIBUTE_ALG_PARAMS;
mGenAttrs->AttributeLength = sizeof(CSSM_DATA);
mGenAttrs->Attribute.Data = derData;
copyCssmData(CssmData::overlay(*derData), params, session());
attrCount = 1;
attrs = mGenAttrs;
}
void DSAKeyPairGenContext::freeGenAttrs()
{
if(mGenAttrs == NULL) {
return;
}
if(mGenAttrs->Attribute.Data) {
if(mGenAttrs->Attribute.Data->Data) {
session().free(mGenAttrs->Attribute.Data->Data);
}
session().free(mGenAttrs->Attribute.Data);
}
session().free(mGenAttrs);
}
void DSAKeyPairGenContext::dsaGenParams(
uint32 keySizeInBits,
const void *inSeed, unsigned inSeedLen,
NSS_DSAAlgParams &algParams,
SecNssCoder &coder) {
unsigned char seedBuf[SHA1_DIGEST_SIZE];
void *seedPtr;
if((keySizeInBits < DSA_MIN_KEY_SIZE) ||
(keySizeInBits > DSA_MAX_KEY_SIZE) ||
(keySizeInBits & DSA_KEY_BITS_MASK)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
}
if(inSeed == NULL) {
session().getRandomBytes(SHA1_DIGEST_SIZE, seedBuf);
seedPtr = seedBuf;
}
else if(inSeedLen == SHA1_DIGEST_SIZE) {
seedPtr = (void *)inSeed;
}
else {
cspGenSha1Hash(inSeed, inSeedLen, seedBuf);
seedPtr = seedBuf;
}
DSA *dsaKey = DSA_generate_parameters(keySizeInBits,
(unsigned char *)seedPtr,
SHA1_DIGEST_SIZE,
NULL, NULL, NULL,
NULL);
if(dsaKey == NULL) {
throwRsaDsa("DSA_generate_parameters");
}
bnToCssmData(dsaKey->p, algParams.p, coder);
bnToCssmData(dsaKey->q, algParams.q, coder);
bnToCssmData(dsaKey->g, algParams.g, coder);
DSA_free(dsaKey);
}
DSAKeyInfoProvider::DSAKeyInfoProvider(
const CssmKey &cssmKey,
AppleCSPSession &session) :
CSPKeyInfoProvider(cssmKey, session)
{
}
CSPKeyInfoProvider *DSAKeyInfoProvider::provider(
const CssmKey &cssmKey,
AppleCSPSession &session)
{
switch(cssmKey.algorithm()) {
case CSSM_ALGID_DSA:
break;
default:
return NULL;
}
switch(cssmKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
case CSSM_KEYCLASS_PRIVATE_KEY:
break;
default:
return NULL;
}
return new DSAKeyInfoProvider(cssmKey, session);
}
void DSAKeyInfoProvider::CssmKeyToBinary(
CssmKey *paramKey, CSSM_KEYATTR_FLAGS &attrFlags, BinaryKey **binKey)
{
*binKey = NULL;
DSA *dsaKey = NULL;
dsaKey = rawCssmKeyToDsa(mKey, mSession, paramKey);
if(dsaKey->p == NULL) {
attrFlags |= CSSM_KEYATTR_PARTIAL;
}
else {
attrFlags &= ~CSSM_KEYATTR_PARTIAL;
}
DSABinaryKey *dsaBinKey = new DSABinaryKey(dsaKey);
*binKey = dsaBinKey;
}
void DSAKeyInfoProvider::QueryKeySizeInBits(
CSSM_KEY_SIZE &keySize)
{
DSA *dsaKey = NULL;
if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
}
dsaKey = rawCssmKeyToDsa(mKey,
mSession,
NULL); if(dsaKey->p != NULL) {
keySize.LogicalKeySizeInBits = BN_num_bits(dsaKey->p);
keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits;
DSA_free(dsaKey);
}
else {
keySize.LogicalKeySizeInBits = BN_num_bits(dsaKey->pub_key);
DSA_free(dsaKey);
CssmError::throwMe(CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE);
}
}
bool DSAKeyInfoProvider::getHashableBlob(
Allocator &allocator,
CssmData &blob) {
return false;
}