/* uint32 --> CSSM_DATA */ 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; } }
/* * 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( 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, /* exactly one of the following two must be valid */ const CSSM_DATA *pwd, // unicode external representation const CSSM_KEY *passKey, SecNssCoder &coder, // for mallocing macData CSSM_DATA &macData) // RETURNED { CSSM_RETURN crtn; CSSM_CC_HANDLE ccHand = 0; /* 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(cspHand, macKey, false, hmacAlg, alg, keySizeInBits, iterCount, salt, pwd, passKey, iv); if(crtn) { return crtn; } /* subsequent errors to errOut: */ /* prealloc the mac data */ 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; }
void p12GenSalt( CSSM_DATA &salt, SecNssCoder &coder) { DevRandomGenerator rng; coder.allocItem(salt, P12_SALT_LEN); rng.random(salt.Data, P12_SALT_LEN); }
/* convert App passphrase to array of chars used in P12 PBE */ 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); }
/* * Generate random label string to allow associating an imported private * key with a cert. */ void p12GenLabel( CSSM_DATA &label, SecNssCoder &coder) { /* first a random uint32 */ uint8 d[4]; DevRandomGenerator rng; rng.random(d, 4); CSSM_DATA cd = {4, d}; uint32 i; p12DataToInt(cd, i); /* sprintf that into a real string */ coder.allocItem(label, 9); memset(label.Data, 0, 9); sprintf((char *)label.Data, "%08X", (unsigned)i); }
void CL_cssmDistPointsToNss( const CE_CRLDistPointsSyntax &cdsaObj, NSS_CRLDistributionPoints &nssObj, SecNssCoder &coder) { memset(&nssObj, 0, sizeof(nssObj)); unsigned numPoints = cdsaObj.numDistPoints; if(numPoints == 0) { return; } nssObj.distPoints = (NSS_DistributionPoint **)clNssNullArray(numPoints, coder); for(unsigned dex=0; dex<numPoints; dex++) { nssObj.distPoints[dex] = (NSS_DistributionPoint *) coder.malloc(sizeof(NSS_DistributionPoint)); NSS_DistributionPoint *npoint = nssObj.distPoints[dex]; memset(npoint, 0, sizeof(NSS_DistributionPoint)); CE_CRLDistributionPoint *cpoint = &cdsaObj.distPoints[dex]; /* all fields are optional */ if(cpoint->distPointName) { /* encode and drop into ASN_ANY slot */ npoint->distPointName = (CSSM_DATA *) coder.malloc(sizeof(CSSM_DATA)); CL_encodeDistributionPointName(*cpoint->distPointName, *npoint->distPointName, coder); } if(cpoint->reasonsPresent) { /* bit string, presumed max length 8 bits */ coder.allocItem(npoint->reasons, 1); npoint->reasons.Data[0] = cpoint->reasons; /* adjust for bit string length */ npoint->reasons.Length = 8; } if(cpoint->crlIssuer) { CL_cssmGeneralNamesToNss(*cpoint->crlIssuer, npoint->crlIssuer, coder); } } }
/* * Attempt to convert a CFStringRef, which represents a SafeBag's * FriendlyName, to a UTF8-encoded CSSM_DATA. The CSSM_DATA and its * referent are allocated in the specified SecNssCoder's memory. * No guarantee that this conversion works. If it doesn't we return * NULL and caller must be prepared to deal with that. */ 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)) { /* not convertible from native Unicode to UTF8 */ return NULL; } return rtn; }
/* * Given an array of PEM parameter lines, infer parameters for key derivation and * encryption. */ static OSStatus opensslPbeParams( CFArrayRef paramLines, // elements are CFStrings SecNssCoder &coder, // IV allocd with this /* remaining arguments RETURNED */ CSSM_ALGORITHMS &pbeAlg, CSSM_ALGORITHMS &keyAlg, CSSM_ALGORITHMS &encrAlg, CSSM_ENCRYPT_MODE &encrMode, CSSM_PADDING &encrPad, uint32 &keySizeInBits, unsigned &blockSizeInBytes, CSSM_DATA &iv) { /* * This format requires PEM parameter lines. We could have gotten here * without them if caller specified wrong format. */ if(paramLines == NULL) { SecImpExpDbg("importWrappedKeyOpenssl: no PEM parameter lines"); return errSecUnknownFormat; } CFStringRef dekInfo = NULL; CFIndex numLines = CFArrayGetCount(paramLines); for(CFIndex dex=0; dex<numLines; dex++) { CFStringRef str = (CFStringRef)CFArrayGetValueAtIndex(paramLines, dex); CFRange range; range = CFStringFind(str, CFSTR("DEK-Info: "), 0); if(range.length != 0) { dekInfo = str; break; } } if(dekInfo == NULL) { SecImpExpDbg("importWrappedKeyOpenssl: no DEK-Info lines"); return errSecUnknownFormat; } /* drop down to C strings for low level grunging */ char cstr[1024]; if(!CFStringGetCString(dekInfo, cstr, sizeof(cstr), kCFStringEncodingASCII)) { SecImpExpDbg("importWrappedKeyOpenssl: bad DEK-Info line (1)"); return errSecUnknownFormat; } /* * This line looks like this: * DEK-Info: DES-CBC,A22977A0A6A6F696 * * Now parse, getting the cipher spec and the IV. */ char *cp = strchr(cstr, ':'); if(cp == NULL) { SecImpExpDbg("importWrappedKeyOpenssl: bad DEK-Info line (2)"); return errSecUnknownFormat; } if((cp[1] == ' ') && (cp[2] != '\0')) { /* as it normally does... */ cp += 2; } /* We only support DES and 3DES here */ if(!strncmp(cp, "DES-EDE3-CBC", 12)) { keyAlg = CSSM_ALGID_3DES_3KEY; encrAlg = CSSM_ALGID_3DES_3KEY_EDE; keySizeInBits = 64 * 3; blockSizeInBytes = 8; } else if(!strncmp(cp, "DES-CBC", 7)) { keyAlg = CSSM_ALGID_DES; encrAlg = CSSM_ALGID_DES; keySizeInBits = 64; blockSizeInBytes = 8; } else { SecImpExpDbg("importWrappedKeyOpenssl: unrecognized wrap alg (%s)", cp); return errSecUnknownFormat; } /* these are more or less fixed */ pbeAlg = CSSM_ALGID_PBE_OPENSSL_MD5; encrMode = CSSM_ALGMODE_CBCPadIV8; encrPad = CSSM_PADDING_PKCS7; /* now get the ASCII hex version of the IV */ cp = strchr(cp, ','); if(cp == NULL) { SecImpExpDbg("importWrappedKeyOpenssl: No IV in DEK-Info line"); return errSecUnknownFormat; } if(cp[1] != '\0') { cp++; } /* remainder should be just the IV */ if(strlen(cp) != (blockSizeInBytes * 2)) { SecImpExpDbg("importWrappedKeyOpenssl: bad IV in DEK-Info line (1)"); return errSecUnknownFormat; } coder.allocItem(iv, blockSizeInBytes); for(unsigned dex=0; dex<blockSizeInBytes; dex++) { if(hexToUchar(cp + (dex * 2), &iv.Data[dex])) { SecImpExpDbg("importWrappedKeyOpenssl: bad IV in DEK-Info line (2)"); return errSecUnknownFormat; } } return errSecSuccess; }
/* * Common code to derive an openssl-wrap style wrap/unwrap key. */ static OSStatus deriveKeyOpensslWrap( const SecKeyImportExportParameters *keyParams, // required CSSM_CSP_HANDLE cspHand, // required impExpVerifyPhrase vp, // import/export CSSM_ALGORITHMS pbeAlg, CSSM_ALGORITHMS keyAlg, uint32 keySizeInBits, const CSSM_DATA &salt, CSSM_KEY_PTR derivedKey) { CFDataRef cfPhrase = NULL; CSSM_KEY *passKey = NULL; OSStatus ortn; /* passphrase or passkey? */ ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, vp, (CFTypeRef *)&cfPhrase, &passKey); if(ortn) { return ortn; } /* subsequent errors to errOut: */ CSSM_CRYPTO_DATA seed; CSSM_CC_HANDLE ccHand = 0; CSSM_ACCESS_CREDENTIALS creds; SecNssCoder coder; CSSM_DATA param = {0, NULL}; CSSM_DATA dummyLabel; memset(&seed, 0, sizeof(seed)); if(cfPhrase != NULL) { size_t len = CFDataGetLength(cfPhrase); coder.allocItem(seed.Param, len); memmove(seed.Param.Data, CFDataGetBytePtr(cfPhrase), len); CFRelease(cfPhrase); } memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); ortn = CSSM_CSP_CreateDeriveKeyContext(cspHand, pbeAlg, keyAlg, keySizeInBits, &creds, passKey, // BaseKey 1, // iterCount - yup, this is what openssl does &salt, &seed, &ccHand); if(ortn) { SecImpExpDbg("deriveKeyOpensslWrap: CSSM_CSP_CreateDeriveKeyContext error"); goto errOut; } memset(derivedKey, 0, sizeof(CSSM_KEY)); dummyLabel.Data = (uint8 *)"temp unwrap key"; dummyLabel.Length = strlen((char *)dummyLabel.Data); ortn = CSSM_DeriveKey(ccHand, ¶m, // i.e., derived IV - don't want one CSSM_KEYUSE_ANY, /* not extractable even for the short time this key lives */ CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, &dummyLabel, NULL, // cred and acl derivedKey); if(ortn) { SecImpExpDbg("importWrappedKeyOpenssl: PKCS5 v1.5 CSSM_DeriveKey failure"); } errOut: if(ccHand != 0) { CSSM_DeleteContext(ccHand); } if(passKey != NULL) { CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE); free(passKey); } return ortn; }
/* * Wrap a private key, yielding shrouded key bits. */ CSSM_RETURN p12WrapKey( CSSM_CSP_HANDLE cspHand, CSSM_KEY_PTR privKey, const CSSM_ACCESS_CREDENTIALS *privKeyCreds, CSSM_ALGORITHMS keyAlg, // of the unwrapping key 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 external representation const CSSM_KEY *passKey, SecNssCoder &coder, // for mallocing keyBits CSSM_DATA &shroudedKeyBits) // RETURNED { 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; /* key must be extractable */ if (!(privKey->KeyHeader.KeyAttr & CSSM_KEYATTR_EXTRACTABLE)) { return errSecDataNotAvailable; } if(privKeyCreds == NULL) { /* i.e., key is from the bare CSP with no ACL support */ memset(&creds, 0, sizeof(creds)); privKeyCreds = &creds; } /* 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(cspHand, ckey, true, keyAlg, pbeHashAlg, keySizeInBits, iterCount, salt, pwd, passKey, iv); if(crtn) { return crtn; } /* subsequent errors to errOut: */ /* CSSM context */ crtn = CSSM_CSP_CreateSymmetricContext(cspHand, encrAlg, mode, NULL, // access cred &ckey, ivPtr, // InitVector, optional padding, NULL, // Params &ccHand); if(crtn) { p12LogCssmError("CSSM_CSP_CreateSymmetricContext", crtn); goto errOut; } memset(&wrappedKey, 0, sizeof(CSSM_KEY)); /* specify PKCS8 wrap format */ 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, // DescriptiveData &wrappedKey); if(crtn) { p12LogCssmError("CSSM_WrapKey", crtn); } else { coder.allocCopyItem(wrappedKey.KeyData, shroudedKeyBits); /* this was mallocd by CSP */ freeCssmMemory(cspHand, wrappedKey.KeyData.Data); } errOut: if(ccHand) { CSSM_DeleteContext(ccHand); } CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE); return crtn; }
/* * Unwrap a shrouded key. */ CSSM_RETURN p12UnwrapKey( CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE_PTR dlDbHand, // optional int keyIsPermanent, // nonzero - store in DB const CSSM_DATA &shroudedKeyBits, CSSM_ALGORITHMS keyAlg, // of the unwrapping key 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 external representation const CSSM_KEY *passKey, SecNssCoder &coder, // for mallocing privKey const CSSM_DATA &labelData, SecAccessRef access, // optional bool noAcl, CSSM_KEYUSE keyUsage, CSSM_KEYATTR_FLAGS keyAttrs, /* * Result: a private key, reference format, optionaly stored * in dlDbHand */ 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}; // not used for PKCS8 wrap CSSM_KEYATTR_FLAGS reqAttr = keyAttrs; ResourceControlContext rcc; ResourceControlContext *rccPtr = NULL; Security::KeychainCore::Access::Maker maker; /* 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(cspHand, ckey, true, keyAlg, pbeHashAlg, keySizeInBits, iterCount, salt, pwd, passKey, iv); if(crtn) { return crtn; } /* subsequent errors to errOut: */ /* CSSM context */ crtn = CSSM_CSP_CreateSymmetricContext(cspHand, encrAlg, mode, NULL, // access cred &ckey, ivPtr, // InitVector, optional padding, NULL, // Params &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; } } /* * Cook up minimal WrappedKey header fields */ 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; /* * This one we do not know. The CSP will figure out the format * of the unwrapped key after it decrypts the raw key material. */ hdr.AlgorithmId = CSSM_ALGID_NONE; hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; /* also inferred by CSP */ 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) { // Create a Access::Maker for the initial owner of the private key. memset(&rcc, 0, sizeof(rcc)); maker.initialOwner(rcc); rccPtr = &rcc; } crtn = CSSM_UnwrapKey(ccHand, NULL, // PublicKey &wrappedKey, keyUsage, reqAttr, &labelData, rccPtr, // CredAndAclEntry privKey, &descrData); // required if(crtn) { p12LogCssmError("CSSM_UnwrapKey", crtn); if(crtn == CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA) { /* report in a keychain-friendly way */ crtn = errSecDuplicateItem; } } // Finally fix the acl and owner of the private key to the // specified access control settings. if((crtn == CSSM_OK) && !noAcl) { try { CssmClient::KeyAclBearer bearer( cspHand, *privKey, Allocator::standard()); SecPointer<KeychainCore::Access> initialAccess(access ? KeychainCore::Access::required(access) : /* caller-supplied */ new KeychainCore::Access("privateKey")); /* default */ initialAccess->setAccess(bearer, maker); } catch (const CssmError &e) { /* not implemented means we're talking to the CSP which does * not implement ACLs */ 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; }
/* * Decrypt (typically, an encrypted P7 ContentInfo contents) */ CSSM_RETURN p12Encrypt( CSSM_CSP_HANDLE cspHand, const CSSM_DATA &plainText, 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, /* exactly one of the following two must be valid */ const CSSM_DATA *pwd, // unicode external representation const CSSM_KEY *passKey, SecNssCoder &coder, // for mallocing cipherText CSSM_DATA &cipherText) { CSSM_RETURN crtn; CSSM_KEY ckey; CSSM_CC_HANDLE ccHand = 0; CSSM_DATA ourCtext = {0, NULL}; CSSM_DATA remData = {0, NULL}; /* 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(cspHand, ckey, true, keyAlg, pbeHashAlg, keySizeInBits, iterCount, salt, pwd, passKey, iv); if(crtn) { return crtn; } /* subsequent errors to errOut: */ /* 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); goto errOut; } /* go - CSP mallocs ctext and rem data */ 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; /* plaintext copied into coder space; free the memory allocated * by the CSP */ freeCssmMemory(cspHand, ourCtext.Data); } /* an artifact of CSPFUllPLuginSession - this never contains * valid data but sometimes gets mallocds */ if(remData.Data) { freeCssmMemory(cspHand, remData.Data); } errOut: if(ccHand) { CSSM_DeleteContext(ccHand); } CSSM_FreeKey(cspHand, NULL, &ckey, CSSM_FALSE); return crtn; }