-/*
- * 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 <Security/SecImportExport.h>
-#include "SecImportExportPkcs8.h"
-#include "SecPkcs8Templates.h"
-#include "SecImportExportUtils.h"
-#include "SecImportExportCrypto.h"
-#include <security_pkcs12/pkcs12Utils.h>
-#include <security_pkcs12/pkcs12Crypto.h>
-#include <security_asn1/SecNssCoder.h>
-#include <Security/keyTemplates.h>
-#include <Security/SecAsn1Templates.h>
-#include <Security/secasn1t.h>
-#include <security_asn1/nssUtils.h>
-#include <security_utilities/debugging.h>
-#include <security_utilities/devrandom.h>
-#include <Security/oidsalg.h>
-#include <Security/SecKeyPriv.h>
-#include <security_cdsa_utils/cuCdsaUtils.h>
-#include <openssl/pem.h>
-#include <assert.h>
-#include <Security/SecBase.h>
-
-#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;
-}