#include "RSA_DSA_utils.h"
#include "RSA_DSA_keys.h"
#include <opensslUtils/opensslAsn1.h>
#include <opensslUtils/opensslUtils.h>
#include <security_utilities/logging.h>
#include <security_utilities/debugging.h>
#include <openssl/bn.h>
#include <openssl/rsa.h>
#include <openssl/dsa.h>
#include <openssl/err.h>
#include <security_utilities/simpleprefs.h>
#include <security_utilities/threading.h>
#include <security_utilities/globalizer.h>
#include <CoreFoundation/CFNumber.h>
#define rsaMiscDebug(args...) secdebug("rsaMisc", ## args)
static void rsaLookupVal(
Dictionary &prefs,
CFStringRef key,
uint32 &val)
{
CFNumberRef cfVal = (CFNumberRef)prefs.getValue(key);
if(cfVal == NULL) {
return;
}
if(CFGetTypeID(cfVal) != CFNumberGetTypeID()) {
return;
}
SInt32 s32 = 0;
CFNumberRef cfLimit = CFNumberCreate(NULL, kCFNumberSInt32Type, &s32);
CFComparisonResult result = CFNumberCompare(cfVal, cfLimit, NULL);
CFRelease(cfLimit);
if(result == kCFCompareLessThan) {
return;
}
s32 = 0x7fffffff;
cfLimit = CFNumberCreate(NULL, kCFNumberSInt32Type, &s32);
result = CFNumberCompare(cfVal, cfLimit, NULL);
CFRelease(cfLimit);
if(result == kCFCompareGreaterThan) {
return;
}
SInt64 s64;
if(!CFNumberGetValue(cfVal, kCFNumberSInt64Type, &s64)) {
return;
}
val = (uint32)s64;
}
struct RSAKeySizes {
uint32 maxKeySize;
uint32 maxPubExponentSize;
RSAKeySizes();
};
RSAKeySizes::RSAKeySizes()
{
maxKeySize = RSA_MAX_KEY_SIZE;
maxPubExponentSize = RSA_MAX_PUB_EXPONENT_SIZE;
Dictionary* d = Dictionary::CreateDictionary(kRSAKeySizePrefsDomain, Dictionary::US_System, true);
if (!d)
{
return;
}
if (d->dict())
{
auto_ptr<Dictionary>apd(d);
rsaLookupVal(*apd, kRSAMaxKeySizePref, maxKeySize);
rsaLookupVal(*apd, kRSAMaxPublicExponentPref, maxPubExponentSize);
}
else
{
delete d;
}
}
static ModuleNexus<RSAKeySizes> rsaKeySizes;
uint32 rsaMaxKeySize()
{
return rsaKeySizes().maxKeySize;
}
uint32 rsaMaxPubExponentSize()
{
return rsaKeySizes().maxPubExponentSize;
}
RSA *contextToRsaKey(
const Context &context,
AppleCSPSession &session,
CSSM_KEYCLASS keyClass, CSSM_KEYUSE usage, bool &mallocdKey, CSSM_DATA &label) {
CssmKey &cssmKey =
context.get<CssmKey>(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY);
const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader;
if(hdr.AlgorithmId != CSSM_ALGID_RSA) {
CssmError::throwMe(CSSMERR_CSP_ALGID_MISMATCH);
}
if(hdr.KeyClass != keyClass) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
cspValidateIntendedKeyUsage(&hdr, usage);
cspVerifyKeyTimes(hdr);
return cssmKeyToRsa(cssmKey, session, mallocdKey, label);
}
RSA *cssmKeyToRsa(
const CssmKey &cssmKey,
AppleCSPSession &session,
bool &allocdKey, CSSM_DATA &label) {
RSA *rsaKey = NULL;
allocdKey = false;
const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
if(hdr->AlgorithmId != CSSM_ALGID_RSA) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
switch(hdr->BlobType) {
case CSSM_KEYBLOB_RAW:
rsaKey = rawCssmKeyToRsa(cssmKey, label);
allocdKey = true;
break;
case CSSM_KEYBLOB_REFERENCE:
{
BinaryKey &binKey = session.lookupRefKey(cssmKey);
RSABinaryKey *rsaBinKey = dynamic_cast<RSABinaryKey *>(&binKey);
if(rsaBinKey == NULL) {
rsaMiscDebug("cssmKeyToRsa: wrong BinaryKey subclass\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}
assert(rsaBinKey->mRsaKey != NULL);
rsaKey = rsaBinKey->mRsaKey;
break;
}
default:
CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
}
return rsaKey;
}
RSA *rawCssmKeyToRsa(
const CssmKey &cssmKey,
CSSM_DATA &label) {
const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
bool isPub;
bool isOaep = false;
assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
switch(hdr->AlgorithmId) {
case CSSM_ALGID_RSA:
break;
case CSSM_ALGMODE_PKCS1_EME_OAEP:
isOaep = true;
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
switch(hdr->KeyClass) {
case CSSM_KEYCLASS_PUBLIC_KEY:
switch(hdr->Format) {
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_INVALID_ATTR_PUBLIC_KEY_FORMAT);
}
if(isOaep && (hdr->Format != CSSM_KEYBLOB_RAW_FORMAT_X509)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT);
}
isPub = true;
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
switch(hdr->Format) {
case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: case CSSM_KEYBLOB_RAW_FORMAT_PKCS1: case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
break;
default:
CssmError::throwMe(
CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT);
}
if(isOaep && (hdr->Format != CSSM_KEYBLOB_RAW_FORMAT_PKCS8)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT);
}
isPub = false;
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
RSA *rsaKey = RSA_new();
if(rsaKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
CSSM_RETURN crtn;
if(isOaep) {
if(isPub) {
crtn = RSAOAEPPublicKeyDecode(rsaKey,
cssmKey.KeyData.Data, cssmKey.KeyData.Length,
&label);
}
else {
crtn = RSAOAEPPrivateKeyDecode(rsaKey,
cssmKey.KeyData.Data, cssmKey.KeyData.Length,
&label);
}
}
else {
if(isPub) {
crtn = RSAPublicKeyDecode(rsaKey, hdr->Format,
cssmKey.KeyData.Data, cssmKey.KeyData.Length);
}
else {
crtn = RSAPrivateKeyDecode(rsaKey, hdr->Format,
cssmKey.KeyData.Data, cssmKey.KeyData.Length);
}
}
if(crtn) {
RSA_free(rsaKey);
CssmError::throwMe(crtn);
}
bool badKey = false;
uint32 keySize = RSA_size(rsaKey) * 8;
if(keySize > rsaMaxKeySize()) {
rsaMiscDebug("rawCssmKeyToRsa: key size exceeded");
badKey = true;
}
else {
keySize = BN_num_bytes(rsaKey->e) * 8;
if(keySize > rsaMaxPubExponentSize()) {
badKey = true;
rsaMiscDebug("rawCssmKeyToRsa: pub exponent size exceeded");
}
}
if(badKey) {
RSA_free(rsaKey);
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
}
return rsaKey;
}
CSSM_RETURN dsaGetParamsFromKey(
DSA *partialKey,
const CssmKey ¶mKey,
AppleCSPSession &session)
{
bool allocdKey;
DSA *dsaParamKey = cssmKeyToDsa(paramKey, session, allocdKey);
if(dsaParamKey == NULL) {
errorLog0("dsaGetParamsFromKey: bad paramKey\n");
return CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
}
CSSM_RETURN crtn = CSSM_OK;
if((dsaParamKey->p == NULL) ||
(dsaParamKey->q == NULL) ||
(dsaParamKey->g == NULL)) {
errorLog0("dsaGetParamsFromKey: incomplete paramKey\n");
crtn = CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE;
goto abort;
}
rsaMiscDebug("dsaGetParamsFromKey: partialKey %p paramKey %p",
partialKey, dsaParamKey);
partialKey->q = BN_dup(dsaParamKey->q);
partialKey->p = BN_dup(dsaParamKey->p);
partialKey->g = BN_dup(dsaParamKey->g);
abort:
if(allocdKey) {
DSA_free(dsaParamKey);
}
return crtn;
}
DSA *contextToDsaKey(
const Context &context,
AppleCSPSession &session,
CSSM_KEYCLASS keyClass, CSSM_KEYUSE usage, bool &mallocdKey) {
CssmKey &cssmKey =
context.get<CssmKey>(CSSM_ATTRIBUTE_KEY, CSSMERR_CSP_MISSING_ATTR_KEY);
const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader;
if(hdr.AlgorithmId != CSSM_ALGID_DSA) {
CssmError::throwMe(CSSMERR_CSP_ALGID_MISMATCH);
}
if(hdr.KeyClass != keyClass) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
cspValidateIntendedKeyUsage(&hdr, usage);
cspVerifyKeyTimes(hdr);
DSA *rtnDsa = cssmKeyToDsa(cssmKey, session, mallocdKey);
if((keyClass == CSSM_KEYCLASS_PUBLIC_KEY) &&
(rtnDsa->p == NULL)) {
rsaMiscDebug("contextToDsaKey; partial DSA key %p", rtnDsa);
CssmKey *paramKey = context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
if(paramKey == NULL) {
rsaMiscDebug("contextToDsaKey: missing DSA params, no pub key in "
"context");
if(mallocdKey) {
DSA_free(rtnDsa);
mallocdKey = false;
}
CssmError::throwMe(CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE);
}
if(!mallocdKey) {
DSA *existKey = rtnDsa;
rtnDsa = DSA_new();
if(rtnDsa == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
rtnDsa->pub_key = BN_dup(existKey->pub_key);
rsaMiscDebug("contextToDsaKey; temp partial copy %p", rtnDsa);
mallocdKey = true;
}
CSSM_RETURN crtn = dsaGetParamsFromKey(rtnDsa, *paramKey, session);
if(crtn) {
if(mallocdKey) {
DSA_free(rtnDsa);
mallocdKey = false;
}
CssmError::throwMe(crtn);
}
}
return rtnDsa;
}
DSA *cssmKeyToDsa(
const CssmKey &cssmKey,
AppleCSPSession &session,
bool &allocdKey) {
DSA *dsaKey = NULL;
allocdKey = false;
const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
if(hdr->AlgorithmId != CSSM_ALGID_DSA) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
switch(hdr->BlobType) {
case CSSM_KEYBLOB_RAW:
dsaKey = rawCssmKeyToDsa(cssmKey, session, NULL);
allocdKey = true;
break;
case CSSM_KEYBLOB_REFERENCE:
{
BinaryKey &binKey = session.lookupRefKey(cssmKey);
DSABinaryKey *dsaBinKey = dynamic_cast<DSABinaryKey *>(&binKey);
if(dsaBinKey == NULL) {
rsaMiscDebug("cssmKeyToDsa: wrong BinaryKey subclass\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
}
assert(dsaBinKey->mDsaKey != NULL);
dsaKey = dsaBinKey->mDsaKey;
break;
}
default:
CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
}
return dsaKey;
}
DSA *rawCssmKeyToDsa(
const CssmKey &cssmKey,
AppleCSPSession &session,
const CssmKey *paramKey) {
const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
bool isPub;
assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
if(hdr->AlgorithmId != CSSM_ALGID_DSA) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
switch(hdr->KeyClass) {
case CSSM_KEYCLASS_PUBLIC_KEY:
switch(hdr->Format) {
case CSSM_KEYBLOB_RAW_FORMAT_FIPS186:
case CSSM_KEYBLOB_RAW_FORMAT_X509:
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2:
break;
default:
CssmError::throwMe(
CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT);
}
isPub = true;
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
switch(hdr->Format) {
case CSSM_KEYBLOB_RAW_FORMAT_FIPS186: case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL: case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: break;
case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH:
default:
CssmError::throwMe(
CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT);
}
isPub = false;
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
CSSM_RETURN crtn;
DSA *dsaKey = DSA_new();
if (dsaKey == NULL) {
crtn = CSSMERR_CSP_MEMORY_ERROR;
}
else {
if(dsaKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
if(isPub) {
crtn = DSAPublicKeyDecode(dsaKey, hdr->Format,
cssmKey.KeyData.Data,
cssmKey.KeyData.Length);
}
else {
crtn = DSAPrivateKeyDecode(dsaKey, hdr->Format,
cssmKey.KeyData.Data,
cssmKey.KeyData.Length);
}
}
if(crtn) {
if (dsaKey != NULL) {
DSA_free(dsaKey);
}
CssmError::throwMe(crtn);
}
if(isPub && (dsaKey->p == NULL) && (paramKey != NULL)) {
rsaMiscDebug("rawCssmKeyToDsa; updating dsaKey %p", dsaKey);
crtn = dsaGetParamsFromKey(dsaKey, *paramKey, session);
if(crtn) {
DSA_free(dsaKey);
CssmError::throwMe(crtn);
}
}
if(dsaKey->p != NULL) {
uint32 keySize = BN_num_bits(dsaKey->p);
if(keySize > DSA_MAX_KEY_SIZE) {
DSA_free(dsaKey);
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
}
}
return dsaKey;
}
void dsaKeyPrivToPub(
DSA *dsaKey)
{
assert(dsaKey != NULL);
assert(dsaKey->priv_key != NULL);
if(dsaKey->pub_key != NULL) {
return;
}
dsaKey->pub_key = BN_new();
if(dsaKey->pub_key == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
BN_CTX *ctx = BN_CTX_new();
if (ctx == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
int rtn = BN_mod_exp(dsaKey->pub_key,
dsaKey->g,
dsaKey->priv_key,
dsaKey->p,
ctx);
BN_CTX_free(ctx);
if(rtn == 0) {
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
}