/* * Copyright (c) 2003 Apple Computer, 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 * * Created 2/28/03 by Doug Mitchell. */ #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 = 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(cfStr == NULL) { CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED); } /* convert unicode to chars with an extra double-NULL */ unsigned len = CFStringGetLength(cfStr); tmpCoder.allocItem(pwd, sizeof(UniChar) * (len + 1)); unsigned char *cp = pwd.Data; UniChar uc = 0; for(unsigned 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; } } /* salt from context */ uint32 saltLen = 0; uint8 *salt = NULL; CssmData *csalt = context.get(CSSM_ATTRIBUTE_SALT); if(csalt) { salt = csalt->Data; saltLen = 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, 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, Param.Length); if(crtn) { CssmError::throwMe(crtn); } } }