#include "pkcs12Crypto.h"
#include "pkcs12Utils.h"
#include "pkcs12Debug.h"
#include <security_cdsa_utils/cuCdsaUtils.h>
#include <security_cdsa_utilities/cssmacl.h>
#include <security_keychain/Access.h>
#define KEY_LABEL "p12 key"
CSSM_RETURN p12KeyGen(
CSSM_CSP_HANDLE cspHand,
CSSM_KEY &key,
bool isForEncr, CSSM_ALGORITHMS keyAlg,
CSSM_ALGORITHMS pbeHashAlg, uint32 keySizeInBits,
uint32 iterCount,
const CSSM_DATA &salt,
const CSSM_DATA *pwd, const CSSM_KEY *passKey,
CSSM_DATA &iv) {
CSSM_RETURN crtn;
CSSM_CC_HANDLE ccHand;
CSSM_DATA dummyLabel;
CSSM_ACCESS_CREDENTIALS creds;
memset(&key, 0, sizeof(CSSM_KEY));
memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
CSSM_ALGORITHMS deriveAlg = CSSM_ALGID_NONE;
if(pbeHashAlg != CSSM_ALGID_SHA1) {
return CSSMERR_CSP_INVALID_ALGORITHM;
}
if(isForEncr) {
deriveAlg = CSSM_ALGID_PKCS12_PBE_ENCR;
}
else {
deriveAlg = CSSM_ALGID_PKCS12_PBE_MAC;
}
CSSM_CRYPTO_DATA seed;
if(pwd) {
seed.Param = *pwd;
}
else {
seed.Param.Data = NULL;
seed.Param.Length = 0;
}
seed.Callback = NULL;
seed.CallerCtx = NULL;
crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
deriveAlg,
keyAlg,
keySizeInBits,
&creds,
passKey, iterCount,
&salt,
&seed, &ccHand);
if(crtn) {
p12LogCssmError("CSSM_CSP_CreateDeriveKeyContext", crtn);
return crtn;
}
dummyLabel.Length = strlen(KEY_LABEL);
dummyLabel.Data = (uint8 *)KEY_LABEL;
crtn = CSSM_DeriveKey(ccHand,
&iv,
CSSM_KEYUSE_ANY,
CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE,
&dummyLabel,
NULL, &key);
CSSM_DeleteContext(ccHand);
if(crtn) {
p12LogCssmError("CSSM_DeriveKey", crtn);
}
return crtn;
}
CSSM_RETURN p12Decrypt(
CSSM_CSP_HANDLE cspHand,
const CSSM_DATA &cipherText,
CSSM_ALGORITHMS keyAlg,
CSSM_ALGORITHMS encrAlg,
CSSM_ALGORITHMS pbeHashAlg, uint32 keySizeInBits,
uint32 blockSizeInBytes, CSSM_PADDING padding, CSSM_ENCRYPT_MODE mode, uint32 iterCount,
const CSSM_DATA &salt,
const CSSM_DATA *pwd, const CSSM_KEY *passKey,
SecNssCoder &coder, CSSM_DATA &plainText)
{
CSSM_RETURN crtn;
CSSM_KEY ckey;
CSSM_CC_HANDLE ccHand = 0;
CSSM_DATA ourPtext = {0, NULL};
CSSM_DATA remData = {0, NULL};
CSSM_DATA iv = {0, NULL};
CSSM_DATA_PTR ivPtr = NULL;
if(blockSizeInBytes) {
coder.allocItem(iv, blockSizeInBytes);
ivPtr = &iv;
}
crtn = p12KeyGen(cspHand, ckey, true, keyAlg, pbeHashAlg,
keySizeInBits, iterCount, salt, pwd, passKey, iv);
if(crtn) {
return crtn;
}
crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
encrAlg,
mode,
NULL, &ckey,
ivPtr, padding,
NULL, &ccHand);
if(crtn) {
cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn);
goto errOut;
}
CSSM_SIZE bytesDecrypted;
crtn = CSSM_DecryptData(ccHand,
&cipherText,
1,
&ourPtext,
1,
&bytesDecrypted,
&remData);
if(crtn) {
cuPrintError("CSSM_DecryptData", crtn);
}
else {
coder.allocCopyItem(ourPtext, plainText);
plainText.Length = bytesDecrypted;
freeCssmMemory(cspHand, ourPtext.Data);
}
if(remData.Data) {
freeCssmMemory(cspHand, remData.Data);
}
errOut:
if(ccHand) {
CSSM_DeleteContext(ccHand);
}
CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE);
return crtn;
}
CSSM_RETURN p12Encrypt(
CSSM_CSP_HANDLE cspHand,
const CSSM_DATA &plainText,
CSSM_ALGORITHMS keyAlg,
CSSM_ALGORITHMS encrAlg,
CSSM_ALGORITHMS pbeHashAlg, uint32 keySizeInBits,
uint32 blockSizeInBytes, CSSM_PADDING padding, CSSM_ENCRYPT_MODE mode, uint32 iterCount,
const CSSM_DATA &salt,
const CSSM_DATA *pwd, const CSSM_KEY *passKey,
SecNssCoder &coder, CSSM_DATA &cipherText)
{
CSSM_RETURN crtn;
CSSM_KEY ckey;
CSSM_CC_HANDLE ccHand = 0;
CSSM_DATA ourCtext = {0, NULL};
CSSM_DATA remData = {0, NULL};
CSSM_DATA iv = {0, NULL};
CSSM_DATA_PTR ivPtr = NULL;
if(blockSizeInBytes) {
coder.allocItem(iv, blockSizeInBytes);
ivPtr = &iv;
}
crtn = p12KeyGen(cspHand, ckey, true, keyAlg, pbeHashAlg,
keySizeInBits, iterCount, salt, pwd, passKey, iv);
if(crtn) {
return crtn;
}
crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
encrAlg,
mode,
NULL, &ckey,
ivPtr, padding,
NULL, &ccHand);
if(crtn) {
cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn);
goto errOut;
}
CSSM_SIZE bytesEncrypted;
crtn = CSSM_EncryptData(ccHand,
&plainText,
1,
&ourCtext,
1,
&bytesEncrypted,
&remData);
if(crtn) {
cuPrintError("CSSM_DecryptData", crtn);
}
else {
coder.allocCopyItem(ourCtext, cipherText);
cipherText.Length = bytesEncrypted;
freeCssmMemory(cspHand, ourCtext.Data);
}
if(remData.Data) {
freeCssmMemory(cspHand, remData.Data);
}
errOut:
if(ccHand) {
CSSM_DeleteContext(ccHand);
}
CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE);
return crtn;
}
CSSM_RETURN p12GenMac(
CSSM_CSP_HANDLE cspHand,
const CSSM_DATA &ptext, CSSM_ALGORITHMS alg, unsigned iterCount,
const CSSM_DATA &salt,
const CSSM_DATA *pwd, const CSSM_KEY *passKey,
SecNssCoder &coder, CSSM_DATA &macData) {
CSSM_RETURN crtn;
CSSM_CC_HANDLE ccHand = 0;
unsigned keySizeInBits;
CSSM_ALGORITHMS hmacAlg;
switch(alg) {
case CSSM_ALGID_SHA1:
keySizeInBits = 160;
hmacAlg = CSSM_ALGID_SHA1HMAC;
break;
case CSSM_ALGID_MD5:
keySizeInBits = 128;
hmacAlg = CSSM_ALGID_MD5HMAC;
break;
default:
return CSSMERR_CSP_INVALID_ALGORITHM;
}
CSSM_KEY macKey;
CSSM_DATA iv = {0, NULL};
crtn = p12KeyGen(cspHand, macKey, false, hmacAlg, alg,
keySizeInBits, iterCount, salt, pwd, passKey, iv);
if(crtn) {
return crtn;
}
coder.allocItem(macData, keySizeInBits / 8);
crtn = CSSM_CSP_CreateMacContext(cspHand, hmacAlg, &macKey, &ccHand);
if(crtn) {
cuPrintError("CSSM_CSP_CreateMacContext", crtn);
goto errOut;
}
crtn = CSSM_GenerateMac (ccHand, &ptext, 1, &macData);
if(crtn) {
cuPrintError("CSSM_GenerateMac", crtn);
}
errOut:
if(ccHand) {
CSSM_DeleteContext(ccHand);
}
CSSM_FreeKey(cspHand, NULL, &macKey, CSSM_FALSE);
return crtn;
}
CSSM_RETURN p12UnwrapKey(
CSSM_CSP_HANDLE cspHand,
CSSM_DL_DB_HANDLE_PTR dlDbHand, int keyIsPermanent, const CSSM_DATA &shroudedKeyBits,
CSSM_ALGORITHMS keyAlg, CSSM_ALGORITHMS encrAlg,
CSSM_ALGORITHMS pbeHashAlg, uint32 keySizeInBits,
uint32 blockSizeInBytes, CSSM_PADDING padding, CSSM_ENCRYPT_MODE mode, uint32 iterCount,
const CSSM_DATA &salt,
const CSSM_DATA *pwd, const CSSM_KEY *passKey,
SecNssCoder &coder, const CSSM_DATA &labelData,
SecAccessRef access, bool noAcl,
CSSM_KEYUSE keyUsage,
CSSM_KEYATTR_FLAGS keyAttrs,
CSSM_KEY_PTR &privKey)
{
CSSM_RETURN crtn;
CSSM_KEY ckey;
CSSM_CC_HANDLE ccHand = 0;
CSSM_KEY wrappedKey;
CSSM_KEY unwrappedKey;
CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader;
CSSM_DATA descrData = {0, NULL}; CSSM_KEYATTR_FLAGS reqAttr = keyAttrs;
ResourceControlContext rcc;
ResourceControlContext *rccPtr = NULL;
Security::KeychainCore::Access::Maker maker;
CSSM_DATA iv = {0, NULL};
CSSM_DATA_PTR ivPtr = NULL;
if(blockSizeInBytes) {
coder.allocItem(iv, blockSizeInBytes);
ivPtr = &iv;
}
crtn = p12KeyGen(cspHand, ckey, true, keyAlg, pbeHashAlg,
keySizeInBits, iterCount, salt, pwd, passKey, iv);
if(crtn) {
return crtn;
}
crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
encrAlg,
mode,
NULL, &ckey,
ivPtr, padding,
NULL, &ccHand);
if(crtn) {
p12LogCssmError("CSSM_CSP_CreateSymmetricContext", crtn);
goto errOut;
}
if(dlDbHand) {
crtn = p12AddContextAttribute(ccHand,
CSSM_ATTRIBUTE_DL_DB_HANDLE,
sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE),
dlDbHand);
if(crtn) {
p12LogCssmError("AddContextAttribute", crtn);
goto errOut;
}
}
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
memset(&unwrappedKey, 0, sizeof(CSSM_KEY));
hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
hdr.BlobType = CSSM_KEYBLOB_WRAPPED;
hdr.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8;
hdr.AlgorithmId = CSSM_ALGID_NONE;
hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY;
hdr.LogicalKeySizeInBits = 0;
hdr.KeyAttr = CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE;
hdr.KeyUsage = CSSM_KEYUSE_ANY;
hdr.WrapAlgorithmId = encrAlg;
hdr.WrapMode = mode;
if(dlDbHand && keyIsPermanent) {
reqAttr |= CSSM_KEYATTR_PERMANENT;
}
wrappedKey.KeyData = shroudedKeyBits;
if(!noAcl) {
memset(&rcc, 0, sizeof(rcc));
maker.initialOwner(rcc);
rccPtr = &rcc;
}
crtn = CSSM_UnwrapKey(ccHand,
NULL, &wrappedKey,
keyUsage,
reqAttr,
&labelData,
rccPtr, privKey,
&descrData); if(crtn) {
p12LogCssmError("CSSM_UnwrapKey", crtn);
if(crtn == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) {
crtn = errSecDuplicateItem;
}
}
if((crtn == CSSM_OK) && !noAcl) {
try {
CssmClient::KeyAclBearer bearer(
cspHand, *privKey, Allocator::standard());
SecPointer<KeychainCore::Access> initialAccess(access ?
KeychainCore::Access::required(access) :
new KeychainCore::Access("privateKey"));
initialAccess->setAccess(bearer, maker);
}
catch (const CssmError &e) {
if(e.error != CSSMERR_CSP_FUNCTION_NOT_IMPLEMENTED) {
crtn = e.error;
}
}
catch(...) {
p12ErrorLog("p12 exception on setAccess\n");
crtn = errSecAuthFailed;
}
}
errOut:
if(ccHand) {
CSSM_DeleteContext(ccHand);
}
CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE);
return crtn;
}
CSSM_RETURN p12WrapKey(
CSSM_CSP_HANDLE cspHand,
CSSM_KEY_PTR privKey,
const CSSM_ACCESS_CREDENTIALS *privKeyCreds,
CSSM_ALGORITHMS keyAlg, CSSM_ALGORITHMS encrAlg,
CSSM_ALGORITHMS pbeHashAlg, uint32 keySizeInBits,
uint32 blockSizeInBytes, CSSM_PADDING padding, CSSM_ENCRYPT_MODE mode, uint32 iterCount,
const CSSM_DATA &salt,
const CSSM_DATA *pwd, const CSSM_KEY *passKey,
SecNssCoder &coder, CSSM_DATA &shroudedKeyBits) {
CSSM_RETURN crtn;
CSSM_KEY ckey;
CSSM_CC_HANDLE ccHand = 0;
CSSM_KEY wrappedKey;
CSSM_CONTEXT_ATTRIBUTE attr;
CSSM_DATA descrData = {0, NULL};
CSSM_ACCESS_CREDENTIALS creds;
if (!(privKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
return errSecDataNotAvailable;
}
if(privKeyCreds == NULL) {
memset(&creds, 0, sizeof(creds));
privKeyCreds = &creds;
}
CSSM_DATA iv = {0, NULL};
CSSM_DATA_PTR ivPtr = NULL;
if(blockSizeInBytes) {
coder.allocItem(iv, blockSizeInBytes);
ivPtr = &iv;
}
crtn = p12KeyGen(cspHand, ckey, true, keyAlg, pbeHashAlg,
keySizeInBits, iterCount, salt, pwd, passKey, iv);
if(crtn) {
return crtn;
}
crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
encrAlg,
mode,
NULL, &ckey,
ivPtr, padding,
NULL, &ccHand);
if(crtn) {
p12LogCssmError("CSSM_CSP_CreateSymmetricContext", crtn);
goto errOut;
}
memset(&wrappedKey, 0, sizeof(CSSM_KEY));
attr.AttributeType = CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT;
attr.AttributeLength = sizeof(uint32);
attr.Attribute.Uint32 = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8;
crtn = CSSM_UpdateContextAttributes(
ccHand,
1,
&attr);
if(crtn) {
p12LogCssmError("CSSM_UpdateContextAttributes", crtn);
goto errOut;
}
crtn = CSSM_WrapKey(ccHand,
privKeyCreds,
privKey,
&descrData, &wrappedKey);
if(crtn) {
p12LogCssmError("CSSM_WrapKey", crtn);
}
else {
coder.allocCopyItem(wrappedKey.KeyData, shroudedKeyBits);
freeCssmMemory(cspHand, wrappedKey.KeyData.Data);
}
errOut:
if(ccHand) {
CSSM_DeleteContext(ccHand);
}
CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE);
return crtn;
}