#include "pkcs12Utils.h"
#include <string.h>
#include "pkcs7Templates.h"
#include "pkcs12Templates.h"
#include "pkcs12Crypto.h"
#include "pkcs12Debug.h"
#include <security_asn1/nssUtils.h>
#include <Security/secasn1t.h>
#include <security_utilities/devrandom.h>
#include <security_utilities/errors.h>
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <Security/oidsattr.h>
#include <Security/oidsalg.h>
#include <Security/cssmapple.h>
void **p12NssNullArray(
uint32 num,
SecNssCoder &coder)
{
unsigned len = (num + 1) * sizeof(void *);
void **p = (void **)coder.malloc(len);
memset(p, 0, len);
return p;
}
bool p12DataToInt(
const CSSM_DATA &cdata,
uint32 &u)
{
if((cdata.Length == 0) || (cdata.Data == NULL)) {
u = 0;
return true;
}
CSSM_SIZE len = cdata.Length;
if(len > sizeof(uint32)) {
return false;
}
uint32 rtn = 0;
uint8 *cp = cdata.Data;
for(uint32 i=0; i<len; i++) {
rtn = (rtn << 8) | *cp++;
}
u = rtn;
return true;
}
void p12IntToData(
uint32 num,
CSSM_DATA &cdata,
SecNssCoder &coder)
{
uint32 len = 0;
if(num < 0x100) {
len = 1;
}
else if(num < 0x10000) {
len = 2;
}
else if(num < 0x1000000) {
len = 3;
}
else {
len = 4;
}
coder.allocItem(cdata, len);
uint8 *cp = &cdata.Data[len - 1];
for(unsigned i=0; i<len; i++) {
*cp-- = num & 0xff;
num >>= 8;
}
}
CFDataRef p12CssmDataToCf(
const CSSM_DATA &c)
{
return CFDataCreate(NULL, c.Data, c.Length);
}
void p12CfDataToCssm(
CFDataRef cf,
CSSM_DATA &c,
SecNssCoder &coder)
{
coder.allocCopyItem(CFDataGetBytePtr(cf),
CFDataGetLength(cf), c);
}
CSSM_DATA_PTR p12StringToUtf8(
CFStringRef cfStr,
SecNssCoder &coder)
{
if(cfStr == NULL) {
return NULL;
}
CFIndex strLen = CFStringGetLength(cfStr);
if(strLen == 0) {
return NULL;
}
CSSM_DATA_PTR rtn = coder.mallocn<CSSM_DATA>();
coder.allocItem(*rtn, strLen + 1);
if(!CFStringGetCString(cfStr, (char *)rtn->Data,strLen + 1,
kCFStringEncodingUTF8)) {
return NULL;
}
return rtn;
}
typedef struct {
unsigned value;
const char *name;
} p12NameValuePair;
#define NVP(attr) {attr, #attr}
#define NVP_END {0, NULL}
static const p12NameValuePair p7CITypeNames[] =
{
NVP(CT_None),
NVP(CT_Data),
NVP(CT_SignedData),
NVP(CT_EnvData),
NVP(CT_SignedEnvData),
NVP(CT_DigestData),
NVP(CT_EncryptedData),
NVP_END
};
static const p12NameValuePair p12BagTypeNames[] =
{
NVP(BT_None),
NVP(BT_KeyBag),
NVP(BT_ShroudedKeyBag),
NVP(BT_CertBag),
NVP(BT_CrlBag),
NVP(BT_SecretBag),
NVP(BT_SafeContentsBag),
NVP_END
};
static const char *typeToStr(
unsigned type,
const p12NameValuePair *table)
{
while(table->name) {
if(table->value == type) {
return table->name;
}
table++;
}
return "Unknown";
}
const char *p12BagTypeStr(
NSS_P12_SB_Type type)
{
return typeToStr(type, p12BagTypeNames);
}
const char *p7ContentInfoTypeStr(
NSS_P7_CI_Type type)
{
return typeToStr(type, p7CITypeNames);
}
typedef struct {
const CSSM_OID *oid;
CSSM_ALGORITHMS keyAlg; CSSM_ALGORITHMS encrAlg; CSSM_ALGORITHMS pbeHashAlg; uint32 keySizeInBits;
uint32 blockSizeInBytes; CSSM_PADDING padding; CSSM_ENCRYPT_MODE mode; PKCS_Which pkcs; } PKCSOidInfo;
static const PKCSOidInfo pkcsOidInfos[] = {
{
&CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4,
CSSM_ALGID_RC4,
CSSM_ALGID_RC4,
CSSM_ALGID_SHA1,
128,
0, CSSM_PADDING_NONE,
CSSM_ALGMODE_NONE,
PW_PKCS12
},
{
&CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4,
CSSM_ALGID_RC4,
CSSM_ALGID_RC4,
CSSM_ALGID_SHA1,
40,
0, CSSM_PADDING_NONE,
CSSM_ALGMODE_NONE,
PW_PKCS12
},
{
&CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC,
CSSM_ALGID_3DES_3KEY,
CSSM_ALGID_3DES_3KEY_EDE,
CSSM_ALGID_SHA1,
64 * 3,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS12
},
{
&CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC,
CSSM_ALGID_3DES_2KEY,
CSSM_ALGID_3DES_2KEY_EDE,
CSSM_ALGID_SHA1,
64 * 2,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS12
},
{
&CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC,
CSSM_ALGID_RC2,
CSSM_ALGID_RC2,
CSSM_ALGID_SHA1,
128,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS12
},
{
&CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC,
CSSM_ALGID_RC2,
CSSM_ALGID_RC2,
CSSM_ALGID_SHA1,
40,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS12
},
{
&CSSMOID_PKCS5_pbeWithMD2AndDES,
CSSM_ALGID_DES,
CSSM_ALGID_DES,
CSSM_ALGID_MD2,
64,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS5_v1_5
},
{
&CSSMOID_PKCS5_pbeWithMD2AndRC2,
CSSM_ALGID_RC2,
CSSM_ALGID_RC2,
CSSM_ALGID_MD2,
64,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS5_v1_5
},
{
&CSSMOID_PKCS5_pbeWithMD5AndDES,
CSSM_ALGID_DES,
CSSM_ALGID_DES,
CSSM_ALGID_MD5,
64,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS5_v1_5
},
{
&CSSMOID_PKCS5_pbeWithMD5AndRC2,
CSSM_ALGID_RC2,
CSSM_ALGID_RC2,
CSSM_ALGID_MD5,
64,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS5_v1_5
},
{
&CSSMOID_PKCS5_pbeWithSHA1AndDES,
CSSM_ALGID_DES,
CSSM_ALGID_DES,
CSSM_ALGID_SHA1,
64,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS5_v1_5
},
{
&CSSMOID_PKCS5_pbeWithSHA1AndRC2,
CSSM_ALGID_RC2,
CSSM_ALGID_RC2,
CSSM_ALGID_SHA1,
64,
8,
CSSM_PADDING_PKCS7,
CSSM_ALGMODE_CBCPadIV8,
PW_PKCS5_v1_5
},
{
&CSSMOID_PKCS5_PBES2,
CSSM_ALGID_NONE,
CSSM_ALGID_NONE,
CSSM_ALGID_NONE,
0, 0, 0, 0,
PW_PKCS5_v2
}
};
#define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1]))
bool pkcsOidToParams(
const CSSM_OID *oid,
CSSM_ALGORITHMS &keyAlg, CSSM_ALGORITHMS &encrAlg, CSSM_ALGORITHMS &pbeHashAlg, uint32 &keySizeInBits,
uint32 &blockSizeInBytes, CSSM_PADDING &padding, CSSM_ENCRYPT_MODE &mode, PKCS_Which &pkcs) {
const PKCSOidInfo *info = pkcsOidInfos;
pkcs = PW_None;
for(unsigned dex=0; dex<NUM_PKCS_OID_INFOS; dex++) {
if(nssCompareCssmData(oid, info->oid)) {
keyAlg = info->keyAlg;
encrAlg = info->encrAlg;
pbeHashAlg = info->pbeHashAlg;
keySizeInBits = info->keySizeInBits;
blockSizeInBytes = info->blockSizeInBytes;
padding = info->padding;
mode = info->mode;
pkcs = info->pkcs;
return true;
}
info++;
}
return false;
}
CSSM_RETURN p12VerifyMac(
const NSS_P12_DecodedPFX &pfx,
CSSM_CSP_HANDLE cspHand,
const CSSM_DATA *pwd, const CSSM_KEY *passKey,
SecNssCoder &coder) {
if(pfx.macData == NULL) {
return CSSMERR_CSP_INVALID_SIGNATURE;
}
NSS_P12_MacData &macData = *pfx.macData;
NSS_P7_DigestInfo &digestInfo = macData.mac;
CSSM_OID &algOid = digestInfo.digestAlgorithm.algorithm;
CSSM_ALGORITHMS macAlg;
if(!cssmOidToAlg(&algOid, &macAlg)) {
return CSSMERR_CSP_INVALID_ALGORITHM;
}
uint32 iterCount = 0;
CSSM_DATA &citer = macData.iterations;
if(!p12DataToInt(citer, iterCount)) {
return CSSMERR_CSP_INVALID_ATTR_ROUNDS;
}
if(iterCount == 0) {
iterCount = 1;
}
CSSM_DATA genMac;
CSSM_RETURN crtn = p12GenMac(cspHand, *pfx.authSafe.content.data,
macAlg, iterCount, macData.macSalt, pwd, passKey, coder, genMac);
if(crtn) {
return crtn;
}
if(nssCompareCssmData(&genMac, &digestInfo.digest)) {
return CSSM_OK;
}
else {
return CSSMERR_CSP_VERIFY_FAILED;
}
}
#define P12_SALT_LEN 8
void p12GenSalt(
CSSM_DATA &salt,
SecNssCoder &coder)
{
DevRandomGenerator rng;
coder.allocItem(salt, P12_SALT_LEN);
rng.random(salt.Data, P12_SALT_LEN);
}
void p12GenLabel(
CSSM_DATA &label,
SecNssCoder &coder)
{
uint8 d[4];
DevRandomGenerator rng;
rng.random(d, 4);
CSSM_DATA cd = {4, d};
uint32 i;
p12DataToInt(cd, i);
coder.allocItem(label, 9);
memset(label.Data, 0, 9);
sprintf((char *)label.Data, "%08X", (unsigned)i);
}
static const uint8 nullAlg[2] = {SEC_ASN1_NULL, 0};
void p12NullAlgParams(
CSSM_X509_ALGORITHM_IDENTIFIER &algId)
{
CSSM_DATA &p = algId.parameters;
p.Data = (uint8 *)nullAlg;
p.Length = 2;
}
void freeCssmMemory(
CSSM_HANDLE hand,
void *p)
{
CSSM_API_MEMORY_FUNCS memFuncs;
CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
if(crtn) {
p12LogCssmError("CSSM_GetAPIMemoryFunctions", crtn);
return;
}
memFuncs.free_func(p, memFuncs.AllocRef);
}
CSSM_RETURN p12SetPubKeyHash(
CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE dlDbHand, CSSM_DATA &keyLabel, CSSM_DATA_PTR newPrintName, SecNssCoder &coder, CSSM_DATA &newLabel, CSSM_KEY_PTR &foundKey) {
CSSM_QUERY query;
CSSM_SELECTION_PREDICATE predicate;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_RETURN crtn;
CSSM_HANDLE resultHand = 0;
CSSM_DATA keyData = {0, NULL};
CSSM_CC_HANDLE ccHand = 0;
CSSM_KEY_PTR privKey = NULL;
CSSM_DATA_PTR keyDigest = NULL;
assert(cspHand != 0);
query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 1;
predicate.DbOperator = CSSM_DB_EQUAL;
predicate.Attribute.Info.AttributeNameFormat =
CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
predicate.Attribute.Info.Label.AttributeName =
(char*) P12_KEY_ATTR_LABEL_AND_HASH;
predicate.Attribute.Info.AttributeFormat =
CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
predicate.Attribute.Value = &keyLabel;
query.SelectionPredicate = &predicate;
query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = CSSM_QUERY_RETURN_DATA;
CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs;
CSSM_DB_ATTRIBUTE_DATA attr[2];
attr[0].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr[0].Info.Label.AttributeName = (char*) P12_KEY_ATTR_LABEL_AND_HASH;
attr[0].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
if(newPrintName) {
attr[1].Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
attr[1].Info.Label.AttributeName = (char*) P12_KEY_ATTR_PRINT_NAME;
attr[1].Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
}
recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
recordAttrs.NumberOfAttributes = newPrintName ? 2 : 1;
recordAttrs.AttributeData = attr;
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
&recordAttrs,
&keyData, &record);
if(crtn != CSSM_OK) {
p12LogCssmError("CSSM_DL_DataGetFirst", crtn);
p12ErrorLog("***p12SetPubKeyHash: can't find private key\n");
return crtn;
}
if(keyData.Data == NULL) {
p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n");
crtn = CSSMERR_CSSM_INTERNAL_ERROR;
goto errOut;
}
privKey = (CSSM_KEY_PTR)keyData.Data;
crtn = CSSM_CSP_CreatePassThroughContext(cspHand, privKey, &ccHand);
if(crtn) {
p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn);
goto errOut;
}
crtn = CSSM_CSP_PassThrough(ccHand,
CSSM_APPLECSP_KEYDIGEST,
NULL,
(void **)&keyDigest);
if(crtn) {
p12LogCssmError("CSSM_CSP_PassThrough", crtn);
goto errOut;
}
freeCssmMemory(dlDbHand.DLHandle, attr[0].Value->Data);
freeCssmMemory(dlDbHand.DLHandle, attr[0].Value);
if(newPrintName) {
freeCssmMemory(dlDbHand.DLHandle, attr[1].Value->Data);
freeCssmMemory(dlDbHand.DLHandle, attr[1].Value);
}
attr[0].Value = keyDigest;
if(newPrintName) {
attr[1].Value = newPrintName;
}
crtn = CSSM_DL_DataModify(dlDbHand,
CSSM_DL_DB_RECORD_PRIVATE_KEY,
record,
&recordAttrs,
NULL, CSSM_DB_MODIFY_ATTRIBUTE_REPLACE);
switch(crtn) {
case CSSM_OK:
coder.allocCopyItem(*keyDigest, newLabel);
break;
default:
p12LogCssmError("CSSM_DL_DataModify", crtn);
break;
case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA:
{
CSSM_RETURN drtn = CSSM_DL_DataDelete(dlDbHand, record);
if(drtn) {
p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn);
crtn = drtn;
break;
}
CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
resultHand = 0;
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
record = NULL;
predicate.Attribute.Value = keyDigest;
drtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
NULL, &keyData,
&record);
if(drtn) {
p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn);
crtn = drtn;
break;
}
foundKey = (CSSM_KEY_PTR)keyData.Data;
coder.allocCopyItem(*keyDigest, newLabel);
break;
}
}
errOut:
if(resultHand) {
CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
}
if(record) {
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
}
if(ccHand) {
CSSM_DeleteContext(ccHand);
}
if(privKey) {
CSSM_FreeKey(cspHand, NULL, privKey, CSSM_FALSE);
freeCssmMemory(dlDbHand.DLHandle, privKey);
}
if(keyDigest) {
freeCssmMemory(cspHand, keyDigest->Data);
freeCssmMemory(cspHand, keyDigest);
}
return crtn;
}
CSSM_RETURN p12AddContextAttribute(CSSM_CC_HANDLE CCHandle,
uint32 AttributeType,
uint32 AttributeLength,
const void *AttributePtr)
{
CSSM_CONTEXT_ATTRIBUTE newAttr;
CSSM_RETURN crtn;
newAttr.AttributeType = AttributeType;
newAttr.AttributeLength = AttributeLength;
newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr;
crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
if(crtn) {
p12LogCssmError("CSSM_UpdateContextAttributes", crtn);
}
return crtn;
}
CSSM_RETURN p12DeleteKey(
CSSM_DL_DB_HANDLE dlDbHand,
const CSSM_DATA &keyLabel)
{
CSSM_QUERY query;
CSSM_SELECTION_PREDICATE predicate;
CSSM_DB_UNIQUE_RECORD_PTR record = NULL;
CSSM_RETURN crtn;
CSSM_HANDLE resultHand = 0;
query.RecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY;
query.Conjunctive = CSSM_DB_NONE;
query.NumSelectionPredicates = 1;
predicate.DbOperator = CSSM_DB_EQUAL;
predicate.Attribute.Info.AttributeNameFormat =
CSSM_DB_ATTRIBUTE_NAME_AS_STRING;
predicate.Attribute.Info.Label.AttributeName =
(char*) P12_KEY_ATTR_LABEL_AND_HASH;
predicate.Attribute.Info.AttributeFormat =
CSSM_DB_ATTRIBUTE_FORMAT_BLOB;
predicate.Attribute.Value = const_cast<CSSM_DATA_PTR>(&keyLabel);
query.SelectionPredicate = &predicate;
query.QueryLimits.TimeLimit = 0;
query.QueryLimits.SizeLimit = 1;
query.QueryFlags = 0;
crtn = CSSM_DL_DataGetFirst(dlDbHand,
&query,
&resultHand,
NULL, NULL, &record);
if(crtn) {
p12LogCssmError("CSSM_DL_DataGetFirst", crtn);
p12ErrorLog("***p12DeleteKey: can't find private key\n");
return crtn;
}
crtn = CSSM_DL_DataDelete(dlDbHand, record);
if(crtn) {
p12LogCssmError("CSSM_DL_DataDelete", crtn);
p12ErrorLog("***p12DeleteKey: can't delete private key\n");
}
CSSM_DL_DataAbortQuery(dlDbHand, resultHand);
CSSM_DL_FreeUniqueRecord(dlDbHand, record);
return crtn;
}
void p12ImportPassPhrase(
CFStringRef inPhrase,
SecNssCoder &coder,
CSSM_DATA &outPhrase)
{
CFDataRef cfData = CFStringCreateExternalRepresentation(NULL,
inPhrase, kCFStringEncodingUTF8, 0);
if(cfData == NULL) {
p12ErrorLog("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
MacOSError::throwMe(errSecParam);
}
CFIndex keyLen = CFDataGetLength(cfData);
coder.allocItem(outPhrase, keyLen);
memmove(outPhrase.Data, CFDataGetBytePtr(cfData), keyLen);
CFRelease(cfData);
}