+++ /dev/null
-/*
- * 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 <Security/cssmapple.h>
-#include <openssl/bn.h>
-#include <pbkdDigest.h>
-
-#include "pkcs12Derive.h"
-#include "AppleCSPUtils.h"
-#include "AppleCSPContext.h"
-
-#include <string.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <assert.h>
-#include <security_asn1/SecNssCoder.h>
-
-#include <CoreFoundation/CoreFoundation.h>
-
-/* 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_<XXX> represent <XXX> 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; dex++) {
- p12_D[dex] = (unsigned char)pbeId;
- }
-
- /*
- * 2. Concatenate copies of the salt together to create
- * a string S of length v * ceil(s/v) bits (the final copy
- * of the salt may be truncated to create S). Note that if
- * the salt is the empty string, then so is S.
- */
- unsigned p12_Slen = p12_v * ((saltLen + p12_v - 1) / p12_v);
- if(p12_Slen) {
- p12_S = p12StrCat(salt, saltLen, coder, p12_Slen);
- }
-
-
- /*
- * 3. Concatenate copies of the password together to create
- * a string P of length v * ceil(p/v) bits (the final copy of
- * the password may be truncated to create P). Note that
- * if the password is the empty string, then so is P.
- */
- unsigned p12_Plen = p12_v * ((unipassLen + p12_v - 1) / p12_v);
- if(p12_Plen) {
- p12_P = p12StrCat(unipass, unipassLen, coder, p12_Plen);
- }
-
- /*
- * 4. Set I= S||P to be the concatenation of S and P.
- */
- unsigned char *p12_I =
- (unsigned char *)coder.malloc(p12_Slen + p12_Plen);
- memmove(p12_I, p12_S, p12_Slen);
- if(p12_Plen) {
- memmove(p12_I + p12_Slen, p12_P, p12_Plen);
- }
-
- /*
- * 5. Set c = ceil(n/u).
- */
- unsigned p12_c = (p12_n + p12_u - 1) / p12_u;
-
- /* allocate c hash-output-size bufs */
- unsigned char *p12_A = (unsigned char *)coder.malloc(p12_c * p12_u);
-
- /* one reusable hash object */
- DigestCtx ourDigest;
- DigestCtx *hashHand = &ourDigest;
-
- /* reused inside the loop */
- unsigned char *p12_B = (unsigned char *)coder.malloc(p12_v + 1);
- BIGNUM *Ij = BN_new();
- BIGNUM *Bpl1 = BN_new();
-
- /*
- * 6. For i=1, 2, ..., p12_c, do the following:
- */
- for(unsigned p12_i=0; p12_i<p12_c; p12_i++) {
- unsigned char *p12_AsubI = p12_A + (p12_i * p12_u);
-
- /*
- * a) Set A[i] = H**r(D||I). (i.e. the rth hash of D||I,
- * H(H(H(...H(D||I))))
- */
- irtn = DigestCtxInit(hashHand, hashAlg);
- if(!irtn) {
- ourRtn = CSSMERR_CSP_INTERNAL_ERROR;
- break;
- }
- DigestCtxUpdate(hashHand, p12_D, p12_v);
- DigestCtxUpdate(hashHand, p12_I, p12_Slen + p12_Plen);
- DigestCtxFinal(hashHand, p12_AsubI);
-
- for(unsigned iter=1; iter<p12_r; iter++) {
- irtn = DigestCtxInit(hashHand, hashAlg);
- if(!irtn) {
- ourRtn = CSSMERR_CSP_INTERNAL_ERROR;
- break;
- }
- DigestCtxUpdate(hashHand, p12_AsubI, p12_u);
- DigestCtxFinal(hashHand, p12_AsubI);
- }
-
- /*
- * b) Concatenate copies of A[i] to create a string B of
- * length v bits (the final copy of A[i]i may be truncated
- * to create B).
- */
- p12StrCat(p12_AsubI, p12_u, coder, p12_v, p12_B);
-
- /*
- * c) Treating I as a concatenation I[0], I[1], ...,
- * I[k-1] of v-bit blocks, where k = ceil(s/v) + ceil(p/v),
- * modify I by setting I[j]=(I[j]+B+1) mod (2 ** v)
- * for each j.
- *
- * Copied from PKCS12_key_gen_uni() from openssl...
- */
- /* Work out B + 1 first then can use B as tmp space */
- BN_bin2bn (p12_B, p12_v, Bpl1);
- BN_add_word (Bpl1, 1);
- unsigned Ilen = p12_Slen + p12_Plen;
-
- for (unsigned j = 0; j < Ilen; j+=p12_v) {
- BN_bin2bn (p12_I + j, p12_v, Ij);
- BN_add (Ij, Ij, Bpl1);
- BN_bn2bin (Ij, p12_B);
- unsigned Ijlen = BN_num_bytes (Ij);
- /* If more than 2^(v*8) - 1 cut off MSB */
- if (Ijlen > 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<CssmCryptoData>(CSSM_ATTRIBUTE_SEED);
- if((cryptData != NULL) && (cryptData->Param.Length != 0)) {
- appPwd = cryptData->Param;
- }
- else {
- /* Get pwd from base key */
- CssmKey *passKey = context.get<CssmKey>(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<len; dex++) {
- uc = CFStringGetCharacterAtIndex(cfStr, dex);
- *cp++ = uc >> 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<CssmData>(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);
- }
- }
-}
-