#include "p12pbe.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <Security/cssm.h>
#include <openssl/bn.h>
#define PBE_MALLOC malloc
#define PBE_FREE free
typedef CSSM_CC_HANDLE HashHand;
static HashHand hashCreate(CSSM_CSP_HANDLE cspHand,
CSSM_ALGORITHMS alg)
{
CSSM_CC_HANDLE hashHand;
CSSM_RETURN crtn = CSSM_CSP_CreateDigestContext(cspHand,
alg,
&hashHand);
if(crtn) {
printf("CSSM_CSP_CreateDigestContext error\n");
return 0;
}
return hashHand;
}
static CSSM_RETURN hashInit(HashHand hand)
{
return CSSM_DigestDataInit(hand);
}
static CSSM_RETURN hashUpdate(HashHand hand,
const unsigned char *buf,
unsigned bufLen)
{
const CSSM_DATA cdata = {bufLen, (uint8 *)buf};
return CSSM_DigestDataUpdate(hand, &cdata, 1);
}
static CSSM_RETURN hashFinal(HashHand hand,
unsigned char *digest, unsigned *digestLen) {
CSSM_DATA cdata = {(uint32)digestLen, digest};
return CSSM_DigestDataFinal(hand, &cdata);
}
static CSSM_RETURN hashDone(HashHand hand)
{
return CSSM_DeleteContext(hand);
}
static unsigned char *p12StrCat(
const unsigned char *inStr,
unsigned inStrLen,
unsigned outLen,
unsigned char *outStr = NULL) {
if(outStr == NULL) {
outStr = (unsigned char *)PBE_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;
}
CSSM_RETURN p12PbeGen_app(
const CSSM_DATA &pwd, const unsigned char *salt,
unsigned saltLen,
unsigned iterCount,
P12_PBE_ID pbeId,
CSSM_ALGORITHMS hashAlg, CSSM_CSP_HANDLE cspHand,
unsigned char *outbuf,
unsigned outbufLen)
{
CSSM_RETURN ourRtn;
unsigned unipassLen = pwd.Length;
unsigned char *unipass = pwd.Data;
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 = 16;
p12_v = 64;
break;
case CSSM_ALGID_SHA1:
p12_u = 20;
p12_v = 64;
break;
default:
return CSSMERR_CSP_INVALID_ALGORITHM;
}
unsigned char *p12_D = NULL; p12_D = (unsigned char *)PBE_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, p12_Slen);
}
unsigned p12_Plen = p12_v * ((unipassLen + p12_v - 1) / p12_v);
if(p12_Plen) {
p12_P = p12StrCat(unipass, unipassLen, p12_Plen);
}
unsigned char *p12_I =
(unsigned char *)PBE_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 *)PBE_MALLOC(p12_c * p12_u);
HashHand hashHand = hashCreate(cspHand, hashAlg);
if(!hashHand) {
return CSSMERR_CSP_INVALID_CONTEXT_HANDLE; }
unsigned char *p12_B = (unsigned char *)PBE_MALLOC(p12_v + sizeof(int));
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);
ourRtn = hashInit(hashHand);
if(ourRtn) break;
ourRtn = hashUpdate(hashHand, p12_D, p12_v);
if(ourRtn) break;
ourRtn = hashUpdate(hashHand, p12_I, p12_Slen + p12_Plen);
if(ourRtn) break;
unsigned outLen = p12_u;
ourRtn = hashFinal(hashHand, p12_AsubI, &outLen);
if(ourRtn) break;
for(unsigned iter=1; iter<p12_r; iter++) {
ourRtn = hashInit(hashHand);
if(ourRtn) break;
ourRtn = hashUpdate(hashHand, p12_AsubI, p12_u);
if(ourRtn) break;
ourRtn = hashFinal(hashHand, p12_AsubI, &outLen);
if(ourRtn) break;
}
p12StrCat(p12_AsubI, p12_u, 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) {
goto errOut;
}
memmove(outbuf, p12_A, outbufLen);
ourRtn = CSSM_OK;
errOut:
if(p12_D) {
PBE_FREE(p12_D);
}
if(p12_S) {
PBE_FREE(p12_S);
}
if(p12_P) {
PBE_FREE(p12_P);
}
if(p12_I) {
PBE_FREE(p12_I);
}
if(p12_A) {
PBE_FREE(p12_A);
}
if(p12_B) {
PBE_FREE(p12_B);
}
if(hashHand) {
hashDone(hashHand);
}
BN_free(Bpl1);
BN_free(Ij);
return ourRtn;
}