--- /dev/null
+/*
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved.
+ *
+ * The contents of this file constitute Original Code as defined in and are
+ * subject to the Apple Public Source License Version 1.2 (the 'License').
+ * You may not use this file except in compliance with the License. Please
+ * obtain a copy of the License at http://www.apple.com/publicsource and
+ * read it before using this file.
+ *
+ * This Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ */
+
+/*
+ * p12Crypto.cpp - PKCS12 Crypto routines. App space reference version.
+ *
+ * Created 2/28/03 by Doug Mitchell.
+ */
+
+#include "p12Crypto.h"
+#include "p12pbe.h"
+#include <security_pkcs12/pkcs12Utils.h>
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <Security/cssmapple.h>
+
+/*
+ * Free memory via specified plugin's app-level allocator
+ */
+static void appFreeCssmMemory(
+ CSSM_HANDLE hand,
+ void *p)
+{
+ CSSM_API_MEMORY_FUNCS memFuncs;
+ CSSM_RETURN crtn = CSSM_GetAPIMemoryFunctions(hand, &memFuncs);
+ if(crtn) {
+ cssmPerror("CSSM_GetAPIMemoryFunctions", crtn);
+ /* oh well, leak and continue */
+ return;
+ }
+ memFuncs.free_func(p, memFuncs.AllocRef);
+}
+
+/*
+ * Given appropriate P12-style parameters, cook up a CSSM_KEY.
+ * Eventually this will use DeriveKey; for now we do it ourself.
+ */
+CSSM_RETURN p12KeyGen_app(
+ CSSM_CSP_HANDLE cspHand,
+ CSSM_KEY &key,
+ bool isForEncr, // true: en/decrypt false: MAC
+ CSSM_ALGORITHMS keyAlg,
+ CSSM_ALGORITHMS pbeHashAlg, // SHA1, MD5 only
+ uint32 keySizeInBits,
+ uint32 iterCount,
+ const CSSM_DATA &salt,
+ const CSSM_DATA &pwd, // unicode, double null terminated
+ CSSM_DATA &iv, // referent is optional
+ SecNssCoder &coder) // for mallocing KeyData
+{
+ memset(&key, 0, sizeof(CSSM_KEY));
+ unsigned keyBytes = (keySizeInBits + 7) / 8;
+ coder.allocItem(key.KeyData, keyBytes);
+ CSSM_RETURN crtn = p12PbeGen_app(pwd,
+ salt.Data, salt.Length,
+ iterCount,
+ isForEncr ? PBE_ID_Key : PBE_ID_Mac,
+ pbeHashAlg,
+ cspHand,
+ (unsigned char *)key.KeyData.Data,
+ key.KeyData.Length);
+ if(crtn) {
+ cuPrintError("p12PbeGen(key)", crtn);
+ return crtn;
+ }
+
+ /* fill in the blanks */
+ CSSM_KEYHEADER &hdr = key.KeyHeader;
+ hdr.HeaderVersion = CSSM_KEYHEADER_VERSION;
+ /* CspId blank */
+ hdr.BlobType = CSSM_KEYBLOB_RAW;
+ hdr.AlgorithmId = keyAlg;
+ hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
+ hdr.KeyClass = CSSM_KEYCLASS_SESSION_KEY;
+ hdr.KeyUsage = CSSM_KEYUSE_ANY;
+ /* start/end date unknown, leave zero */
+ hdr.WrapAlgorithmId = CSSM_ALGID_NONE;
+ hdr.WrapMode = CSSM_ALGMODE_NONE;
+ hdr.LogicalKeySizeInBits = keySizeInBits;
+
+ /* P12 style IV derivation, optional */
+ if(iv.Data != NULL) {
+ crtn = p12PbeGen_app(pwd,
+ salt.Data, salt.Length,
+ iterCount,
+ PBE_ID_IV,
+ pbeHashAlg,
+ cspHand,
+ iv.Data, iv.Length);
+ if(crtn) {
+ cuPrintError("p12PbeGen (IV)", crtn);
+ return crtn;
+ }
+ }
+
+ return CSSM_OK;
+}
+
+/*
+ * Decrypt (typically, an encrypted P7 ContentInfo contents or
+ * a P12 ShroudedKeyBag).
+ */
+CSSM_RETURN p12Decrypt_app(
+ CSSM_CSP_HANDLE cspHand,
+ const CSSM_DATA &cipherText,
+ CSSM_ALGORITHMS keyAlg,
+ CSSM_ALGORITHMS encrAlg,
+ CSSM_ALGORITHMS pbeHashAlg, // SHA1, MD5 only
+ uint32 keySizeInBits,
+ uint32 blockSizeInBytes, // for IV
+ CSSM_PADDING padding, // CSSM_PADDING_PKCS7, etc.
+ CSSM_ENCRYPT_MODE mode, // CSSM_ALGMODE_CBCPadIV8, etc.
+ uint32 iterCount,
+ const CSSM_DATA &salt,
+ const CSSM_DATA &pwd, // unicode, double null terminated
+ SecNssCoder &coder, // for mallocing KeyData and plainText
+ CSSM_DATA &plainText)
+{
+ CSSM_RETURN crtn;
+ CSSM_KEY ckey;
+ CSSM_CC_HANDLE ccHand = 0;
+
+ /* P12 style IV derivation, optional */
+ CSSM_DATA iv = {0, NULL};
+ CSSM_DATA_PTR ivPtr = NULL;
+ if(blockSizeInBytes) {
+ coder.allocItem(iv, blockSizeInBytes);
+ ivPtr = &iv;
+ }
+
+ /* P12 style key derivation */
+ crtn = p12KeyGen_app(cspHand, ckey, true, keyAlg, pbeHashAlg,
+ keySizeInBits, iterCount, salt, pwd, iv, coder);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* CSSM context */
+ crtn = CSSM_CSP_CreateSymmetricContext(cspHand,
+ encrAlg,
+ mode,
+ NULL, // access cred
+ &ckey,
+ ivPtr, // InitVector, optional
+ padding,
+ NULL, // Params
+ &ccHand);
+ if(crtn) {
+ cuPrintError("CSSM_CSP_CreateSymmetricContext", crtn);
+ return crtn;
+ }
+
+ /* go - CSP mallocs ptext and rem data */
+ CSSM_DATA ourPtext = {0, NULL};
+ CSSM_DATA remData = {0, NULL};
+ uint32 bytesDecrypted;
+ crtn = CSSM_DecryptData(ccHand,
+ &cipherText,
+ 1,
+ &ourPtext,
+ 1,
+ &bytesDecrypted,
+ &remData);
+ if(crtn) {
+ cuPrintError("CSSM_EncryptData", crtn);
+ }
+ else {
+ coder.allocCopyItem(ourPtext, plainText);
+ plainText.Length = bytesDecrypted;
+
+ /* plaintext copied into coder space; free the memory allocated
+ * by the CSP */
+ appFreeCssmMemory(cspHand, ourPtext.Data);
+ }
+ /* an artifact of CSPFUllPLuginSession - this never contains
+ * valid data but sometimes gets mallocds */
+ if(remData.Data) {
+ appFreeCssmMemory(cspHand, remData.Data);
+ }
+ CSSM_DeleteContext(ccHand);
+ return crtn;
+}
+
+/*
+ * Calculate the MAC for a PFX. Caller is either going compare
+ * the result against an existing PFX's MAC or drop the result into
+ * a newly created PFX.
+ */
+CSSM_RETURN p12GenMac_app(
+ CSSM_CSP_HANDLE cspHand,
+ const CSSM_DATA &ptext, // e.g., NSS_P12_DecodedPFX.derAuthSaafe
+ CSSM_ALGORITHMS alg, // better be SHA1!
+ unsigned iterCount,
+ const CSSM_DATA &salt,
+ const CSSM_DATA &pwd, // unicode, double null terminated
+ SecNssCoder &coder, // for mallocing macData
+ CSSM_DATA &macData) // RETURNED
+{
+ CSSM_RETURN crtn;
+
+ /* P12 style key derivation */
+ unsigned keySizeInBits;
+ CSSM_ALGORITHMS hmacAlg;
+ switch(alg) {
+ case CSSM_ALGID_SHA1:
+ keySizeInBits = 160;
+ hmacAlg = CSSM_ALGID_SHA1HMAC;
+ break;
+ case CSSM_ALGID_MD5:
+ /* not even sure if this is legal in p12 world... */
+ keySizeInBits = 128;
+ hmacAlg = CSSM_ALGID_MD5HMAC;
+ break;
+ default:
+ return CSSMERR_CSP_INVALID_ALGORITHM;
+ }
+ CSSM_KEY macKey;
+ CSSM_DATA iv = {0, NULL};
+ crtn = p12KeyGen_app(cspHand, macKey, false, hmacAlg, alg,
+ keySizeInBits, iterCount, salt, pwd, iv, coder);
+ if(crtn) {
+ return crtn;
+ }
+
+ /* prealloc the mac data */
+ coder.allocItem(macData, keySizeInBits / 8);
+ CSSM_CC_HANDLE ccHand = 0;
+ crtn = CSSM_CSP_CreateMacContext(cspHand, hmacAlg, &macKey, &ccHand);
+ if(crtn) {
+ cuPrintError("CSSM_CSP_CreateMacContext", crtn);
+ return crtn;
+ }
+
+ crtn = CSSM_GenerateMac (ccHand, &ptext, 1, &macData);
+ if(crtn) {
+ cuPrintError("CSSM_GenerateMac", crtn);
+ }
+ CSSM_DeleteContext(ccHand);
+ return crtn;
+}
+
+/*
+ * Verify MAC on an existing PFX.
+ */
+CSSM_RETURN p12VerifyMac_app(
+ const NSS_P12_DecodedPFX &pfx,
+ CSSM_CSP_HANDLE cspHand,
+ const CSSM_DATA &pwd, // unicode, double null terminated
+ SecNssCoder &coder) // for temp mallocs
+{
+ 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) {
+ /* optional, default 1 */
+ iterCount = 1;
+ }
+
+ /*
+ * In classic fashion, the PKCS12 spec now says:
+ *
+ * When password integrity mode is used to secure a PFX PDU,
+ * an SHA-1 HMAC is computed on the BER-encoding of the contents
+ * of the content field of the authSafe field in the PFX PDU.
+ *
+ * So here we go.
+ */
+ CSSM_DATA genMac;
+ CSSM_RETURN crtn = p12GenMac_app(cspHand, *pfx.authSafe.content.data,
+ macAlg, iterCount, macData.macSalt, pwd, coder, genMac);
+ if(crtn) {
+ return crtn;
+ }
+ if(nssCompareCssmData(&genMac, &digestInfo.digest)) {
+ return CSSM_OK;
+ }
+ else {
+ return CSSMERR_CSP_VERIFY_FAILED;
+ }
+}
+