#include <HMACSHA1.h>
#include <pbkdf2.h>
#include <pbkdDigest.h>
#include <pkcs12Derive.h>
#include "AppleCSPSession.h"
#include "AppleCSPUtils.h"
#include "AppleCSPContext.h"
#include "cspdebugging.h"
#include <security_cdsa_utilities/context.h>
#include <DH_exchange.h>
#include "FEEAsymmetricContext.h"
#define PBKDF2_MIN_SALT 8
#define PBKDF2_MIN_ITER_CNT 1000
#define ALLOW_ZERO_PASSWORD 1
void AppleCSPSession::DeriveKey_PBKDF2(
const Context &context,
const CssmData &Param,
CSSM_DATA *keyData)
{
if(Param.Length != sizeof(CSSM_PKCS5_PBKDF2_PARAMS)) {
errorLog0("DeriveKey_PBKDF2: Param wrong size\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER);
}
const CSSM_PKCS5_PBKDF2_PARAMS *pbkdf2Params =
reinterpret_cast<const CSSM_PKCS5_PBKDF2_PARAMS *>(Param.Data);
if(pbkdf2Params == NULL) {
errorLog0("DeriveKey_PBKDF2: null Param.Data\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
}
CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
CSSM_SIZE passphraseLen = 0;
uint8 *passphrase = NULL;
if(passKey != NULL) {
AppleCSPContext::symmetricKeyBits(context, *this,
CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE,
passphrase, passphraseLen);
}
else {
passphraseLen = pbkdf2Params->Passphrase.Length;
passphrase = pbkdf2Params->Passphrase.Data;
}
#if !ALLOW_ZERO_PASSWORD
if(passphrase == NULL) {
errorLog0("DeriveKey_PBKDF2: null Passphrase\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
}
if(passphraseLen == 0) {
errorLog0("DeriveKey_PBKDF2: zero length passphrase\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER);
}
#endif
if(pbkdf2Params->PseudoRandomFunction !=
CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1) {
errorLog0("DeriveKey_PBKDF2: invalid PRF\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
CssmData salt = context.get<CssmData>(CSSM_ATTRIBUTE_SALT,
CSSMERR_CSP_MISSING_ATTR_SALT);
if((salt.Data == NULL) || (salt.Length < PBKDF2_MIN_SALT)){
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT);
}
uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT,
CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT);
if(iterCount < PBKDF2_MIN_ITER_CNT) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT);
}
uint32 tempLen = salt.Length + 4;
if(tempLen < kSHA1DigestSize) {
tempLen = kSHA1DigestSize;
}
tempLen += (2 * kSHA1DigestSize);
CSSM_DATA tempData = {0, NULL};
setUpData(tempData, tempLen, privAllocator);
pbkdf2 (hmacsha1,
kSHA1DigestSize,
passphrase, passphraseLen,
salt.Data, salt.Length,
iterCount,
keyData->Data, keyData->Length,
tempData.Data);
freeData(&tempData, privAllocator, false);
}
void AppleCSPSession::DeriveKey_PKCS5_V1_5(
const Context &context,
CSSM_ALGORITHMS algId,
const CssmData &Param, CSSM_DATA *keyData) {
CSSM_DATA pwd = {0, NULL};
CssmCryptoData *cryptData =
context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED);
if((cryptData != NULL) && (cryptData->Param.Length != 0)) {
pwd = cryptData->Param;
}
else {
CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
if (passKey != NULL) {
AppleCSPContext::symmetricKeyBits(context, *this,
CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE,
pwd.Data, pwd.Length);
}
}
if(pwd.Data == NULL) {
errorLog0("DeriveKey_PKCS5_V1_5: null Passphrase\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
}
if(pwd.Length == 0) {
errorLog0("DeriveKey_PKCS5_V1_5: zero length passphrase\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER);
}
CSSM_ALGORITHMS hashAlg;
unsigned digestLen;
bool opensslAlg = false;
switch(algId) {
case CSSM_ALGID_PKCS5_PBKDF1_MD5:
hashAlg = CSSM_ALGID_MD5;
digestLen = kMD5DigestSize;
break;
case CSSM_ALGID_PKCS5_PBKDF1_MD2:
hashAlg = CSSM_ALGID_MD2;
digestLen = kMD2DigestSize;
break;
case CSSM_ALGID_PKCS5_PBKDF1_SHA1:
hashAlg = CSSM_ALGID_SHA1;
digestLen = kSHA1DigestSize;
break;
case CSSM_ALGID_PBE_OPENSSL_MD5:
hashAlg = CSSM_ALGID_MD5;
digestLen = kMD5DigestSize;
opensslAlg = true;
break;
default:
assert(0);
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
CSSM_DATA iv = Param;
if(!opensslAlg && ((keyData->Length + iv.Length) > digestLen)) {
errorLog0("DeriveKey_PKCS5_V1_5: requested length larger than digest\n");
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
}
CssmData salt = context.get<CssmData>(CSSM_ATTRIBUTE_SALT,
CSSMERR_CSP_MISSING_ATTR_SALT);
if(salt.Data == NULL) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT);
}
uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT,
CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT);
DigestCtx ctx;
uint8 *keyDataP = keyData->Data;
uint32 keyBytesToGo = keyData->Length;
uint8 *ivDataP = iv.Data;
uint32 ivBytesToGo = iv.Length;
bool looping = false; unsigned char digestOut[kMaxDigestSize];
for(;;) {
DigestCtxInit(&ctx, hashAlg);
if(looping) {
DigestCtxUpdate(&ctx, digestOut, digestLen);
}
DigestCtxUpdate(&ctx, pwd.Data, pwd.Length);
DigestCtxUpdate(&ctx, salt.Data, salt.Length);
DigestCtxFinal(&ctx, digestOut);
for(unsigned dex=1; dex<iterCount; dex++) {
DigestCtxInit(&ctx, hashAlg);
DigestCtxUpdate(&ctx, digestOut, digestLen);
DigestCtxFinal(&ctx, digestOut);
}
uint32 bytesAvail = digestLen;
uint32 toMove = (keyBytesToGo > bytesAvail) ? bytesAvail : keyBytesToGo;
memmove(keyDataP, digestOut, toMove);
uint8 *remainder = digestOut + toMove;
bytesAvail -= toMove;
keyDataP += toMove;
keyBytesToGo -= toMove;
if(ivBytesToGo && bytesAvail) {
toMove = (ivBytesToGo > bytesAvail) ? bytesAvail : ivBytesToGo;
memmove(ivDataP, remainder, toMove);
ivDataP += toMove;
ivBytesToGo -= toMove;
}
if((keyBytesToGo == 0) && (ivBytesToGo == 0)) {
break;
}
assert(opensslAlg == true);
looping = true;
}
DigestCtxFree(&ctx);
}
void AppleCSPSession::DeriveKey(
CSSM_CC_HANDLE CCHandle,
const Context &context,
CssmData &Param,
uint32 KeyUsage,
uint32 KeyAttr,
const CssmData *KeyLabel,
const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
CssmKey &DerivedKey)
{
switch(context.algorithm()) {
case CSSM_ALGID_PKCS5_PBKDF2:
case CSSM_ALGID_DH:
case CSSM_ALGID_PKCS12_PBE_ENCR:
case CSSM_ALGID_PKCS12_PBE_MAC:
case CSSM_ALGID_PKCS5_PBKDF1_MD5:
case CSSM_ALGID_PKCS5_PBKDF1_MD2:
case CSSM_ALGID_PKCS5_PBKDF1_SHA1:
case CSSM_ALGID_PBE_OPENSSL_MD5:
case CSSM_ALGID_OPENSSH1:
#if CRYPTKIT_CSP_ENABLE
case CSSM_ALGID_ECDH:
case CSSM_ALGID_ECDH_X963_KDF:
#endif
break;
default:
CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
}
DerivedKey.KeyData.Data = NULL;
DerivedKey.KeyData.Length = 0;
cspKeyStorage keyStorage = cspParseKeyAttr(CKT_Session, KeyAttr);
cspValidateKeyUsageBits(CKT_Session, KeyUsage);
uint32 keyType = context.getInt(CSSM_ATTRIBUTE_KEY_TYPE,
CSSMERR_CSP_MISSING_ATTR_KEY_TYPE);
uint32 reqKeySize = context.getInt(
CSSM_ATTRIBUTE_KEY_LENGTH,
CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
uint32 keySizeInBytes = (reqKeySize + 7) / 8;
SymmetricBinaryKey *binKey = NULL;
CSSM_DATA_PTR keyData = NULL;
switch(keyStorage) {
case CKS_None:
CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
case CKS_Ref:
binKey = new SymmetricBinaryKey(reqKeySize);
keyData = &binKey->mKeyData;
break;
case CKS_Data:
keyData = &DerivedKey.KeyData;
setUpData(*keyData, keySizeInBytes,
normAllocator);
break;
}
switch(context.algorithm()) {
case CSSM_ALGID_PKCS5_PBKDF2:
DeriveKey_PBKDF2(context,
Param,
keyData);
break;
case CSSM_ALGID_DH:
DeriveKey_DH(context,
Param,
keyData,
*this);
break;
case CSSM_ALGID_PKCS12_PBE_ENCR:
case CSSM_ALGID_PKCS12_PBE_MAC:
DeriveKey_PKCS12(context,
*this,
Param,
keyData);
break;
case CSSM_ALGID_PKCS5_PBKDF1_MD5:
case CSSM_ALGID_PKCS5_PBKDF1_MD2:
case CSSM_ALGID_PKCS5_PBKDF1_SHA1:
case CSSM_ALGID_PBE_OPENSSL_MD5:
DeriveKey_PKCS5_V1_5(context,
context.algorithm(),
Param,
keyData);
break;
case CSSM_ALGID_OPENSSH1:
DeriveKey_OpenSSH1(context,
context.algorithm(),
Param,
keyData);
break;
#if CRYPTKIT_CSP_ENABLE
case CSSM_ALGID_ECDH:
case CSSM_ALGID_ECDH_X963_KDF:
CryptKit::DeriveKey_ECDH(context,
context.algorithm(),
Param,
keyData,
*this);
break;
#endif
default:
assert(0);
}
KeyAttr &= ~KEY_ATTR_RETURN_MASK;
CSSM_KEYHEADER &hdr = DerivedKey.KeyHeader;
setKeyHeader(hdr,
plugin.myGuid(),
keyType,
CSSM_KEYCLASS_SESSION_KEY,
KeyAttr,
KeyUsage);
hdr.LogicalKeySizeInBits = keyData->Length * 8;
if(keyStorage == CKS_Ref) {
addRefKey(*binKey, DerivedKey);
}
else {
hdr.BlobType = CSSM_KEYBLOB_RAW;
hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
}
}