#include "DH_keys.h"
#include "DH_utils.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 <Security/oidsalg.h>
#include <YarrowConnection.h>
#define dhKeyDebug(args...) secinfo("dhKey", ## args)
#define DH_GENERATOR_DEFAULT DH_GENERATOR_2
DHBinaryKey::DHBinaryKey(DH *dhKey)
: mDhKey(dhKey)
{
}
DHBinaryKey::~DHBinaryKey()
{
if(mDhKey) {
DH_free(mDhKey);
mDhKey = NULL;
}
}
void DHBinaryKey::generateKeyBlob(
Allocator &allocator,
CssmData &blob,
CSSM_KEYBLOB_FORMAT &format,
AppleCSPSession &session,
const CssmKey *paramKey,
CSSM_KEYATTR_FLAGS &attrFlags)
{
switch(mKeyHeader.KeyClass) {
case CSSM_KEYCLASS_PUBLIC_KEY:
{
switch(format) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE:
format = DH_PUB_KEY_FORMAT;
break;
case DH_PUB_KEY_FORMAT:
case CSSM_KEYBLOB_RAW_FORMAT_X509:
break;
case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
format = DH_PUB_KEY_FORMAT;
break;
default:
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
}
assert(mDhKey != NULL);
CssmAutoData encodedKey(allocator);
CSSM_RETURN crtn = DHPublicKeyEncode(mDhKey, format,
encodedKey);
if(crtn) {
CssmError::throwMe(crtn);
}
blob = encodedKey.release();
break;
}
case CSSM_KEYCLASS_PRIVATE_KEY:
{
switch(format) {
case CSSM_KEYBLOB_RAW_FORMAT_NONE:
format = DH_PRIV_KEY_FORMAT;
break;
case DH_PRIV_KEY_FORMAT:
case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
break;
case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
{
assert(mDhKey != NULL);
if(mDhKey->pub_key == NULL) {
int irtn = DH_generate_key(mDhKey);
if(!irtn) {
throwRsaDsa("DH_generate_key");
}
}
assert(mDhKey->pub_key != NULL);
setUpData(blob,
BN_num_bytes(mDhKey->pub_key),
*DH_Factory::privAllocator);
BN_bn2bin(mDhKey->pub_key, blob);
format = DH_PUB_KEY_FORMAT;
return;
}
default:
CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
}
assert(mDhKey != NULL);
CssmAutoData encodedKey(allocator);
CSSM_RETURN crtn = DHPrivateKeyEncode(mDhKey, format,
encodedKey);
if(crtn) {
CssmError::throwMe(crtn);
}
blob = encodedKey.release();
break;
}
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
}
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;
}
}
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);
NSS_DHParameterBlock algParamBlock;
NSS_DHParameter &algParams = algParamBlock.params;
uint32 privValueLen = 0; SecNssCoder coder;
if(paramData != NULL) {
CSSM_RETURN crtn;
crtn = DHParamBlockDecode(*paramData, algParamBlock, coder);
if(crtn) {
CssmError::throwMe(crtn);
}
if(algParams.privateValueLength.Data) {
privValueLen = cssmDataToInt(algParams.privateValueLength);
}
size_t paramKeyBytes;
if(privValueLen) {
paramKeyBytes = (privValueLen + 7) / 8;
}
else {
paramKeyBytes = algParams.prime.Length;
const unsigned char *uo =
(const unsigned char *)algParams.prime.Data;
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");
memset(&algParamBlock, 0, sizeof(algParamBlock));
dhGenParams(keyBits, DH_GENERATOR_DEFAULT, 0, algParams, coder);
}
rPrivBinKey.mDhKey = DH_new();
if(rPrivBinKey.mDhKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
DH *dhKey = rPrivBinKey.mDhKey;
dhKey->p = cssmDataToBn(algParams.prime);
dhKey->g = cssmDataToBn(algParams.base);
dhKey->length = privValueLen;
cspDhDebug("private DH binary key dhKey %p", dhKey);
int irtn = DH_generate_key(dhKey);
if(!irtn) {
throwRsaDsa("DH_generate_key");
}
rPubBinKey.mDhKey = DH_new();
if(rPubBinKey.mDhKey == NULL) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
DH *pubDhKey = rPubBinKey.mDhKey;
pubDhKey->pub_key = BN_dup(dhKey->pub_key);
pubDhKey->p = BN_dup(dhKey->p);
pubDhKey->g = BN_dup(dhKey->g);
cspDhDebug("public DH binary key pubDhKey %p", pubDhKey);
}
DHKeyInfoProvider::DHKeyInfoProvider(
const CssmKey &cssmKey,
AppleCSPSession &session) :
CSPKeyInfoProvider(cssmKey, session)
{
}
CSPKeyInfoProvider *DHKeyInfoProvider::provider(
const CssmKey &cssmKey,
AppleCSPSession &session)
{
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 new DHKeyInfoProvider(cssmKey, session);
}
void DHKeyInfoProvider::CssmKeyToBinary(
CssmKey *paramKey, CSSM_KEYATTR_FLAGS &attrFlags, BinaryKey **binKey)
{
*binKey = NULL;
assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
switch(mKey.keyClass()) {
case CSSM_KEYCLASS_PUBLIC_KEY:
case CSSM_KEYCLASS_PRIVATE_KEY:
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
}
DH *dhKey = rawCssmKeyToDh(mKey);
DHBinaryKey *dhBinKey = new DHBinaryKey(dhKey);
*binKey = dhBinKey;
cspDhDebug("CssmKeyToBinary dhKey %p", dhKey);
}
void DHKeyInfoProvider::QueryKeySizeInBits(
CSSM_KEY_SIZE &keySize)
{
uint32 numBits = 0;
if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
}
DH *dhKey = rawCssmKeyToDh(mKey);
if(dhKey->p != NULL) {
numBits = DH_size(dhKey) * 8;
}
else {
assert(dhKey->pub_key != NULL);
numBits = BN_num_bytes(dhKey->pub_key) * 8;
}
DH_free(dhKey);
keySize.LogicalKeySizeInBits = numBits;
keySize.EffectiveKeySizeInBits = numBits;
}
bool DHKeyInfoProvider::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_PKCS3) {
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;
}
void DHKeyPairGenContext::generate(
const Context &context,
uint32 bitSize,
CssmData ¶ms, uint32 &attrCount, Context::Attr * &attrs) {
NSS_DHParameterBlock algParamBlock;
SecNssCoder coder;
NSS_DHParameter &algParams = algParamBlock.params;
dhGenParams(bitSize, DH_GENERATOR_DEFAULT, 0, algParams, coder);
algParamBlock.oid = CSSMOID_PKCS3;
CssmAutoData aDerData(session());
PRErrorCode perr;
perr = SecNssEncodeItemOdata(&algParamBlock, kSecAsn1DHParameterBlockTemplate,
aDerData);
if(perr) {
CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
}
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, NSS_DHParameter &algParams,
SecNssCoder &coder) {
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");
}
bnToCssmData(dhKey->p, algParams.prime, coder);
bnToCssmData(dhKey->g, algParams.base, coder);
CSSM_DATA &privValData = algParams.privateValueLength;
if(privValueLength) {
intToCssmData(privValueLength, privValData, coder);
}
else {
privValData.Data = NULL;
privValData.Length = 0;
}
DH_free(dhKey);
}