#include <Security/cssmapple.h>
#include <openssl/bn_legacy.h>
#include "pbkdDigest.h"
#include "pkcs12Derive.h"
#include "AppleCSPUtils.h"
#include "AppleCSPContext.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
#include <security_asn1/SecNssCoder.h>
#include <CoreFoundation/CoreFoundation.h>
typedef enum {
PBE_ID_Key = 1,
PBE_ID_IV = 2,
PBE_ID_MAC = 3
} P12_PBE_ID;
static unsigned char *p12StrCat(
const unsigned char *inStr,
unsigned inStrLen,
SecNssCoder &coder,
unsigned outLen,
unsigned char *outStr = NULL) {
if(outStr == NULL) {
outStr = (unsigned char *)coder.malloc(outLen);
}
unsigned toMove = outLen;
unsigned char *outp = outStr;
while(toMove) {
unsigned thisMove = inStrLen;
if(thisMove > toMove) {
thisMove = toMove;
}
memmove(outp, inStr, thisMove);
toMove -= thisMove;
outp += thisMove;
}
return outStr;
}
static CSSM_RETURN p12PbeGen(
const CSSM_DATA &pwd, const uint8 *salt,
unsigned saltLen,
unsigned iterCount,
P12_PBE_ID pbeId,
CSSM_ALGORITHMS hashAlg, SecNssCoder &coder,
uint8 *outbuf,
unsigned outbufLen)
{
CSSM_RETURN ourRtn = CSSM_OK;
unsigned unipassLen = (unsigned)pwd.Length;
unsigned char *unipass = pwd.Data;
int irtn;
unsigned p12_r = iterCount;
unsigned p12_n = outbufLen;
unsigned p12_u; unsigned p12_v; unsigned char *p12_P = NULL; unsigned char *p12_S = NULL;
switch(hashAlg) {
case CSSM_ALGID_MD5:
p12_u = kMD5DigestSize;
p12_v = kMD5BlockSize;
break;
case CSSM_ALGID_SHA1:
p12_u = kSHA1DigestSize;
p12_v = kSHA1BlockSize;
break;
default:
return CSSMERR_CSP_INVALID_ALGORITHM;
}
unsigned char *p12_D = NULL; p12_D = (unsigned char *)coder.malloc(p12_v);
for(unsigned dex=0; dex<p12_v; dex++) {
p12_D[dex] = (unsigned char)pbeId;
}
unsigned p12_Slen = p12_v * ((saltLen + p12_v - 1) / p12_v);
if(p12_Slen) {
p12_S = p12StrCat(salt, saltLen, coder, p12_Slen);
}
unsigned p12_Plen = p12_v * ((unipassLen + p12_v - 1) / p12_v);
if(p12_Plen) {
p12_P = p12StrCat(unipass, unipassLen, coder, p12_Plen);
}
unsigned char *p12_I =
(unsigned char *)coder.malloc(p12_Slen + p12_Plen);
memmove(p12_I, p12_S, p12_Slen);
if(p12_Plen) {
memmove(p12_I + p12_Slen, p12_P, p12_Plen);
}
unsigned p12_c = (p12_n + p12_u - 1) / p12_u;
unsigned char *p12_A = (unsigned char *)coder.malloc(p12_c * p12_u);
DigestCtx ourDigest;
DigestCtx *hashHand = &ourDigest;
unsigned char *p12_B = (unsigned char *)coder.malloc(p12_v + 1);
BIGNUM *Ij = BN_new();
BIGNUM *Bpl1 = BN_new();
for(unsigned p12_i=0; p12_i<p12_c; p12_i++) {
unsigned char *p12_AsubI = p12_A + (p12_i * p12_u);
irtn = DigestCtxInit(hashHand, hashAlg);
if(!irtn) {
ourRtn = CSSMERR_CSP_INTERNAL_ERROR;
break;
}
DigestCtxUpdate(hashHand, p12_D, p12_v);
DigestCtxUpdate(hashHand, p12_I, p12_Slen + p12_Plen);
DigestCtxFinal(hashHand, p12_AsubI);
for(unsigned iter=1; iter<p12_r; iter++) {
irtn = DigestCtxInit(hashHand, hashAlg);
if(!irtn) {
ourRtn = CSSMERR_CSP_INTERNAL_ERROR;
break;
}
DigestCtxUpdate(hashHand, p12_AsubI, p12_u);
DigestCtxFinal(hashHand, p12_AsubI);
}
p12StrCat(p12_AsubI, p12_u, coder, p12_v, p12_B);
BN_bin2bn (p12_B, p12_v, Bpl1);
BN_add_word (Bpl1, 1);
unsigned Ilen = p12_Slen + p12_Plen;
for (unsigned j = 0; j < Ilen; j+=p12_v) {
BN_bin2bn (p12_I + j, p12_v, Ij);
BN_add (Ij, Ij, Bpl1);
BN_bn2bin (Ij, p12_B);
unsigned Ijlen = BN_num_bytes (Ij);
if (Ijlen > p12_v) {
BN_bn2bin (Ij, p12_B);
memcpy (p12_I + j, p12_B + 1, p12_v);
} else if (Ijlen < p12_v) {
memset(p12_I + j, 0, p12_v - Ijlen);
BN_bn2bin(Ij, p12_I + j + p12_v - Ijlen);
} else BN_bn2bin (Ij, p12_I + j);
}
}
if(ourRtn == CSSM_OK) {
memmove(outbuf, p12_A, outbufLen);
}
if(p12_D) {
memset(p12_D, 0, p12_v);
}
if(p12_S) {
memset(p12_S, 0, p12_Slen);
}
if(p12_P) {
memset(p12_P, 0, p12_Plen);
}
if(p12_I) {
memset(p12_I, 0, p12_Slen + p12_Plen);
}
if(p12_A) {
memset(p12_A, 0, p12_c * p12_u);
}
if(p12_B) {
memset(p12_B, 0, p12_v);
}
if(hashHand) {
DigestCtxFree(hashHand);
}
BN_free(Bpl1);
BN_free(Ij);
return ourRtn;
}
void DeriveKey_PKCS12 (
const Context &context,
AppleCSPSession &session,
const CssmData &Param, CSSM_DATA *keyData) {
SecNssCoder tmpCoder;
CSSM_DATA pwd = {0, NULL};
CSSM_DATA appPwd = {0, NULL};
CssmCryptoData *cryptData =
context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED);
if((cryptData != NULL) && (cryptData->Param.Length != 0)) {
appPwd = cryptData->Param;
}
else {
CssmKey *passKey = context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
if (passKey != NULL) {
AppleCSPContext::symmetricKeyBits(context, session,
CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE,
appPwd.Data, appPwd.Length);
}
}
if(appPwd.Data) {
CFDataRef cfData = CFDataCreate(NULL, appPwd.Data, appPwd.Length);
CFStringRef cfStr = CFStringCreateFromExternalRepresentation(NULL,
cfData, kCFStringEncodingUTF8);
if (cfData)
CFRelease(cfData);
if(cfStr == NULL) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED);
}
CFIndex len = CFStringGetLength(cfStr);
tmpCoder.allocItem(pwd, sizeof(UniChar) * (len + 1));
unsigned char *cp = pwd.Data;
UniChar uc = 0;
for(CFIndex dex=0; dex<len; dex++) {
uc = CFStringGetCharacterAtIndex(cfStr, dex);
*cp++ = uc >> 8;
*cp++ = uc & 0xff;
}
if(uc == 0) {
if(pwd.Length < 2) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED);
}
pwd.Length -= 2;
}
else {
*cp++ = 0;
*cp++ = 0;
}
if (cfStr)
CFRelease(cfStr);
}
uint32 saltLen = 0;
uint8 *salt = NULL;
CssmData *csalt = context.get<CssmData>(CSSM_ATTRIBUTE_SALT);
if(csalt) {
salt = csalt->Data;
saltLen = (uint32)csalt->Length;
}
uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT,
CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT);
if(iterCount == 0) {
CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT);
}
P12_PBE_ID pbeId = PBE_ID_Key;
switch(context.algorithm()) {
case CSSM_ALGID_PKCS12_PBE_ENCR:
pbeId = PBE_ID_Key;
break;
case CSSM_ALGID_PKCS12_PBE_MAC:
pbeId = PBE_ID_MAC;
break;
default:
assert(0);
CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
}
CSSM_RETURN crtn = p12PbeGen(pwd,
salt, saltLen,
iterCount,
pbeId,
CSSM_ALGID_SHA1, tmpCoder,
keyData->Data,
(unsigned)keyData->Length);
if(crtn) {
CssmError::throwMe(crtn);
}
if(Param.Data) {
crtn = p12PbeGen(pwd,
salt, saltLen,
iterCount,
PBE_ID_IV,
CSSM_ALGID_SHA1, tmpCoder,
Param.Data,
(unsigned)Param.Length);
if(crtn) {
CssmError::throwMe(crtn);
}
}
}