/* * 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; }
OSStatus impExpWrappedOpenSSHExport( SecKeyRef secKey, SecItemImportExportFlags flags, const SecKeyImportExportParameters *keyParams, // optional const CssmData &descData, CFMutableDataRef outData) // output appended here { CSSM_CSP_HANDLE cspdlHand = 0; OSStatus ortn; bool releaseCspHand = false; CSSM_RETURN crtn; if(keyParams == NULL) { return errSecParam; } /* we need a CSPDL handle - try to get it from the key */ ortn = SecKeyGetCSPHandle(secKey, &cspdlHand); if(ortn) { cspdlHand = cuCspStartup(CSSM_FALSE); if(cspdlHand == 0) { return CSSMERR_CSSM_ADDIN_LOAD_FAILED; } releaseCspHand = true; } /* subsequent errors to errOut: */ /* derive wrapping key */ CSSM_KEY wrappingKey; crtn = openSSHv1DeriveKey(cspdlHand, keyParams, VP_Export, &wrappingKey); if(crtn) { goto errOut; } /* GO */ CSSM_KEY wrappedKey; memset(&wrappedKey, 0, sizeof(CSSM_KEY)); crtn = impExpExportKeyCommon(cspdlHand, secKey, &wrappingKey, &wrappedKey, CSSM_ALGID_OPENSSH1, CSSM_ALGMODE_NONE, CSSM_PADDING_NONE, CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1, CSSM_ATTRIBUTE_NONE, CSSM_KEYBLOB_RAW_FORMAT_NONE, &descData, NULL); // IV if(crtn) { goto errOut; } /* the wrappedKey's KeyData is out output */ CFDataAppendBytes(outData, wrappedKey.KeyData.Data, wrappedKey.KeyData.Length); CSSM_FreeKey(cspdlHand, NULL, &wrappedKey, CSSM_FALSE); errOut: if(releaseCspHand) { cuCspDetachUnload(cspdlHand, CSSM_FALSE); } return crtn; }
/* * Free resources allocated in cdsaDeriveKey(). */ CSSM_RETURN cdsaFreeKey( CSSM_CSP_HANDLE cspHandle, CSSM_KEY_PTR key) { return CSSM_FreeKey(cspHandle, NULL, // access cred key, CSSM_FALSE); // don't delete since it wasn't permanent }
/* * If cspHand is provided instead of importKeychain, the CSP * handle MUST be for the CSPDL, not for the raw CSP. */ OSStatus impExpWrappedOpenSSHImport( CFDataRef inData, SecKeychainRef importKeychain, // optional CSSM_CSP_HANDLE cspHand, // required SecItemImportExportFlags flags, const SecKeyImportExportParameters *keyParams, // optional const char *printName, CFMutableArrayRef outArray) // optional, append here { OSStatus ortn; impExpKeyUnwrapParams unwrapParams; assert(cspHand != 0); if(keyParams == NULL) { return errSecParam; } memset(&unwrapParams, 0, sizeof(unwrapParams)); /* derive unwrapping key */ CSSM_KEY unwrappingKey; ortn = openSSHv1DeriveKey(cspHand, keyParams, VP_Import, &unwrappingKey); if(ortn) { return ortn; } /* set up key to unwrap */ CSSM_KEY wrappedKey; CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader; memset(&wrappedKey, 0, sizeof(CSSM_KEY)); hdr.HeaderVersion = CSSM_KEYHEADER_VERSION; /* CspId : don't care */ hdr.BlobType = CSSM_KEYBLOB_WRAPPED; hdr.Format = CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1; hdr.AlgorithmId = CSSM_ALGID_RSA; /* the oly algorithm supported in SSHv1 */ hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; /* LogicalKeySizeInBits : calculated by CSP during unwrap */ hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; hdr.KeyUsage = CSSM_KEYUSE_ANY; wrappedKey.KeyData.Data = (uint8 *)CFDataGetBytePtr(inData); wrappedKey.KeyData.Length = CFDataGetLength(inData); unwrapParams.unwrappingKey = &unwrappingKey; unwrapParams.encrAlg = CSSM_ALGID_OPENSSH1; /* GO */ ortn = impExpImportKeyCommon(&wrappedKey, importKeychain, cspHand, flags, keyParams, &unwrapParams, printName, outArray); if(unwrappingKey.KeyData.Data != NULL) { CSSM_FreeKey(cspHand, NULL, &unwrappingKey, CSSM_FALSE); } return ortn; }
/* * Find private key by label, modify its Label attr to be the * hash of the associated public key. * Also optionally re-sets the key's PrintName attribute; used to reset * this attr from the random label we create when first unwrap it * to the friendly name we find later after parsing attributes. * Detection of a duplicate key when updating the key's attributes * results in a lookup of the original key and returning it in * foundKey. */ CSSM_RETURN p12SetPubKeyHash( CSSM_CSP_HANDLE cspHand, // where the key lives CSSM_DL_DB_HANDLE dlDbHand, // ditto CSSM_DATA &keyLabel, // for DB lookup CSSM_DATA_PTR newPrintName, // optional SecNssCoder &coder, // for mallocing newLabel CSSM_DATA &newLabel, // RETURNED with label as hash CSSM_KEY_PTR &foundKey) // RETURNED { 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; /* hope this cast is OK */ predicate.Attribute.Value = &keyLabel; query.SelectionPredicate = &predicate; query.QueryLimits.TimeLimit = 0; // FIXME - meaningful? query.QueryLimits.SizeLimit = 1; // FIXME - meaningful? query.QueryFlags = CSSM_QUERY_RETURN_DATA; /* build Record attribute with one or two attrs */ 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, // theData &record); /* abort only on success */ if(crtn != CSSM_OK) { p12LogCssmError("CSSM_DL_DataGetFirst", crtn); p12ErrorLog("***p12SetPubKeyHash: can't find private key\n"); return crtn; } /* subsequent errors to errOut: */ if(keyData.Data == NULL) { p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n"); crtn = CSSMERR_CSSM_INTERNAL_ERROR; goto errOut; } privKey = (CSSM_KEY_PTR)keyData.Data; /* public key hash via passthrough - works on any key, any CSP/CSPDL.... */ /* * Warning! This relies on the current default ACL meaning "allow this * current app to access this private key" since we created the key. */ 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; } /* * Replace Label attr data with hash. * NOTE: the module which allocated this attribute data - a DL - * was loaded and attached by out client layer, not by us. Thus * we can't use the memory allocator functions *we* used when * attaching to the CSP - we have to use the ones * which the client registered with the DL. */ 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); } /* modify key attributes */ attr[0].Value = keyDigest; if(newPrintName) { attr[1].Value = newPrintName; } crtn = CSSM_DL_DataModify(dlDbHand, CSSM_DL_DB_RECORD_PRIVATE_KEY, record, &recordAttrs, NULL, // DataToBeModified CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); switch(crtn) { case CSSM_OK: /* give caller the key's new label */ coder.allocCopyItem(*keyDigest, newLabel); break; default: p12LogCssmError("CSSM_DL_DataModify", crtn); break; case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA: { /* * Special case: dup private key. The label we just tried to modify is * the public key hash so we can be confident that this really is a dup. * Delete it, look up the original, and return the original to caller. */ CSSM_RETURN drtn = CSSM_DL_DataDelete(dlDbHand, record); if(drtn) { p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn); crtn = drtn; break; } /* Free items created in last search */ CSSM_DL_DataAbortQuery(dlDbHand, resultHand); resultHand = 0; CSSM_DL_FreeUniqueRecord(dlDbHand, record); record = NULL; /* lookup by label as public key hash this time */ predicate.Attribute.Value = keyDigest; drtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, NULL, // no attrs this time &keyData, &record); if(drtn) { p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn); crtn = drtn; break; } foundKey = (CSSM_KEY_PTR)keyData.Data; /* give caller the key's actual label */ coder.allocCopyItem(*keyDigest, newLabel); break; } } errOut: /* free resources */ if(resultHand) { CSSM_DL_DataAbortQuery(dlDbHand, resultHand); } if(record) { CSSM_DL_FreeUniqueRecord(dlDbHand, record); } if(ccHand) { CSSM_DeleteContext(ccHand); } if(privKey) { /* key created by the CSPDL */ CSSM_FreeKey(cspHand, NULL, privKey, CSSM_FALSE); freeCssmMemory(dlDbHand.DLHandle, privKey); } if(keyDigest) { /* mallocd by someone else's CSP */ freeCssmMemory(cspHand, keyDigest->Data); freeCssmMemory(cspHand, keyDigest); } return crtn; }
/* * 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; }
/* * Find private key by label, modify its Label attr to be the * hash of the associated public key. */ static CSSM_RETURN setPubKeyHash( CSSM_CSP_HANDLE cspHand, CSSM_DL_DB_HANDLE dlDbHand, const CSSM_KEY *pubOrPrivKey, // to get hash; raw or ref/CSPDL const char *keyLabel) // look up by this { CSSM_QUERY query; CSSM_SELECTION_PREDICATE predicate; CSSM_DB_UNIQUE_RECORD_PTR record = NULL; CSSM_RETURN crtn; CSSM_DATA labelData; CSSM_HANDLE resultHand; labelData.Data = (uint8 *)keyLabel; labelData.Length = strlen(keyLabel) + 1; // incl. NULL 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 = "Label"; predicate.Attribute.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; predicate.Attribute.Value = &labelData; query.SelectionPredicate = &predicate; query.QueryLimits.TimeLimit = 0; query.QueryLimits.SizeLimit = 1; query.QueryFlags = 0; /* build Record attribute with one attr */ CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs; CSSM_DB_ATTRIBUTE_DATA attr; attr.Info.AttributeNameFormat = CSSM_DB_ATTRIBUTE_NAME_AS_STRING; attr.Info.Label.AttributeName = "Label"; attr.Info.AttributeFormat = CSSM_DB_ATTRIBUTE_FORMAT_BLOB; recordAttrs.DataRecordType = CSSM_DL_DB_RECORD_PRIVATE_KEY; recordAttrs.NumberOfAttributes = 1; recordAttrs.AttributeData = &attr; crtn = CSSM_DL_DataGetFirst(dlDbHand, &query, &resultHand, &recordAttrs, NULL, // hopefully optional ...theData, &record); /* abort only on success */ if(crtn != CSSM_OK) { sec_error("CSSM_DL_DataGetFirst: setPubKeyHash: can't find private key: %s", sec_errstr(crtn)); return crtn; } /* * If specified key is a ref key, do NULL unwrap for use with raw CSP. * If the CSPDL and SecurityServer support the key digest passthrough * this is unnecessary. */ CSSM_KEY rawKeyToDigest; if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) { crtn = refKeyToRaw(cspHand, pubOrPrivKey, &rawKeyToDigest); if(crtn) { sec_error("setPubKeyHash: Error converting public key to raw format: %s", sec_errstr(crtn)); return crtn; } } else { /* use as is */ rawKeyToDigest = *pubOrPrivKey; } /* connect to raw CSP */ CSSM_CSP_HANDLE rawCspHand = srCspStartup(CSSM_TRUE); if(rawCspHand == 0) { printf("***Error connecting to raw CSP; aborting.\n"); return -1; } /* calculate hash of pub key from private or public part */ CSSM_DATA_PTR keyDigest = NULL; CSSM_CC_HANDLE ccHand; crtn = CSSM_CSP_CreatePassThroughContext(rawCspHand, &rawKeyToDigest, &ccHand); if(ccHand == 0) { sec_error("CSSM_CSP_CreatePassThroughContext: Error calculating public key hash. Aborting: %s", sec_errstr(crtn)); return -1; } crtn = CSSM_CSP_PassThrough(ccHand, CSSM_APPLECSP_KEYDIGEST, NULL, (void **)&keyDigest); if(crtn) { sec_error("CSSM_CSP_PassThrough(PUBKEYHASH): Error calculating public key hash. Aborting: %s", sec_errstr(crtn)); return crtn; } if(pubOrPrivKey->KeyHeader.BlobType == CSSM_KEYBLOB_REFERENCE) { /* created in refKeyToRaw().... */ CSSM_FreeKey(cspHand, NULL, &rawKeyToDigest, CSSM_FALSE); } CSSM_DeleteContext(ccHand); CSSM_ModuleDetach(rawCspHand); /* * Replace Label attr data with hash. * NOTE: the module which allocated this attribute data - a DL - * was loaded and attached by the Sec layer, not by us. Thus * we can't use the memory allocator functions *we* used when * attaching to the CSPDL - we have to use the ones * which the Sec layer registered with the DL. */ CSSM_API_MEMORY_FUNCS memFuncs; crtn = CSSM_GetAPIMemoryFunctions(dlDbHand.DLHandle, &memFuncs); if(crtn) { sec_error("CSSM_GetAPIMemoryFunctions(DLHandle): Error: %s", sec_errstr(crtn)); /* oh well, leak and continue */ } else { memFuncs.free_func(attr.Value->Data, memFuncs.AllocRef); memFuncs.free_func(attr.Value, memFuncs.AllocRef); } attr.Value = keyDigest; /* modify key attributes */ crtn = CSSM_DL_DataModify(dlDbHand, CSSM_DL_DB_RECORD_PRIVATE_KEY, record, &recordAttrs, NULL, // DataToBeModified CSSM_DB_MODIFY_ATTRIBUTE_REPLACE); if(crtn) { sec_error("CSSM_DL_DataModify(PUBKEYHASH): Error setting public key hash. Aborting: %s", sec_errstr(crtn)); return crtn; } crtn = CSSM_DL_DataAbortQuery(dlDbHand, resultHand); if(crtn) { sec_error("CSSM_DL_DataAbortQuery: Error while stopping query: %s", sec_errstr(crtn)); /* let's keep going in this case */ } crtn = CSSM_DL_FreeUniqueRecord(dlDbHand, record); if(crtn) { sec_error("CSSM_DL_FreeUniqueRecord: Error while freeing record: %s", sec_errstr(crtn)); /* let's keep going in this case */ crtn = CSSM_OK; } /* free resources */ if (keyDigest) { srAppFree(keyDigest->Data, NULL); srAppFree(keyDigest, NULL); } return CSSM_OK; }
OSStatus createPair(CFStringRef hostName,CFStringRef userName,SecKeychainRef keychainRef, uint32 keySizeInBits, CFDataRef *cert) { SecCertificateRef certRef = NULL; CSSM_DL_DB_HANDLE dlDbHand = {0, 0}; CSSM_CSP_HANDLE cspHand = 0; CSSM_TP_HANDLE tpHand = 0; CSSM_CL_HANDLE clHand = 0; CSSM_KEY_PTR pubKey = NULL; CSSM_KEY_PTR privKey = NULL; CSSM_DATA certData = {0, NULL}; char *hostStr = NULL; char *userStr = NULL; OSStatus ortn; CSSM_OID algOid = SR_CERT_SIGNATURE_ALG_OID; hostStr = secCopyCString(hostName); userStr = secCopyCString(userName); if (!hostStr || !userStr) // could not convert to UTF-8 { ortn = paramErr; goto xit; } // open keychain, connect to all the CDSA modules we'll need ortn = SecKeychainGetCSPHandle(keychainRef, &cspHand); if (ortn) goto xit; ortn = SecKeychainGetDLDBHandle(keychainRef, &dlDbHand); if (ortn) goto xit; tpHand = srTpStartup(); if (tpHand == 0) { ortn = ioErr; goto xit; } clHand = srClStartup(); if (clHand == 0) { ortn = ioErr; goto xit; } // generate key pair, private key stored in keychain ortn = generateKeyPair(cspHand, dlDbHand, SR_KEY_ALGORITHM, keySizeInBits, "FileVault Master Password Key", &pubKey, &privKey); if (ortn) goto xit; // generate the cert ortn = createRootCert(tpHand,clHand,cspHand,pubKey,privKey,hostStr,userStr, SR_CERT_SIGNATURE_ALGORITHM,&algOid,&certData); if (ortn) goto xit; // store the cert in the same DL/DB as the key pair [see SecCertificateCreateFromData] ortn = SecCertificateCreateFromData(&certData, CSSM_CERT_X_509v3, CSSM_CERT_ENCODING_DER, &certRef); if (ortn) goto xit; ortn = SecCertificateAddToKeychain(certRef, keychainRef); if (ortn) goto xit; // return the cert to caller *cert = CFDataCreate(NULL, certData.Data, certData.Length); // cleanup xit: if (certRef) CFRelease(certRef); if (certData.Data) free(certData.Data); if (hostStr) free(hostStr); if (userStr) free(userStr); if (tpHand) CSSM_ModuleDetach(tpHand); if (clHand) CSSM_ModuleDetach(clHand); if (pubKey) { CSSM_FreeKey(cspHand, NULL, // access cred pubKey, CSSM_FALSE); // delete APP_FREE(pubKey); } if (privKey) { CSSM_FreeKey(cspHand, NULL, // access cred privKey, CSSM_FALSE); // delete APP_FREE(privKey); } return ortn; }
/* * Common code to derive a wrap/unwrap key for OpenSSHv1. * Caller must CSSM_FreeKey when done. */ static CSSM_RETURN openSSHv1DeriveKey( CSSM_CSP_HANDLE cspHand, const SecKeyImportExportParameters *keyParams, // required impExpVerifyPhrase verifyPhrase, // for secure passphrase CSSM_KEY_PTR symKey) // RETURNED { CSSM_KEY *passKey = NULL; CFDataRef cfPhrase = NULL; CSSM_RETURN crtn; OSStatus ortn; CSSM_DATA dummyLabel; uint32 keyAttr; CSSM_CC_HANDLE ccHand = 0; CSSM_ACCESS_CREDENTIALS creds; CSSM_CRYPTO_DATA seed; CSSM_DATA nullParam = {0, NULL}; memset(symKey, 0, sizeof(CSSM_KEY)); /* passphrase or passkey? */ ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, verifyPhrase, (CFTypeRef *)&cfPhrase, &passKey); if(ortn) { return ortn; } /* subsequent errors to errOut: */ memset(&seed, 0, sizeof(seed)); if(cfPhrase != NULL) { /* TBD - caller-supplied empty passphrase means "export in the clear" */ size_t len = CFDataGetLength(cfPhrase); seed.Param.Data = (uint8 *)malloc(len); seed.Param.Length = len; memmove(seed.Param.Data, CFDataGetBytePtr(cfPhrase), len); CFRelease(cfPhrase); } memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand, CSSM_ALGID_OPENSSH1, CSSM_ALGID_OPENSSH1, CC_MD5_DIGEST_LENGTH * 8, &creds, passKey, // BaseKey 0, // iterationCount NULL, // salt &seed, &ccHand); if(crtn) { SecSSHDbg("openSSHv1DeriveKey CSSM_CSP_CreateDeriveKeyContext failure"); goto errOut; } /* not extractable even for the short time this key lives */ keyAttr = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE; dummyLabel.Data = (uint8 *)"temp unwrap key"; dummyLabel.Length = strlen((char *)dummyLabel.Data); crtn = CSSM_DeriveKey(ccHand, &nullParam, CSSM_KEYUSE_ANY, keyAttr, &dummyLabel, NULL, // cred and acl symKey); if(crtn) { SecSSHDbg("openSSHv1DeriveKey CSSM_DeriveKey failure"); } errOut: if(ccHand != 0) { CSSM_DeleteContext(ccHand); } if(passKey != NULL) { CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE); free(passKey); } if(seed.Param.Data) { memset(seed.Param.Data, 0, seed.Param.Length); free(seed.Param.Data); } return crtn; }