X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/sec/Security/p12import.c?ds=inline diff --git a/sec/Security/p12import.c b/sec/Security/p12import.c deleted file mode 100644 index 500055ab..00000000 --- a/sec/Security/p12import.c +++ /dev/null @@ -1,517 +0,0 @@ -/* - * Copyright (c) 2007-2010 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@ - */ - -#include -#include -#include -#include - -#include -#include -#include - -#include - -#include -#include -#include - -#include "p12pbegen.h" -#include "p12import.h" -#include "SecImportExport.h" - -#ifdef NDEBUG -#define p12DecodeLog(args...) -#else -#define p12DecodeLog(args...) secdebug("pkcs12", "%s\n", args) -#endif - -int decode_item(pkcs12_context * context, const SecAsn1Item *item, - const SecAsn1Template *tmpl, void *dest); -inline int decode_item(pkcs12_context * context, const SecAsn1Item *item, - const SecAsn1Template *tmpl, void *dest) -{ - return SecAsn1Decode(context->coder, (const char *)item->Data, item->Length, tmpl, dest); -} - -void alloc_item(pkcs12_context * context, SecAsn1Item *item, size_t len); -inline void alloc_item(pkcs12_context * context, SecAsn1Item *item, size_t len) -{ - SecAsn1AllocItem(context->coder, item, len); -} - -/* - * OIDS for P12 map to the following attributes. - */ -typedef struct { - CCAlgorithm alg; - uint32_t keySizeInBits; // XXX/cs make keysize in bytes - uint32_t blockSizeInBytes; // for IV, optional, make iv size in bytes - CCOptions options; // padding and mode. -} PKCSOidInfo; - -/* PKCS12 algorithms OID_ISO_MEMBER, OID_US, OID_RSA, OID_PKCS, OID_PKCS_12 */ -static const uint8_t PKCS12_pbep[] = { 42, 134, 72, 134, 247, 13, 1, 12, 1 }; -static const DERItem OID_PKCS12_pbep = { (uint8_t*)PKCS12_pbep, sizeof(PKCS12_pbep) }; -static const PKCSOidInfo pkcsOidInfos[] = { - { /*CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4,*/ - kCCAlgorithmRC4, 128, 0/* stream cipher */, 0 }, - { /*CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4,*/ - kCCAlgorithmRC4, 40, 0/* stream cipher */, 0 }, - { /*CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC,*/ - kCCAlgorithm3DES, 64 * 3, 8, kCCOptionPKCS7Padding }, - { /*CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC,*/ - -1 /*CSSM_ALGID_3DES_2KEY unsupported*/, 64 * 2, 8, kCCOptionPKCS7Padding }, - { /*CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC,*/ - kCCAlgorithmRC2, 128, 8, kCCOptionPKCS7Padding }, - { /*CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC,*/ - kCCAlgorithmRC2, 40, 8, kCCOptionPKCS7Padding } -}; - -#define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1])) - -static int pkcsOidToParams(const SecAsn1Item *oid, CCAlgorithm *alg, - uint32_t *keySizeInBits, uint32_t *blockSizeInBytes, CCOptions *options) -{ - DERItem prefix = { oid->Data, oid->Length }; - prefix.length -= 1; - if (DEROidCompare(&OID_PKCS12_pbep, &prefix)) { - uint8_t postfix = oid->Data[oid->Length-1]; - if (postfix > NUM_PKCS_OID_INFOS || postfix == 4) - return -1; - *alg = pkcsOidInfos[postfix-1].alg; - *keySizeInBits = pkcsOidInfos[postfix-1].keySizeInBits; - *blockSizeInBytes = pkcsOidInfos[postfix-1].blockSizeInBytes; - *options = pkcsOidInfos[postfix-1].options; - return 0; - } - return -1; -} - -static int p12DataToInt(const SecAsn1Item *cdata, uint32_t *u) -{ - /* default/not present */ - if((cdata->Length == 0) || (cdata->Data == NULL)) { - *u = 0; - return 0; - } - size_t len = cdata->Length; - if(len > sizeof(uint32_t)) { - return -1; - } - - uint32_t rtn = 0; - uint8_t *cp = cdata->Data; - size_t i; - for(i = 0; i < len; i++) { - rtn = (rtn << 8) | *cp++; - } - *u = rtn; - return 0; -} - -/* - * Parse an SecAsn1AlgId specific to P12. - * Decode the alg params as a NSS_P12_PBE_Params and parse and - * return the result if the pbeParams is non-NULL. - */ -static int algIdParse(pkcs12_context * context, - const SecAsn1AlgId *algId, NSS_P12_PBE_Params *pbeParams/*optional*/) -{ - p12DecodeLog("algIdParse"); - const SecAsn1Item *param = &algId->parameters; - require(pbeParams, out); - require(param && param->Length, out); - memset(pbeParams, 0, sizeof(*pbeParams)); - require_noerr(decode_item(context, param, NSS_P12_PBE_ParamsTemplate, pbeParams), out); - - return 0; -out: - return -1; -} - -static int p12Decrypt(pkcs12_context * context, const SecAsn1AlgId *algId, - const SecAsn1Item *cipherText, SecAsn1Item *plainText) -{ - NSS_P12_PBE_Params pbep; - // XXX/cs not requiring decoding, but if pbep is uninit this will fail later - algIdParse(context, algId, &pbep); - - CCAlgorithm alg = 0; - uint32_t keySizeInBits = 0; - uint32_t blockSizeInBytes = 0; // for IV, optional - CCOptions options = 0; - require_noerr_quiet(pkcsOidToParams(&algId->algorithm, &alg, &keySizeInBits, - &blockSizeInBytes, &options), out); - - uint32_t iterCount = 0; - require_noerr(p12DataToInt(&pbep.iterations, &iterCount), out); - - /* P12 style key derivation */ - SecAsn1Item key = {0, NULL}; - if(keySizeInBits) - alloc_item(context, &key, (keySizeInBits+7)/8); - require_noerr(p12_pbe_gen(context->passphrase, pbep.salt.Data, pbep.salt.Length, - iterCount, PBE_ID_Key, key.Data, key.Length), out); - - /* P12 style IV derivation, optional */ - SecAsn1Item iv = {0, NULL}; - if(blockSizeInBytes) { - alloc_item(context, &iv, blockSizeInBytes); - require_noerr(p12_pbe_gen(context->passphrase, pbep.salt.Data, pbep.salt.Length, - iterCount, PBE_ID_IV, iv.Data, iv.Length), out); - } - - SecAsn1Item ourPtext = {0, NULL}; - alloc_item(context, &ourPtext, cipherText->Length); - require_noerr(CCCrypt(kCCDecrypt, alg, options/*kCCOptionPKCS7Padding*/, - key.Data, key.Length, iv.Data, cipherText->Data, cipherText->Length, - ourPtext.Data, ourPtext.Length, &ourPtext.Length), out); - *plainText = ourPtext; - - return 0; -out: - return -1; -} - -static int emit_item(pkcs12_context * context, NSS_Attribute **attrs, - CFStringRef item_key, CFTypeRef item_value) -{ - int result = -1; - /* parse attrs into friendlyName, localKeyId; ignoring generic attrs */ - CFMutableDictionaryRef attr_dict = CFDictionaryCreateMutable(kCFAllocatorDefault, - 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); - require(attr_dict, out); - unsigned numAttrs = nssArraySize((const void **)attrs); - unsigned int dex; - for(dex = 0; dex < numAttrs; dex++) { - NSS_Attribute *attr = attrs[dex]; - unsigned numValues = nssArraySize((const void**)attr->attrValue); - DERItem type = { attr->attrType.Data, attr->attrType.Length }; - if(DEROidCompare(&type, &oidFriendlyName)) { - /* - * BMP string (UniCode). Spec says only one legal value. - */ - require(numValues == 1, out); - SecAsn1Item friendly_name_asn1; - require_noerr(decode_item(context, attr->attrValue[0], - kSecAsn1BMPStringTemplate, &friendly_name_asn1), out); - CFStringRef friendly_name = CFStringCreateWithBytes(kCFAllocatorDefault, - friendly_name_asn1.Data, friendly_name_asn1.Length, - kCFStringEncodingUnicode, true); - if (friendly_name) { - CFDictionarySetValue(attr_dict, kSecImportItemLabel, friendly_name); - CFRelease(friendly_name); - } - } - else if(DEROidCompare(&type, &oidLocalKeyId)) { - /* - * Octet string. Spec says only one legal value. - */ - require(numValues == 1, out); - SecAsn1Item local_key_id; - require_noerr(decode_item(context, attr->attrValue[0], - kSecAsn1OctetStringTemplate, &local_key_id), out); - CFDataRef keyid = CFDataCreate(kCFAllocatorDefault, local_key_id.Data, local_key_id.Length); - if (keyid) { - CFDictionarySetValue(attr_dict, kSecImportItemKeyID, keyid); - CFRelease(keyid); - } - } - } - - CFTypeRef key = CFDictionaryGetValue(attr_dict, kSecImportItemKeyID); - if (!key) - key = CFDictionaryGetValue(attr_dict, kSecImportItemLabel); - if (!key) - key = item_value; - - CFMutableDictionaryRef item = (CFMutableDictionaryRef)CFDictionaryGetValue(context->items, key); - if (item) { - CFDictionarySetValue(item, item_key, item_value); - } else { - CFDictionarySetValue(attr_dict, item_key, item_value); - CFDictionarySetValue(context->items, key, attr_dict); - } - result = 0; -out: - CFReleaseSafe(attr_dict); - return result; -} - - -/* - * ShroudedKeyBag parser w/decrypt - */ -static int shroudedKeyBagParse(pkcs12_context * context, const NSS_P12_SafeBag *safeBag) -{ - p12DecodeLog("Found shrouded key bag"); - - const NSS_P12_ShroudedKeyBag *keyBag = safeBag->bagValue.shroudedKeyBag; - SecAsn1Item ptext = {0, NULL}; - require_noerr_quiet(p12Decrypt(context, &keyBag->algorithm, - &keyBag->encryptedData, &ptext), out); - - /* Decode PKCS#8 formatted private key */ - NSS_PrivateKeyInfo pki; - memset(&pki, 0, sizeof(pki)); - require_noerr(decode_item(context, &ptext, kSecAsn1PrivateKeyInfoTemplate, - &pki), out); - - DERItem algorithm = { pki.algorithm.algorithm.Data, pki.algorithm.algorithm.Length }; - CFDataRef algoidData = NULL; - if (DEROidCompare(&oidEcPubKey, &algorithm)) { - algoidData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, oidEcPubKey.data, oidEcPubKey.length, kCFAllocatorNull); - } else if (DEROidCompare(&oidRsa, &algorithm)) { - algoidData = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, oidRsa.data, oidRsa.length, kCFAllocatorNull); - } else { - goto out; - } - require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("algid"), algoidData), out); - CFRelease(algoidData); - - CFDataRef keyData = CFDataCreate(kCFAllocatorDefault, pki.privateKey.Data, pki.privateKey.Length); - require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("key"), keyData), out); - CFRelease(keyData); - - return 0; -out: - return -1; -} - - -/* - * CertBag parser - */ -static int certBagParse(pkcs12_context * context, const NSS_P12_SafeBag *safeBag) -{ - p12DecodeLog("found certBag"); - NSS_P12_CertBag *certBag = safeBag->bagValue.certBag; - - switch(certBag->type) { - case CT_X509: - { - /* certType = CSSM_CERT_X_509v3; - certEncoding = CSSM_CERT_ENCODING_DER; */ - CFDataRef certData = CFDataCreate(kCFAllocatorDefault, certBag->certValue.Data, - certBag->certValue.Length); - require_noerr(emit_item(context, safeBag->bagAttrs, CFSTR("cert"), certData), out); - CFRelease(certData); - break; - } - case CT_SDSI: - /* certType = CSSM_CERT_SDSIv1; */ - /* it's base64 encoded - no value for that in this enum */ - break; - default: - return -1; - } - return 0; -out: - return -1; -} - - -/* - * Parse an encoded NSS_P12_SafeContents. This could be either - * present as plaintext in an AuthSafe or decrypted. - */ -static int safeContentsParse(pkcs12_context * context, const SecAsn1Item *contentsBlob) -{ - p12DecodeLog("safeContentsParse"); - - NSS_P12_SafeContents sc; - memset(&sc, 0, sizeof(sc)); - require_noerr(decode_item(context, contentsBlob, NSS_P12_SafeContentsTemplate, - &sc), out); - - unsigned numBags = nssArraySize((const void **)sc.bags); - unsigned int dex; - for(dex=0; dexbagValue.keyBag != NULL, out); - - /* - * Break out to individual bag type - */ - switch(bag->type) { - case BT_ShroudedKeyBag: - require_noerr(shroudedKeyBagParse(context, bag), out); - break; - case BT_CertBag: - require_noerr(certBagParse(context, bag), out); - break; - - case BT_KeyBag: - /* keyBagParse(bag); */ - p12DecodeLog("Unhandled BT_KeyBag"); - break; - case BT_CrlBag: - /* crlBagParse(bag); */ - p12DecodeLog("Unhandled BT_CrlBag"); - break; - case BT_SecretBag: - /* secretBagParse(bag); */ - p12DecodeLog("Unhandled BT_SecretBag"); - break; - case BT_SafeContentsBag: - /* safeContentsBagParse(bag); */ - p12DecodeLog("Unhandled BT_SafeContentsBag"); - break; - default: - p12DecodeLog("Unknown bag type"); - goto out; - break; - } - } - return 0; -out: - return -1; -} - -/* - * Parse a ContentInfo in the context of (i.e., as an element of) - * an AuthenticatedSafe. - */ -static int authSafeElementParse(pkcs12_context * context, const NSS_P7_DecodedContentInfo *info) -{ - p12DecodeLog("authSafeElementParse"); - switch(info->type) { - case CT_Data: - /* unencrypted SafeContents */ - require_noerr(safeContentsParse(context, info->content.data), out); - break; - - case CT_EncryptedData: - { - /* - * Decrypt contents to get a SafeContents and - * then parse that. - */ - SecAsn1Item ptext = {0, NULL}; - NSS_P7_EncryptedData *edata = info->content.encryptData; - require_noerr_quiet(p12Decrypt(context, &edata->contentInfo.encrAlg, - &edata->contentInfo.encrContent, &ptext), out); - require_noerr(safeContentsParse(context, &ptext), out); - break; - } - default: - break; - } - return 0; -out: - return -1; -} - -/* - * Parse an encoded NSS_P12_AuthenticatedSafe - */ -static int authSafeParse(pkcs12_context * context, const SecAsn1Item *authSafeBlob) -{ - p12DecodeLog("authSafeParse"); - NSS_P12_AuthenticatedSafe authSafe; - memset(&authSafe, 0, sizeof(authSafe)); - require_noerr(decode_item(context, authSafeBlob, - NSS_P12_AuthenticatedSafeTemplate, &authSafe), out); - - unsigned numInfos = nssArraySize((const void **)authSafe.info); - unsigned int dex; - for (dex=0; dexmacData; - require(macData, out); - NSS_P7_DigestInfo *digestInfo = &macData->mac; - require(digestInfo, out); - SecAsn1Item *algOid = &digestInfo->digestAlgorithm.algorithm; - require(algOid, out); - - /* has to be OID_OIW_SHA1 */ - DERItem algOidItem = { algOid->Data, algOid->Length }; - require(algOidItem.length && DEROidCompare(&oidSha1, &algOidItem), out); - - uint32_t iterCount = 0; - require_noerr_quiet(p12DataToInt(&macData->iterations, &iterCount), out); - if (iterCount == 0) { /* optional, default 1 */ - iterCount = 1; - } - - /* - * In classic fashion, the PKCS12 spec now says: - * - * When password integrity mode is used to secure a PFX PDU, - * an SHA-1 HMAC is computed on the BER-encoding of the contents - * of the content field of the authSafe field in the PFX PDU. - * - * So here we go. - */ - uint8_t hmac_key[CC_SHA1_DIGEST_LENGTH]; - require_noerr_quiet(p12_pbe_gen(context->passphrase, - macData->macSalt.Data, macData->macSalt.Length, - iterCount, PBE_ID_MAC, hmac_key, sizeof(hmac_key)), out); - - /* prealloc the mac data */ - SecAsn1Item verifyMac; - alloc_item(context, &verifyMac, CC_SHA1_DIGEST_LENGTH); - SecAsn1Item *ptext = pfx->authSafe.content.data; - CCHmac(kCCHmacAlgSHA1, hmac_key, CC_SHA1_DIGEST_LENGTH, - ptext->Data, ptext->Length, verifyMac.Data); - require_quiet(nssCompareSecAsn1Items(&verifyMac, &digestInfo->digest), out); - - return 0; -out: - return -1; -} - -p12_error p12decode(pkcs12_context * context, CFDataRef cdpfx) -{ - int err = p12_decodeErr; - NSS_P12_DecodedPFX pfx; - memset(&pfx, 0, sizeof(pfx)); - SecAsn1Item raw_blob = { CFDataGetLength(cdpfx), (void*)CFDataGetBytePtr(cdpfx) }; - - require_noerr_quiet(decode_item(context, &raw_blob, NSS_P12_DecodedPFXTemplate, &pfx), out); - NSS_P7_DecodedContentInfo *dci = &pfx.authSafe; - - /* only support CT_Data at top level (password based integrity mode) */ - require(dci->type == CT_Data, out); - require(pfx.macData, out); - - require_noerr_action_quiet(p12VerifyMac(context, &pfx), out, err = p12_passwordErr); - require_noerr_quiet(authSafeParse(context, dci->content.data), out); - - return errSecSuccess; -out: - return err; -}