X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/OSX/libsecurity_apple_csp/lib/pkcs12Derive.cpp diff --git a/OSX/libsecurity_apple_csp/lib/pkcs12Derive.cpp b/OSX/libsecurity_apple_csp/lib/pkcs12Derive.cpp new file mode 100644 index 00000000..72ba60a9 --- /dev/null +++ b/OSX/libsecurity_apple_csp/lib/pkcs12Derive.cpp @@ -0,0 +1,445 @@ +/* + * Copyright (c) 2003,2011-2012,2014 Apple Inc. All Rights Reserved. + * + * The contents of this file constitute Original Code as defined in and are + * subject to the Apple Public Source License Version 1.2 (the 'License'). + * You may not use this file except in compliance with the License. Please + * obtain a copy of the License at http://www.apple.com/publicsource and + * read it before using this file. + * + * This 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. + */ +/* + * pkcs12Derive.cpp - PKCS12 PBE routine + * + */ + +#include +#include +#include + +#include "pkcs12Derive.h" +#include "AppleCSPUtils.h" +#include "AppleCSPContext.h" + +#include +#include +#include +#include +#include + +#include + +/* specify which flavor of bits to generate */ +typedef enum { + PBE_ID_Key = 1, + PBE_ID_IV = 2, + PBE_ID_MAC = 3 +} P12_PBE_ID; + +/* + * Create a "string" (in the loose p12 notation) of specified length + * from the concatention of copies of the specified input string. + */ +static unsigned char *p12StrCat( + const unsigned char *inStr, + unsigned inStrLen, + SecNssCoder &coder, + unsigned outLen, + unsigned char *outStr = NULL) // if not present, we malloc +{ + if(outStr == NULL) { + outStr = (unsigned char *)coder.malloc(outLen); + } + unsigned toMove = outLen; + unsigned char *outp = outStr; + while(toMove) { + unsigned thisMove = inStrLen; + if(thisMove > toMove) { + thisMove = toMove; + } + memmove(outp, inStr, thisMove); + toMove -= thisMove; + outp += thisMove; + } + return outStr; +} + +/* + * PBE generator per PKCS12 v.1 section B.2. + */ +static CSSM_RETURN p12PbeGen( + const CSSM_DATA &pwd, // unicode, double null terminated + const uint8 *salt, + unsigned saltLen, + unsigned iterCount, + P12_PBE_ID pbeId, + CSSM_ALGORITHMS hashAlg, // MS5 or SHA1 only + SecNssCoder &coder, // for temp allocs + /* result goes here, mallocd by caller */ + uint8 *outbuf, + unsigned outbufLen) +{ + CSSM_RETURN ourRtn = CSSM_OK; + unsigned unipassLen = (unsigned)pwd.Length; + unsigned char *unipass = pwd.Data; + int irtn; + + /* + * all variables of the form p12_ represent from the + * PKCS12 spec. E.g., p12_u is u, the length of the digest output. + * Only difference here is: all of our sizes are in BYTES, not + * bits. + */ + unsigned p12_r = iterCount; + unsigned p12_n = outbufLen; + + unsigned p12_u; // hash output size + unsigned p12_v; // hash block size + unsigned char *p12_P = NULL; // catted passwords + unsigned char *p12_S = NULL; // catted salts + + switch(hashAlg) { + case CSSM_ALGID_MD5: + p12_u = kMD5DigestSize; + p12_v = kMD5BlockSize; + break; + case CSSM_ALGID_SHA1: + p12_u = kSHA1DigestSize; + p12_v = kSHA1BlockSize; + break; + default: + return CSSMERR_CSP_INVALID_ALGORITHM; + } + + /* + * 1. Construct a string, D (the diversifier), by + * concatenating v/8 copies of ID. + */ + unsigned char *p12_D = NULL; // diversifier + p12_D = (unsigned char *)coder.malloc(p12_v); + for(unsigned dex=0; dex p12_v) { + BN_bn2bin (Ij, p12_B); + memcpy (p12_I + j, p12_B + 1, p12_v); + /* If less than v bytes pad with zeroes */ + } else if (Ijlen < p12_v) { + memset(p12_I + j, 0, p12_v - Ijlen); + BN_bn2bin(Ij, p12_I + j + p12_v - Ijlen); + } else BN_bn2bin (Ij, p12_I + j); + } + } + + if(ourRtn == CSSM_OK) { + /* + * 7. Concatenate A[1], A[2], ..., A[c] together to form a + * pseudo-random bit string, A. + * + * 8. Use the first n bits of A as the output of this entire + * process. + */ + memmove(outbuf, p12_A, outbufLen); + } + + /* clear all these strings */ + if(p12_D) { + memset(p12_D, 0, p12_v); + } + if(p12_S) { + memset(p12_S, 0, p12_Slen); + } + if(p12_P) { + memset(p12_P, 0, p12_Plen); + } + if(p12_I) { + memset(p12_I, 0, p12_Slen + p12_Plen); + } + if(p12_A) { + memset(p12_A, 0, p12_c * p12_u); + } + if(p12_B) { + memset(p12_B, 0, p12_v); + } + if(hashHand) { + DigestCtxFree(hashHand); + } + BN_free(Bpl1); + BN_free(Ij); + return ourRtn; +} + +/* + * Public P12 derive key function, called out from + * AppleCSPSession::DeriveKey() + * + * On input: + * --------- + * Context parameters: + * Salt + * Iteration Count + * CSSM_CRYPTO_DATA.Param - Unicode passphrase, double-NULL terminated + * Algorithm - CSSM_ALGID_PKCS12_PBE_{ENCR,MAC} + * Passed explicitly from DeriveKey(): + * CSSM_DATA Param - IN/OUT - optional IV - caller mallocs space to + * tell us to generate an IV. The param itself is not + * optional; the presence or absence of allocated data in it + * is our IV indicator (present/absent as well as size) + * KeyData - mallocd by caller, we fill in keyData->Length bytes + */ +void DeriveKey_PKCS12 ( + const Context &context, + AppleCSPSession &session, + const CssmData &Param, // other's public key + CSSM_DATA *keyData) // mallocd by caller + // we fill in keyData->Length bytes +{ + SecNssCoder tmpCoder; + + /* + * According to the spec, both passphrase and salt are optional. + * Get them from context if they're present. In practical terms + * the user really should supply a passphrase either in the + * seed attribute (as a Unicode passphrase) or as the BaseKey + * as a CSSM_ALGID_SECURE_PASSPHRASE key). + */ + CSSM_DATA pwd = {0, NULL}; + CSSM_DATA appPwd = {0, NULL}; + CssmCryptoData *cryptData = + context.get(CSSM_ATTRIBUTE_SEED); + if((cryptData != NULL) && (cryptData->Param.Length != 0)) { + appPwd = cryptData->Param; + } + else { + /* Get pwd from base key */ + CssmKey *passKey = context.get(CSSM_ATTRIBUTE_KEY); + if (passKey != NULL) { + AppleCSPContext::symmetricKeyBits(context, session, + CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE, + appPwd.Data, appPwd.Length); + } + } + if(appPwd.Data) { + /* + * The incoming passphrase is a UTF8 encoded enternal representation + * of a CFString. Convert to CFString and obtain the unicode characters + * from the string. + */ + CFDataRef cfData = CFDataCreate(NULL, appPwd.Data, appPwd.Length); + CFStringRef cfStr = CFStringCreateFromExternalRepresentation(NULL, + cfData, kCFStringEncodingUTF8); + if (cfData) + CFRelease(cfData); + if(cfStr == NULL) { + CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED); + } + + /* convert unicode to chars with an extra double-NULL */ + CFIndex len = CFStringGetLength(cfStr); + tmpCoder.allocItem(pwd, sizeof(UniChar) * (len + 1)); + unsigned char *cp = pwd.Data; + UniChar uc = 0; + for(CFIndex dex=0; dex> 8; + *cp++ = uc & 0xff; + } + /* CFString tends to include a NULL at the end; add it if it's not there */ + if(uc == 0) { + if(pwd.Length < 2) { + CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED); + } + pwd.Length -= 2; + } + else { + *cp++ = 0; + *cp++ = 0; + } + if (cfStr) + CFRelease(cfStr); + } + + /* salt from context */ + uint32 saltLen = 0; + uint8 *salt = NULL; + CssmData *csalt = context.get(CSSM_ATTRIBUTE_SALT); + if(csalt) { + salt = csalt->Data; + saltLen = (uint32)csalt->Length; + } + + /* + * Iteration count, from context, required. + * The spec's ASN1 definition says this is optional with a default + * of one but that's a BER encode/decode issue. Here we require + * a nonzero value. + */ + uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT, + CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT); + if(iterCount == 0) { + CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT); + } + + /* + * Algorithm determines which of {PBE_ID_Key,PBE_ID_MAC} we now + * generate. We'll also do an optional PBE_ID_IV later. + */ + P12_PBE_ID pbeId = PBE_ID_Key; + switch(context.algorithm()) { + case CSSM_ALGID_PKCS12_PBE_ENCR: + pbeId = PBE_ID_Key; + break; + case CSSM_ALGID_PKCS12_PBE_MAC: + pbeId = PBE_ID_MAC; + break; + default: + /* really should not be here */ + assert(0); + CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR); + } + + /* Go */ + CSSM_RETURN crtn = p12PbeGen(pwd, + salt, saltLen, + iterCount, + pbeId, + CSSM_ALGID_SHA1, // all we support for now + tmpCoder, + keyData->Data, + (unsigned)keyData->Length); + if(crtn) { + CssmError::throwMe(crtn); + } + + /* + * Optional IV - makes no sense if we just did PBE_ID_MAC, but why + * bother restricting? + */ + if(Param.Data) { + crtn = p12PbeGen(pwd, + salt, saltLen, + iterCount, + PBE_ID_IV, + CSSM_ALGID_SHA1, // all we support for now + tmpCoder, + Param.Data, + (unsigned)Param.Length); + if(crtn) { + CssmError::throwMe(crtn); + } + } +} +