X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/Security/libsecurity_keychain/lib/SecImportExportPkcs8.cpp diff --git a/Security/libsecurity_keychain/lib/SecImportExportPkcs8.cpp b/Security/libsecurity_keychain/lib/SecImportExportPkcs8.cpp deleted file mode 100644 index 13547958..00000000 --- a/Security/libsecurity_keychain/lib/SecImportExportPkcs8.cpp +++ /dev/null @@ -1,978 +0,0 @@ -/* - * Copyright (c) 2000-2004,2011-2014 Apple Inc. All Rights Reserved. - * - * @APPLE_LICENSE_HEADER_START@ - * - * This file contains Original Code and/or Modifications of Original Code - * as defined in and that are subject to the Apple Public Source License - * Version 2.0 (the 'License'). You may not use this file except in - * compliance with the License. Please obtain a copy of the License at - * http://www.opensource.apple.com/apsl/ and read it before using this - * file. - * - * The 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. - * - * @APPLE_LICENSE_HEADER_END@ - */ - -/* - * SecImportExportPkcs8.cpp - support for generating and parsing/decoding - * private keys in PKCS8 format. - * - * The current version (as of March 12 2004) can parse and decode every - * PKCS8 blob generated by openssl with the exception of those using - * double DES encryption. This has been verified by actually generating - * those blobs with openssl and decoding them here. - * - * PLEASE: don't even *think* about changing a single line of code here - * without verifying the results against the full import/export regression - * test in SecurityTests/clxutils/importExport. - * - */ - -#include -#include "SecImportExportPkcs8.h" -#include "SecPkcs8Templates.h" -#include "SecImportExportUtils.h" -#include "SecImportExportCrypto.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SecPkcs8Dbg(args...) secdebug("SecPkcs8", ## args) - -#pragma mark --- PKCS5 v1.5 Key Derivation --- - -/* - * PKCS5 v1.5. Caller has gleaned everything except salt, - * iterationCount, and IV from the AlgId.algorithm OID. - * - * We get salt and iteration count from the incoming alg params. - * IV is derived along with the unwrapping key from the passphrase. - */ -static CSSM_RETURN pkcs5_v15_genKey( - CSSM_CSP_HANDLE cspHand, - SecNssCoder &coder, - const SecKeyImportExportParameters *keyParams, - const CSSM_DATA ¶mData, - CSSM_ALGORITHMS keyAlg, - CSSM_ALGORITHMS pbeHashAlg, - uint32 keySizeInBits, - uint32 blockSizeInBytes, - impExpKeyUnwrapParams *unwrapParams) -{ - CSSM_KEY *passKey = NULL; - CFDataRef cfPhrase = NULL; - CSSM_RETURN crtn; - OSStatus ortn; - CSSM_CRYPTO_DATA seed; - CSSM_CC_HANDLE ccHand = 0; - CSSM_ACCESS_CREDENTIALS creds; - - - /* passphrase or passkey? */ - ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, VP_Import, - (CFTypeRef *)&cfPhrase, &passKey); - if(ortn) { - return ortn; - } - /* subsequent errors to errOut: */ - - 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); - } - - /* hash algorithm --> PBE alg for CSP */ - CSSM_ALGORITHMS pbeAlg; - switch(pbeHashAlg) { - case CSSM_ALGID_MD2: - pbeAlg = CSSM_ALGID_PKCS5_PBKDF1_MD2; - break; - case CSSM_ALGID_MD5: - pbeAlg = CSSM_ALGID_PKCS5_PBKDF1_MD5; - break; - case CSSM_ALGID_SHA1: - pbeAlg = CSSM_ALGID_PKCS5_PBKDF1_SHA1; - break; - default: - /* really shouldn't happen - pbeHashAlg was inferred by - * pkcsOidToParams() */ - SecPkcs8Dbg("PKCS8: PKCS5 v1/5 bogus hash alg"); - crtn = CSSMERR_CSP_INTERNAL_ERROR; - goto errOut; - } - - /* Salt and iteration count from alg parameters */ - impExpPKCS5_PBE_Parameters pbeParams; - memset(&pbeParams, 0, sizeof(pbeParams)); - if(coder.decodeItem(paramData, impExpPKCS5_PBE_ParametersTemplate, &pbeParams)) { - SecPkcs8Dbg("PKCS8: PKCS5 v1.5 pbeParams decode error"); - crtn = errSecUnknownFormat; - goto errOut; - } - uint32 iterCount; - if(!p12DataToInt(pbeParams.iterations, iterCount)) { - SecPkcs8Dbg("PKCS8: bad PKCS5 v1.5 iteration count"); - crtn = errSecUnknownFormat; - goto errOut; - } - - /* ask for hard coded 8 bytes of IV */ - coder.allocItem(unwrapParams->iv, 8); - - memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); - crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand, - pbeAlg, - keyAlg, - keySizeInBits, - &creds, - passKey, // BaseKey - iterCount, - &pbeParams.salt, - &seed, - &ccHand); - if(crtn) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_CSP_CreateDeriveKeyContext failure"); - goto errOut; - } - - memset(unwrapParams->unwrappingKey, 0, sizeof(CSSM_KEY)); - - CSSM_DATA dummyLabel; - dummyLabel.Data = (uint8 *)"temp unwrap key"; - dummyLabel.Length = strlen((char *)dummyLabel.Data); - - crtn = CSSM_DeriveKey(ccHand, - &unwrapParams->iv, // IV returned in in/out Param - 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 - unwrapParams->unwrappingKey); - if(crtn) { - SecPkcs8Dbg("PKCS8: 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 crtn; -} - -#pragma mark --- PKCS5 v2.0 Key Derivation --- - -/* - * PKCS5 v2.0 has different means of encoding algorithm parameters, - * depending on the encryption algorithm. - */ -/* - * Obtain encryption parameters for PKCS5 v2.0, DES and DES3 variants. - */ -static OSStatus pkcs5_DES_params( - const CSSM_DATA ¶mData, // encryptionScheme.parameters - CSSM_OID *encrOid, - impExpKeyUnwrapParams *unwrapParams, - CSSM_ALGORITHMS *keyAlg, // RETURNED - uint32 *keySizeInBits, // IN/OUT (returned if 0 on entry) - SecNssCoder &coder) -{ - /* Params is iv as OCTET STRING */ - if(coder.decodeItem(paramData, kSecAsn1OctetStringTemplate, &unwrapParams->iv)) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 DES init vector decode error"); - return errSecUnknownFormat; - } - if(nssCompareCssmData(encrOid, &CSSMOID_PKCS5_DES_EDE3_CBC)) { - *keyAlg = CSSM_ALGID_3DES_3KEY; - unwrapParams->encrAlg = CSSM_ALGID_3DES_3KEY_EDE; - if(*keySizeInBits == 0) { - *keySizeInBits = 3 * 64; - } - } - else { - *keyAlg = CSSM_ALGID_DES; - unwrapParams->encrAlg = CSSM_ALGID_DES; - if(*keySizeInBits == 0) { - *keySizeInBits = 64; - } - } - unwrapParams->encrPad = CSSM_PADDING_PKCS7; - unwrapParams->encrMode = CSSM_ALGMODE_CBCPadIV8; - return errSecSuccess; -} - -/* - * Obtain encryption parameters for PKCS5 v2.0, RC2 variant. - */ -static OSStatus pkcs5_RC2_params( - const CSSM_DATA ¶mData, // encryptionScheme.parameters - impExpKeyUnwrapParams *unwrapParams, - CSSM_ALGORITHMS *keyAlg, // RETURNED - uint32 *keySizeInBits, // IN/OUT (returned if 0 on entry) - SecNssCoder &coder) -{ - /* Params is impExpPKCS5_RC2Params */ - impExpPKCS5_RC2Params rc2Params; - memset(&rc2Params, 0, sizeof(rc2Params)); - if(coder.decodeItem(paramData, impExpPKCS5_RC2ParamsTemplate, &rc2Params)) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 RC2 params decode error"); - return errSecUnknownFormat; - } - - *keyAlg = CSSM_ALGID_RC2; - unwrapParams->encrAlg = CSSM_ALGID_RC2; - unwrapParams->encrPad = CSSM_PADDING_PKCS7; - unwrapParams->encrMode = CSSM_ALGMODE_CBCPadIV8; - - /* the version actually maps to effective key size like this */ - /* I swear all of this is in the PKCS5 v2.0 spec */ - unwrapParams->effectiveKeySizeInBits = 32; // default - if(rc2Params.version.Data) { - uint32 v; - if(!p12DataToInt(rc2Params.version, v)) { - SecPkcs8Dbg("PKCS8: bad PKCS5 rc2Params.version"); - return errSecUnknownFormat; - } - switch(v) { - case 160: - unwrapParams->effectiveKeySizeInBits = 40; - break; - case 120: - unwrapParams->effectiveKeySizeInBits = 64; - break; - case 58: - unwrapParams->effectiveKeySizeInBits = 128; - break; - default: - if(v >= 256) { - unwrapParams->effectiveKeySizeInBits = v; - } - else { - /* not in the spec, use as zero */ - } - break; - } - } - unwrapParams->iv = rc2Params.iv; - - /* the PKCS5 spec does not give a default for the RC2 key size */ - if(*keySizeInBits == 0) { - SecPkcs8Dbg("PKCS8: NO RC2 DEFAULT KEYSIZE!"); - return errSecUnknownFormat; - } - return errSecSuccess; -} - -/* - * Infer encryption parameters for PKCS5 v2.0, RC5 variant. - * All info contained in encryptionScheme.parameters. - */ -static OSStatus pkcs5_RC5_params( - const CSSM_DATA ¶mData, // encryptionScheme.parameters - impExpKeyUnwrapParams *unwrapParams, - CSSM_ALGORITHMS *keyAlg, // RETURNED - uint32 *keySizeInBits, // IN/OUT (returned if 0 on entry) - SecNssCoder &coder) -{ - /* Params is a impExpPKCS5_RC5Params */ - impExpPKCS5_RC5Params rc5Params; - memset(&rc5Params, 0, sizeof(rc5Params)); - if(coder.decodeItem(paramData, impExpPKCS5_RC5ParamsTemplate, &rc5Params)) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 RC5 params decode error"); - return errSecUnknownFormat; - } - - *keyAlg = CSSM_ALGID_RC5; - unwrapParams->encrAlg = CSSM_ALGID_RC5; - unwrapParams->encrPad = CSSM_PADDING_PKCS7; - unwrapParams->encrMode = CSSM_ALGMODE_CBCPadIV8; - - if(rc5Params.rounds.Data) { - if(!p12DataToInt(rc5Params.rounds, unwrapParams->rounds)) { - SecPkcs8Dbg("PKCS8: bad PKCS5 rc5Params.rounds"); - return errSecUnknownFormat; - } - } - if(rc5Params.blockSizeInBits.Data) { - if(!p12DataToInt(rc5Params.blockSizeInBits, unwrapParams->blockSizeInBits)) { - SecPkcs8Dbg("PKCS8: bad PKCS5 rc5Params.blockSizeInBits"); - return errSecUnknownFormat; - } - } - - /* Spec says default iv is zeroes */ - unwrapParams->iv = rc5Params.iv; - if(unwrapParams->iv.Length == 0) { - uint32 len = unwrapParams->blockSizeInBits / 8; - coder.allocItem(unwrapParams->iv, len); - memset(unwrapParams->iv.Data, 0, len); - } - - /* - * Spec does not give a default for key RC5 size, and openssl doesn't - * support RC5 for PKCS8. - */ - if(*keySizeInBits == 0) { - SecPkcs8Dbg("PKCS8: NO RC5 DEFAULT KEYSIZE!"); - return errSecUnknownFormat; - } - return errSecSuccess; -} - -/* - * Common code to derive a wrap/unwrap key using PBKDF2 (i.e., using PKCS5 v2.0 - * key derivation). Caller must CSSM_FreeKey when done. - */ -static CSSM_RETURN pbkdf2DeriveKey( - CSSM_CSP_HANDLE cspHand, - SecNssCoder &coder, - CSSM_ALGORITHMS keyAlg, - uint32 keySizeInBits, - uint32 iterationCount, - const CSSM_DATA &salt, - const SecKeyImportExportParameters *keyParams, // required - impExpVerifyPhrase verifyPhrase, // for secure passphrase - CSSM_KEY_PTR symKey) // RETURNED -{ - CSSM_KEY *passKey = NULL; - CFDataRef cfPhrase = NULL; - CSSM_PKCS5_PBKDF2_PARAMS pbeParams; - CSSM_RETURN crtn; - OSStatus ortn; - CSSM_DATA dummyLabel; - CSSM_DATA pbeData; - uint32 keyAttr; - CSSM_CC_HANDLE ccHand = 0; - CSSM_ACCESS_CREDENTIALS creds; - - memset(&pbeParams, 0, sizeof(pbeParams)); - - /* passphrase or passkey? */ - ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_Data, verifyPhrase, - (CFTypeRef *)&cfPhrase, &passKey); - if(ortn) { - return ortn; - } - /* subsequent errors to errOut: */ - - if(cfPhrase != NULL) { - size_t len = CFDataGetLength(cfPhrase); - coder.allocItem(pbeParams.Passphrase, len); - memmove(pbeParams.Passphrase.Data, - CFDataGetBytePtr(cfPhrase), len); - CFRelease(cfPhrase); - } - - memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); - crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand, - CSSM_ALGID_PKCS5_PBKDF2, - keyAlg, - keySizeInBits, - &creds, - passKey, // BaseKey - iterationCount, - &salt, - NULL, // seed - &ccHand); - if(crtn) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_CSP_CreateDeriveKeyContext failure"); - goto errOut; - } - - memset(symKey, 0, sizeof(CSSM_KEY)); - - /* 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); - - pbeParams.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1; - pbeData.Data = (uint8 *)&pbeParams; - pbeData.Length = sizeof(pbeParams); - crtn = CSSM_DeriveKey(ccHand, - &pbeData, - CSSM_KEYUSE_ANY, - keyAttr, - &dummyLabel, - NULL, // cred and acl - symKey); - if(crtn) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 CSSM_DeriveKey failure"); - } -errOut: - if(ccHand != 0) { - CSSM_DeleteContext(ccHand); - } - if(passKey != NULL) { - CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE); - free(passKey); - } - return crtn; -} - -/* - * Obtain PKCS5, v.2.0 key derivation and encryption parameters and - * derive the key. This one obtains all of the crypt parameters - * from the top-level AlgId.Params. What a mess. - */ -static CSSM_RETURN pkcs5_v2_genKey( - CSSM_CSP_HANDLE cspHand, - SecNssCoder &coder, - const CSSM_DATA ¶mData, - const SecKeyImportExportParameters *keyParams, - impExpKeyUnwrapParams *unwrapParams) -{ - SecPkcs8Dbg("PKCS8: generating PKCS5 v2.0 key"); - - CSSM_ALGORITHMS keyAlg = CSSM_ALGID_NONE; - uint32 prf = 0; // CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1... - - /* caller should check */ - assert(keyParams != NULL); - - /* AlgId.Params --> impExpPKCS5_PBES2_Params */ - if(paramData.Length == 0) { - SecPkcs8Dbg("PKCS8: empty PKCS5 v2 pbes2Params"); - return errSecUnknownFormat; - } - impExpPKCS5_PBES2_Params pbes2Params; - memset(&pbes2Params, 0, sizeof(pbes2Params)); - if(coder.decodeItem(paramData, impExpPKCS5_PBES2_ParamsTemplate, &pbes2Params)) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 pbes2Params decode error"); - return errSecUnknownFormat; - } - - /* - * As far as I know the keyDerivationFunc OID must be id-PBKDF2 - */ - if(!nssCompareCssmData(&pbes2Params.keyDerivationFunc.algorithm, - &CSSMOID_PKCS5_PBKDF2)) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 unexpected keyDerivationFunc alg"); - return errSecUnknownFormat; - } - - /* - * The params of the keyDerivationFunc algId are an encoded - * impExpPKCS5_PBKDF2_Params. - */ - impExpPKCS5_PBKDF2_Params pbkdf2Params; - memset(&pbkdf2Params, 0, sizeof(pbkdf2Params)); - if(coder.decodeItem(pbes2Params.keyDerivationFunc.parameters, - impExpPKCS5_PBKDF2_ParamsTemplate, &pbkdf2Params)) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 pbkdf2Params decode error"); - return errSecUnknownFormat; - } - - /* - * Salt and iteration count from the impExpPKCS5_PBKDF2_Params (ignoring the - * possible CHOICE for salt source). - */ - CSSM_DATA salt = pbkdf2Params.salt; - uint32 iterCount; - if(!p12DataToInt(pbkdf2Params.iterationCount, iterCount)) { - SecPkcs8Dbg("PKCS8: bad PKCS5 v2 iteration count"); - return errSecUnknownFormat; - } - - /* - * Key size optional, use defaults per alg (later) if it's not there - */ - uint32 keySizeInBits = 0; - if(pbkdf2Params.keyLengthInBytes.Data) { - uint32 keyLengthInBytes; - if(!p12DataToInt(pbkdf2Params.keyLengthInBytes, keyLengthInBytes)) { - SecPkcs8Dbg("PKCS8: bad PKCS5 v2 key size"); - return errSecUnknownFormat; - } - keySizeInBits = keyLengthInBytes * 8; - } - /* else we'll infer key size from the encryption algorithm */ - - /* prf optional, but if it's there it better be CSSMOID_PKCS5_HMAC_SHA1 */ - if(pbkdf2Params.prf.Data) { - if(!nssCompareCssmData(&pbkdf2Params.prf, &CSSMOID_PKCS5_HMAC_SHA1)) { - SecPkcs8Dbg("PKCS8: PKCS5 v2 unexpected prf OID"); - return errSecUnknownFormat; - } - } - prf = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1; - - /* - * Now process the encryptionScheme, which is even messier - the algParams - * varies per encryption algorithm. - */ - CSSM_X509_ALGORITHM_IDENTIFIER &encrScheme = pbes2Params.encryptionScheme; - CSSM_OID *encrOid = &encrScheme.algorithm; - OSStatus ortn; - CSSM_DATA &encrParam = encrScheme.parameters; - - if(nssCompareCssmData(encrOid, &CSSMOID_PKCS5_DES_EDE3_CBC) || - nssCompareCssmData(encrOid, &CSSMOID_DES_CBC)) { - ortn = pkcs5_DES_params(encrParam, encrOid, unwrapParams, &keyAlg, - &keySizeInBits, coder); - if(ortn) { - return ortn; - } - } - else if(nssCompareCssmData(encrOid, &CSSMOID_PKCS5_RC2_CBC)) { - ortn = pkcs5_RC2_params(encrParam, unwrapParams, &keyAlg, - &keySizeInBits, coder); - if(ortn) { - return ortn; - } - } - else if(nssCompareCssmData(encrOid, &CSSMOID_PKCS5_RC5_CBC)) { - ortn = pkcs5_RC5_params(encrParam, unwrapParams, &keyAlg, - &keySizeInBits, coder); - if(ortn) { - return ortn; - } - } - else { - SecPkcs8Dbg("PKCS8: PKCS5 v2 unknown encrScheme.algorithm"); - return errSecUnknownFormat; - } - - /* We should be ready to go */ - assert(keyAlg != CSSM_ALGID_NONE); - assert(unwrapParams->encrAlg != CSSM_ALGID_NONE); - - /* use all the stuff we just figured out to derive a symmetric decryption key */ - return pbkdf2DeriveKey(cspHand, coder, - keyAlg, keySizeInBits, - iterCount, salt, - keyParams, - VP_Import, - unwrapParams->unwrappingKey); -} - -#pragma mark --- PKCS12 Key Derivation --- - -/* - * PKCS12 style key derivation. Caller has gleaned everything except - * salt, iterationCount, and IV from the AlgId.algorithm OID. - * - * We get salt and iteration count from the incoming alg params. - * IV is derived along with the unwrapping key from the passphrase. - */ -static CSSM_RETURN pkcs12_genKey( - CSSM_CSP_HANDLE cspHand, - SecNssCoder &coder, - const SecKeyImportExportParameters *keyParams, - const CSSM_DATA ¶mData, // from algID - CSSM_ALGORITHMS keyAlg, // valid on entry - CSSM_ALGORITHMS pbeHashAlg, // valid on entry - uint32 keySizeInBits, // valid on entry - uint32 blockSizeInBytes, // for IV - impExpKeyUnwrapParams *unwrapParams) -{ - SecPkcs8Dbg("PKCS8: generating PKCS12 key"); - - assert(keyAlg != CSSM_ALGID_NONE); - assert(pbeHashAlg != CSSM_ALGID_NONE); - assert(keySizeInBits != 0); - - /* get iteration count, salt from alg params */ - NSS_P12_PBE_Params pbeParams; - - if(paramData.Length == 0) { - SecPkcs8Dbg("PKCS8: empty P12 pbeParams"); - return errSecUnknownFormat; - } - memset(&pbeParams, 0, sizeof(pbeParams)); - if(coder.decodeItem(paramData, NSS_P12_PBE_ParamsTemplate, &pbeParams)) { - SecPkcs8Dbg("PKCS8: P12 pbeParams decode error"); - return errSecUnknownFormat; - } - - uint32 iterCount = 0; - if(!p12DataToInt(pbeParams.iterations, iterCount)) { - SecPkcs8Dbg("PKCS8: bad P12 iteration count"); - return errSecUnknownFormat; - } - - /* passphrase or passkey? */ - CSSM_KEY *passKey = NULL; - CFStringRef phraseStr = NULL; - CSSM_DATA phraseData = {0, NULL}; - CSSM_DATA *phraseDataP = NULL; - OSStatus ortn; - CSSM_RETURN crtn; - - assert(keyParams != NULL); - - ortn = impExpPassphraseCommon(keyParams, cspHand, SPF_String, VP_Import, - (CFTypeRef *)&phraseStr, &passKey); - if(ortn) { - return ortn; - } - /* subsequent errors to errOut: */ - - if(phraseStr != NULL) { - /* convert to CSSM_DATA for use with p12KeyGen() */ - try { - p12ImportPassPhrase(phraseStr, coder, phraseData); - } - catch(...) { - SecPkcs8Dbg("PKCS8: p12ImportPassPhrase threw"); - crtn = errSecAllocate; - goto errOut; - } - CFRelease(phraseStr); - phraseDataP = &phraseData; - } - - /* use p12 module to cook up the key and IV */ - if(blockSizeInBytes) { - coder.allocItem(unwrapParams->iv, blockSizeInBytes); - } - crtn = p12KeyGen(cspHand, - *unwrapParams->unwrappingKey, - true, // isForEncr - keyAlg, - pbeHashAlg, - keySizeInBits, - iterCount, - pbeParams.salt, - phraseDataP, - passKey, - unwrapParams->iv); - if(crtn) { - SecPkcs8Dbg("PKCS8: p12KeyGen failed"); - } -errOut: - if(passKey != NULL) { - CSSM_FreeKey(cspHand, NULL, passKey, CSSM_FALSE); - free(passKey); - } - return crtn; -} - -#pragma mark --- Public PKCS8 import function --- - -/* - * Called out from SecImportRep::importWrappedKey(). - * If cspHand is provided instead of importKeychain, the CSP - * handle MUST be for the CSPDL, not for the raw CSP. - */ -OSStatus impExpPkcs8Import( - CFDataRef inData, - SecKeychainRef importKeychain, // optional - CSSM_CSP_HANDLE cspHand, // required - SecItemImportExportFlags flags, - const SecKeyImportExportParameters *keyParams, // REQUIRED for unwrap - CFMutableArrayRef outArray) // optional, append here -{ - CSSM_KEY wrappedKey; - CSSM_KEYHEADER &hdr = wrappedKey.KeyHeader; - CSSM_RETURN crtn = CSSM_OK; - - /* key derivation and encryption parameters gleaned from alg ID */ - impExpKeyUnwrapParams unwrapParams; - memset(&unwrapParams, 0, sizeof(unwrapParams)); - CSSM_ALGORITHMS keyAlg = CSSM_ALGID_NONE; - CSSM_ALGORITHMS pbeHashAlg = CSSM_ALGID_NONE; // SHA1 or MD5 - uint32 keySizeInBits; - uint32 blockSizeInBytes; - PKCS_Which pkcs = PW_None; - - if( (keyParams == NULL) || - ( (keyParams->passphrase == NULL) && - !(keyParams->flags & kSecKeySecurePassphrase) ) ) { - /* passphrase mandatory */ - return errSecPassphraseRequired; - } - assert(cspHand != 0); - - /* - * Top-level decode - */ - SecNssCoder coder; - NSS_EncryptedPrivateKeyInfo encrPrivKeyInfo; - - memset(&encrPrivKeyInfo, 0, sizeof(encrPrivKeyInfo)); - if(coder.decode(CFDataGetBytePtr(inData), - CFDataGetLength(inData), - kSecAsn1EncryptedPrivateKeyInfoTemplate, &encrPrivKeyInfo)) { - SecImpExpDbg("impExpPkcs8Import: error decoding top-level encrPrivKeyInfo"); - return errSecUnknownFormat; - } - - /* - * The algorithm OID of that top-level struct is the key piece of info - * for now... - */ - bool found = false; - found = pkcsOidToParams(&encrPrivKeyInfo.algorithm.algorithm, - keyAlg, unwrapParams.encrAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, - unwrapParams.encrPad, unwrapParams.encrMode, pkcs); - if(!found) { - SecImpExpDbg("impExpPkcs8Import: unknown OID in top-level encrPrivKeyInfo"); - return errSecUnknownFormat; - } - - /* - * Each PBE method has its own way of filling in the remaining gaps - * in impExpKeyUnwrapParams and generating a key. - */ - CSSM_KEY unwrappingKey; - memset(&unwrappingKey, 0, sizeof(unwrappingKey)); - unwrapParams.unwrappingKey = &unwrappingKey; - CSSM_DATA ¶mData = encrPrivKeyInfo.algorithm.parameters; - - switch(pkcs) { - case PW_PKCS5_v1_5: - /* we have everything except iv, iterations, salt */ - crtn = pkcs5_v15_genKey(cspHand, coder, keyParams, paramData, - keyAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, - &unwrapParams); - break; - - case PW_PKCS5_v2: - /* obtain everything, including iv, from alg params */ - crtn = pkcs5_v2_genKey(cspHand, coder, paramData, keyParams, &unwrapParams); - break; - case PW_PKCS12: - /* we have everything except iv, iterations, salt */ - crtn = pkcs12_genKey(cspHand, coder, keyParams, paramData, - keyAlg, pbeHashAlg, keySizeInBits, blockSizeInBytes, - &unwrapParams); - break; - case PW_None: - /* satisfy compiler */ - assert(0); - return errSecUnknownFormat; - } - if(crtn) { - SecPkcs8Dbg("PKCS8: key derivation failed"); - return crtn; - } - - /* we should be ready to rock'n'roll no matter how we got here */ - assert(unwrapParams.encrAlg != CSSM_ALGID_NONE); - assert(unwrappingKey.KeyData.Data != NULL); - assert(unwrappingKey.KeyHeader.AlgorithmId != CSSM_ALGID_NONE); - - /* set up key to unwrap */ - 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_PKCS8; - /* AlgorithmId : inferred by CSP */ - hdr.AlgorithmId = CSSM_ALGID_NONE; - hdr.KeyClass = CSSM_KEYCLASS_PRIVATE_KEY; - /* LogicalKeySizeInBits : calculated by CSP during unwrap */ - hdr.KeyAttr = CSSM_KEYATTR_EXTRACTABLE; - hdr.KeyUsage = CSSM_KEYUSE_ANY; - - wrappedKey.KeyData = encrPrivKeyInfo.encryptedData; - - crtn = impExpImportKeyCommon( - &wrappedKey, - importKeychain, - cspHand, - flags, - keyParams, - &unwrapParams, - NULL, // default label - outArray); - CSSM_FreeKey(cspHand, NULL, &unwrappingKey, CSSM_FALSE); - return crtn; -} - -#pragma mark --- Public PKCS8 export function --- - -#define PKCS5_V2_SALT_LEN 8 -#define PKCS5_V2_ITERATIONS 2048 -#define PKCS5_V2_DES_IV_SIZE 8 - -/* - * Unlike impExpPkcs8Import(), which can handle every PBE algorithm in the spec - * and implemented by openssl, this one has a fixed PBE and encryption scheme. - * We do not provide a means at the API for the client to specify these. - * - * We generate blobs with triple DES encryption, with PKCS5 v2.0 key - * derivation. - */ -OSStatus impExpPkcs8Export( - SecKeyRef secKey, - SecItemImportExportFlags flags, - const SecKeyImportExportParameters *keyParams, // optional - CFMutableDataRef outData, // output appended here - const char **pemHeader) -{ - DevRandomGenerator rng; - SecNssCoder coder; - impExpPKCS5_PBES2_Params pbes2Params; - CSSM_X509_ALGORITHM_IDENTIFIER &keyDeriveAlgId = pbes2Params.keyDerivationFunc; - CSSM_ATTRIBUTE_TYPE formatAttrType = CSSM_ATTRIBUTE_NONE; - CSSM_KEYBLOB_FORMAT blobForm = CSSM_KEYBLOB_RAW_FORMAT_NONE; - const CSSM_KEY *cssmKey; - - if(keyParams == NULL) { - return errSecParam; - } - assert(secKey != NULL); - assert(outData != NULL); - - memset(&pbes2Params, 0, sizeof(pbes2Params)); - - /* - * keyDeriveAlgId - * parameters is an encoded impExpPKCS5_PBKDF2_Params - * We generate random salt - */ - keyDeriveAlgId.algorithm = CSSMOID_PKCS5_PBKDF2; - impExpPKCS5_PBKDF2_Params pbkdf2Params; - memset(&pbkdf2Params, 0, sizeof(pbkdf2Params)); - coder.allocItem(pbkdf2Params.salt, PKCS5_V2_SALT_LEN); - rng.random(pbkdf2Params.salt.Data, PKCS5_V2_SALT_LEN); - p12IntToData(PKCS5_V2_ITERATIONS, pbkdf2Params.iterationCount, coder); - /* leave pbkdf2Params.keyLengthInBytes NULL for default */ - /* openssl can't handle this, which is the default value: - pbkdf2Params.prf = CSSMOID_PKCS5_HMAC_SHA1; - */ - - coder.encodeItem(&pbkdf2Params, impExpPKCS5_PBKDF2_ParamsTemplate, - keyDeriveAlgId.parameters); - - /* - * encryptionScheme - * parameters is an encoded OCTET STRING containing the (random) IV - */ - CSSM_X509_ALGORITHM_IDENTIFIER &encrScheme = pbes2Params.encryptionScheme; - encrScheme.algorithm = CSSMOID_PKCS5_DES_EDE3_CBC; - CSSM_DATA rawIv = {0, NULL}; - coder.allocItem(rawIv, PKCS5_V2_DES_IV_SIZE); - rng.random(rawIv.Data, PKCS5_V2_DES_IV_SIZE); - - coder.encodeItem(&rawIv, kSecAsn1OctetStringTemplate, - encrScheme.parameters); - - /* - * Top level NSS_EncryptedPrivateKeyInfo, whose parameters is the encoded - * impExpPKCS5_PBES2_Params. - */ - NSS_EncryptedPrivateKeyInfo encrPrivKeyInfo; - memset(&encrPrivKeyInfo, 0, sizeof(encrPrivKeyInfo)); - CSSM_X509_ALGORITHM_IDENTIFIER &topAlgId = encrPrivKeyInfo.algorithm; - topAlgId.algorithm = CSSMOID_PKCS5_PBES2; - coder.encodeItem(&pbes2Params, impExpPKCS5_PBES2_ParamsTemplate, - topAlgId.parameters); - - /* - * Now all we have to do is generate the encrypted key data itself. - * When doing a WrapKey op in PKCS8 form, the CSP gives us the - * NSS_EncryptedPrivateKeyInfo.encryptedData values. - */ - - /* we need a CSPDL handle - try to get it from the key */ - CSSM_CSP_HANDLE cspdlHand = 0; - OSStatus ortn; - bool releaseCspHand = false; - CSSM_DATA encodedKeyInfo = {0, NULL}; - - 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: */ - - /* get wrapping key from parameters we just set up */ - CSSM_KEY wrappingKey; - memset(&wrappingKey, 0, sizeof(CSSM_KEY)); - CSSM_RETURN crtn = pbkdf2DeriveKey(cspdlHand, coder, - CSSM_ALGID_3DES_3KEY, 3 * 64, - PKCS5_V2_ITERATIONS, pbkdf2Params.salt, - keyParams, - VP_Export, - &wrappingKey); - if(crtn) { - goto errOut; - } - - /* - * Special case for DSA, ECDSA: specify that the raw blob, pre-encrypt, is in - * the PKCS8 PrivateKeyInfo format that openssl understands. The - * default is BSAFE. - */ - crtn = SecKeyGetCSSMKey(secKey, &cssmKey); - if(crtn) { - SecImpExpDbg("impExpPkcs8Export SecKeyGetCSSMKey error"); - goto errOut; - } - switch(cssmKey->KeyHeader.AlgorithmId) { - case CSSM_ALGID_DSA: - case CSSM_ALGID_ECDSA: - formatAttrType = CSSM_ATTRIBUTE_PRIVATE_KEY_FORMAT; - blobForm = CSSM_KEYBLOB_RAW_FORMAT_PKCS8; - break; - default: - break; - } - - /* GO */ - CSSM_KEY wrappedKey; - memset(&wrappedKey, 0, sizeof(CSSM_KEY)); - - crtn = impExpExportKeyCommon(cspdlHand, secKey, &wrappingKey, &wrappedKey, - CSSM_ALGID_3DES_3KEY_EDE, CSSM_ALGMODE_CBCPadIV8, CSSM_PADDING_PKCS7, - CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8, formatAttrType, blobForm, NULL, &rawIv); - if(crtn) { - goto errOut; - } - - /* - * OK... *that* wrapped key's data goes into the top-level - * NSS_EncryptedPrivateKeyInfo, which we then encode; the caller - * gets the result of that encoding. - */ - encrPrivKeyInfo.encryptedData = wrappedKey.KeyData; - coder.encodeItem(&encrPrivKeyInfo, kSecAsn1EncryptedPrivateKeyInfoTemplate, - encodedKeyInfo); - - CFDataAppendBytes(outData, encodedKeyInfo.Data, encodedKeyInfo.Length); - CSSM_FreeKey(cspdlHand, NULL, &wrappedKey, CSSM_FALSE); - - *pemHeader = PEM_STRING_PKCS8; - -errOut: - if(wrappingKey.KeyData.Data) { - CSSM_FreeKey(cspdlHand, NULL, &wrappingKey, CSSM_FALSE); - } - if(releaseCspHand) { - cuCspDetachUnload(cspdlHand, CSSM_FALSE); - } - return crtn; -}