/* * Copyright (c) 2003-2004 Apple Computer, 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@ */ /* * pkc12Crypto.cpp - PKCS12-specific cyptrographic routines */ #include "pkcs12Crypto.h" #include "pkcs12Utils.h" #include "pkcs12Debug.h" #include #include #include /* * Given appropriate P12-style parameters, cook up a CSSM_KEY. * Caller MUST CSSM_FreeKey() when it's done with the key. */ #define KEY_LABEL "p12 key" CSSM_RETURN p12KeyGen( CSSM_CSP_HANDLE cspHand, CSSM_KEY &key, bool isForEncr, // true: en/decrypt false: MAC CSSM_ALGORITHMS keyAlg, CSSM_ALGORITHMS pbeHashAlg, // actually must be SHA1 for now uint32 keySizeInBits, 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, CSSM_DATA &iv) // referent is optional { CSSM_RETURN crtn; CSSM_CC_HANDLE ccHand; CSSM_DATA dummyLabel; CSSM_ACCESS_CREDENTIALS creds; memset(&key, 0, sizeof(CSSM_KEY)); memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS)); /* infer key derivation algorithm */ CSSM_ALGORITHMS deriveAlg = CSSM_ALGID_NONE; if(pbeHashAlg != CSSM_ALGID_SHA1) { return CSSMERR_CSP_INVALID_ALGORITHM; } if(isForEncr) { /* * FIXME - if this key is going to be used to wrap/unwrap a * shrouded key bag, its usage will change accordingly... */ deriveAlg = CSSM_ALGID_PKCS12_PBE_ENCR; } else { deriveAlg = CSSM_ALGID_PKCS12_PBE_MAC; } CSSM_CRYPTO_DATA seed; if(pwd) { seed.Param = *pwd; } else { seed.Param.Data = NULL; seed.Param.Length = 0; } seed.Callback = NULL; seed.CallerCtx = NULL; crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand, deriveAlg, keyAlg, keySizeInBits, &creds, passKey, // BaseKey iterCount, &salt, &seed, // seed &ccHand); if(crtn) { p12LogCssmError("CSSM_CSP_CreateDeriveKeyContext", crtn); return crtn; } dummyLabel.Length = strlen(KEY_LABEL); dummyLabel.Data = (uint8 *)KEY_LABEL; /* KEYUSE_ANY - this is just an ephemeral session key */ crtn = CSSM_DeriveKey(ccHand, &iv, CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE, &dummyLabel, NULL, // cred and acl &key); CSSM_DeleteContext(ccHand); if(crtn) { p12LogCssmError("CSSM_DeriveKey", crtn); } return crtn; } /* * Decrypt (typically, an encrypted P7 ContentInfo contents) */ CSSM_RETURN p12Decrypt( CSSM_CSP_HANDLE cspHand, const CSSM_DATA &cipherText, 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 plainText CSSM_DATA &plainText) { CSSM_RETURN crtn; CSSM_KEY ckey; CSSM_CC_HANDLE ccHand = 0; CSSM_DATA ourPtext = {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 ptext and rem data */ uint32 bytesDecrypted; crtn = CSSM_DecryptData(ccHand, &cipherText, 1, &ourPtext, 1, &bytesDecrypted, &remData); if(crtn) { cuPrintError("CSSM_DecryptData", crtn); } else { coder.allocCopyItem(ourPtext, plainText); plainText.Length = bytesDecrypted; /* plaintext copied into coder space; free the memory allocated * by the CSP */ freeCssmMemory(cspHand, ourPtext.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; } /* * 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 */ uint32 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; } /* * 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; } /* * 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, /* * 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 = CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE; ResourceControlContext rcc; 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; // Create a Access::Maker for the initial owner of the private key. memset(&rcc, 0, sizeof(rcc)); maker.initialOwner(rcc); crtn = CSSM_UnwrapKey(ccHand, NULL, // PublicKey &wrappedKey, /* * FIXME: I suppose we should infer these from an associated * cert.... */ CSSM_KEYUSE_ANY, reqAttr, &labelData, &rcc, // 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) { try { CssmClient::KeyAclBearer bearer(cspHand, *privKey, Allocator::standard()); SecPointer initialAccess(new KeychainCore::Access("privateKey")); 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; } /* * 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; 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; }