#include "DH_keys.h"
#include "DH_utils.h"
#include <opensslUtils/opensslUtils.h>
#include <opensslUtils/openRsaSnacc.h>
#include <Security/cssmdata.h>
#include <AppleCSP/AppleCSPSession.h>
#include <AppleCSP/AppleCSPUtils.h>
#include <assert.h>
#include <Security/debugging.h>
#include <AppleCSP/YarrowConnection.h>
#include <Security/appleoids.h>
#include <Security/cdsaUtils.h>
#include <Security/asn-octs.h>
#include <Security/sm_vdatypes.h>
#define dhKeyDebug(args...) debug("dhKey", ## args)
#define DH_GENERATOR_DEFAULT DH_GENERATOR_2
DHBinaryKey::DHBinaryKey(DH *dhKey)
: mDhKey(dhKey)
{
mPubKey.Data = NULL;
mPubKey.Length = 0;
}
DHBinaryKey::DHBinaryKey(const CSSM_DATA *pubBlob)
: mDhKey(NULL)
{
setPubBlob(pubBlob);
}
DHBinaryKey::~DHBinaryKey()
{
if(mDhKey) {
assert(mPubKey.Data == NULL);
DH_free(mDhKey);
mDhKey = NULL;
}
if(mPubKey.Data) {
assert(mDhKey == NULL);
DH_Factory::privAllocator->free(mPubKey.Data);
mPubKey.Data = NULL;
mPubKey.Length = 0;
}
}
void DHBinaryKey::generateKeyBlob(
CssmAllocator &allocator,
CssmData &blob,
CSSM_KEYBLOB_FORMAT &format)
{
switch(mKeyHeader.KeyClass) {
case CSSM_KEYCLASS_PUBLIC_KEY:
{
assert(mDhKey == NULL);
assert(mPubKey.Data != NULL);
format = DH_PUB_KEY_FORMAT;
copyCssmData(CssmData::overlay(mPubKey), blob, allocator);
break;
}
case CSSM_KEYCLASS_PRIVATE_KEY:
{
assert(mDhKey != NULL);
assert(mPubKey.Data == NULL);
format = DH_PRIV_KEY_FORMAT;
CssmAutoData encodedKey(allocator);
CSSM_RETURN crtn = DHPrivateKeyEncode(mDhKey, encodedKey);
if(crtn) {
CssmError::throwMe(crtn);
}
blob = encodedKey.release();
break;
}
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
}
void DHBinaryKey::setPubBlob(const CSSM_DATA *pubBlob)
{
assert(mDhKey == NULL);
assert(mPubKey.Data == NULL);
setUpData(mPubKey, pubBlob->Length, *DH_Factory::privAllocator);
memmove(mPubKey.Data, pubBlob->Data, pubBlob->Length);
}
void DHBinaryKey::setPubBlob(DH *privKey)
{
assert(mDhKey == NULL);
assert(mPubKey.Data == NULL);
setUpData(mPubKey, BN_num_bytes(privKey->pub_key),
*DH_Factory::privAllocator);
BN_bn2bin(privKey->pub_key, mPubKey.Data);
}
void DHKeyPairGenContext::generate(
const Context &context,
CssmKey &pubKey,
CssmKey &privKey)
{
DHBinaryKey *pubBinKey = new DHBinaryKey();
DHBinaryKey *privBinKey = new DHBinaryKey();
try {
AppleKeyPairGenContext::generate(context,
session(),
pubKey,
pubBinKey,
privKey,
privBinKey);
}
catch (...) {
delete pubBinKey;
delete privBinKey;
throw;
}
}
static uint32 bigIntStrToInt(
const BigIntegerStr &bint,
CSSM_RETURN toThrow) {
size_t bytes = bint.Len();
if(bytes > 4) {
dhKeyDebug("DH integer overflow");
if(toThrow) {
CssmError::throwMe(toThrow);
}
else {
return 0;
}
}
uint32 rtn = 0;
const unsigned char *uo = (const unsigned char *)bint.Octs();
for(size_t i=0; i<bytes; i++) {
rtn <<= 8;
rtn |= uo[i];
}
return rtn;
}
void DHKeyPairGenContext::generate(
const Context &context,
BinaryKey &pubBinKey,
BinaryKey &privBinKey,
uint32 &keyBits)
{
DHBinaryKey &rPubBinKey =
dynamic_cast<DHBinaryKey &>(pubBinKey);
DHBinaryKey &rPrivBinKey =
dynamic_cast<DHBinaryKey &>(privBinKey);
keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
CssmData *paramData = context.get<CssmData>(CSSM_ATTRIBUTE_ALG_PARAMS);
DHParameterBlock algParamBlock;
DHParameter *algParams = NULL;
uint32 privValueLen = 0;
if(paramData != NULL) {
try {
SC_decodeAsnObj(*paramData, algParamBlock);
}
catch(...) {
if(algParamBlock.params) {
delete algParamBlock.params;
algParamBlock.params = NULL;
}
algParamBlock.params = new DHParameter;
try {
SC_decodeAsnObj(*paramData, *algParamBlock.params);
dhKeyDebug("Trying openssl-style DH param decoding");
}
catch(...) {
dhKeyDebug("openssl-style DH param decoding FAILED");
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
}
}
algParams = algParamBlock.params;
if(algParams == NULL) {
dhKeyDebug("Bad DH param decoding");
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
}
if(algParams->privateValueLength) {
privValueLen = bigIntStrToInt(*algParams->privateValueLength,
CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
}
uint32 paramKeyBytes;
if(privValueLen) {
paramKeyBytes = (privValueLen + 7) / 8;
}
else {
paramKeyBytes = algParams->prime.Len();
const unsigned char *uo =
(const unsigned char *)algParams->prime.Octs();
if(*uo == 0) {
paramKeyBytes--;
}
}
uint32 reqBytes = (keyBits + 7) / 8;
if(paramKeyBytes != reqBytes) {
dhKeyDebug("DH key size mismatch (req %d param %d)",
(int)reqBytes, (int)paramKeyBytes);
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE);
}
}
else {
dhKeyDebug("DH implicit alg param calculation");
algParamBlock.params = new DHParameter;
algParams = algParamBlock.params;
dhGenParams(keyBits, DH_GENERATOR_DEFAULT, 0, *algParams);
}
rPrivBinKey.mDhKey = DH_new();
if(rPrivBinKey.mDhKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
DH *dhKey = rPrivBinKey.mDhKey;
dhKey->p = bigIntStrToBn(algParams->prime);
dhKey->g = bigIntStrToBn(algParams->base);
dhKey->length = privValueLen;
int irtn = DH_generate_key(dhKey);
if(!irtn) {
throwRsaDsa("DH_generate_key");
}
rPubBinKey.setPubBlob(dhKey);
}
DHKeyInfoProvider::DHKeyInfoProvider(
const CssmKey &cssmKey) :
CSPKeyInfoProvider(cssmKey)
{
switch(cssmKey.algorithm()) {
case CSSM_ALGID_DH:
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
switch(cssmKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
case CSSM_KEYCLASS_PRIVATE_KEY:
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
return;
}
void DHKeyInfoProvider::CssmKeyToBinary(
BinaryKey **binKey)
{
*binKey = NULL;
assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
switch(mKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
{
DHBinaryKey *dhKey = new DHBinaryKey(&mKey.KeyData);
*binKey = dhKey;
break;
}
case CSSM_KEYCLASS_PRIVATE_KEY:
{
DH *dhKey = rawCssmKeyToDh(mKey);
DHBinaryKey *dhBinKey = new DHBinaryKey(dhKey);
*binKey = dhBinKey;
break;
}
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
}
void DHKeyInfoProvider::QueryKeySizeInBits(
CSSM_KEY_SIZE &keySize)
{
uint32 numBits = 0;
if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
}
switch(mKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
numBits = mKey.KeyData.Length * 8;
break;
case CSSM_KEYCLASS_PRIVATE_KEY:
{
DH *dhKey = rawCssmKeyToDh(mKey);
numBits = DH_size(dhKey) * 8;
DH_free(dhKey);
break;
}
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
keySize.LogicalKeySizeInBits = numBits;
keySize.EffectiveKeySizeInBits = numBits;
}
void DHKeyPairGenContext::generate(
const Context &context,
uint32 bitSize,
CssmData ¶ms, uint32 &attrCount, Context::Attr * &attrs) {
DHParameterBlock algParamBlock;
algParamBlock.params = new DHParameter;
DHParameter *algParams = algParamBlock.params;
dhGenParams(bitSize, DH_GENERATOR_DEFAULT, 0, *algParams);
algParamBlock.oid.Set(pkcs_3_arc);
size_t maxSize = sizeofBigInt(algParams->prime) +
sizeofBigInt(algParams->base)
+ 30; if(algParams->privateValueLength) {
maxSize += sizeofBigInt(*algParams->privateValueLength);
}
CssmAutoData aDerData(session());
SC_encodeAsnObj(algParamBlock, aDerData, maxSize);
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 DHKeyPairGenContext::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 DHKeyPairGenContext::dhGenParams(
uint32 keySizeInBits,
unsigned g, int privValueLength, DHParameter &algParams)
{
if((keySizeInBits < DH_MIN_KEY_SIZE) ||
(keySizeInBits > DH_MAX_KEY_SIZE)) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
}
DH *dhKey = DH_generate_parameters(keySizeInBits, g, NULL, NULL);
if(dhKey == NULL) {
throwRsaDsa("DSA_generate_parameters");
}
bnToBigIntStr(dhKey->p, algParams.prime);
bnToBigIntStr(dhKey->g, algParams.base);
if(privValueLength) {
algParams.privateValueLength = new BigIntegerStr();
snaccIntToBigIntegerStr(g, *algParams.privateValueLength);
}
DH_free(dhKey);
}