2  * Copyright (c) 2003,2011-2012,2014 Apple Inc. All Rights Reserved. 
   4  * The contents of this file constitute Original Code as defined in and are 
   5  * subject to the Apple Public Source License Version 1.2 (the 'License'). 
   6  * You may not use this file except in compliance with the License. Please  
   7  * obtain a copy of the License at http://www.apple.com/publicsource and  
   8  * read it before using this file. 
  10  * This Original Code and all software distributed under the License are 
  11  * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER  
  12  * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,  
  13  * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,  
  14  * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.  
  15  * Please see the License for the specific language governing rights and  
  16  * limitations under the License. 
  19  * pkcs12Derive.cpp - PKCS12 PBE routine 
  23 #include <Security/cssmapple.h> 
  24 #include <openssl/bn_legacy.h> 
  25 #include "pbkdDigest.h" 
  27 #include "pkcs12Derive.h" 
  28 #include "AppleCSPUtils.h" 
  29 #include "AppleCSPContext.h" 
  34 #include <security_utilities/simulatecrash_assert.h> 
  35 #include <security_asn1/SecNssCoder.h> 
  37 #include <CoreFoundation/CoreFoundation.h> 
  39 /* specify which flavor of bits to generate */ 
  47  * Create a "string" (in the loose p12 notation) of specified length  
  48  * from the concatention of copies of the specified input string. 
  50 static unsigned char *p12StrCat( 
  51         const unsigned char *inStr
, 
  55         unsigned char *outStr 
= NULL
)   // if not present, we malloc 
  58                 outStr 
= (unsigned char *)coder
.malloc(outLen
); 
  60         unsigned toMove 
= outLen
; 
  61         unsigned char *outp 
= outStr
; 
  63                 unsigned thisMove 
= inStrLen
; 
  64                 if(thisMove 
> toMove
) { 
  67                 memmove(outp
, inStr
, thisMove
); 
  75  * PBE generator per PKCS12 v.1 section B.2. 
  77 static CSSM_RETURN 
p12PbeGen( 
  78         const CSSM_DATA         
&pwd
,                   // unicode, double null terminated 
  83         CSSM_ALGORITHMS         hashAlg
,                // MS5 or SHA1 only 
  84         SecNssCoder                     
&coder
,                 // for temp allocs 
  85         /* result goes here, mallocd by caller */ 
  89         CSSM_RETURN ourRtn 
= CSSM_OK
; 
  90         unsigned unipassLen 
= (unsigned)pwd
.Length
; 
  91         unsigned char *unipass 
= pwd
.Data
; 
  95          * all variables of the form p12_<XXX> represent <XXX> from the  
  96          * PKCS12 spec. E.g., p12_u is u, the length of the digest output. 
  97          * Only difference here is: all of our sizes are in BYTES, not 
 100         unsigned p12_r 
= iterCount
; 
 101         unsigned p12_n 
= outbufLen
; 
 103         unsigned p12_u
;                                 // hash output size 
 104         unsigned p12_v
;                                 // hash block size 
 105         unsigned char *p12_P 
= NULL
;    // catted passwords 
 106         unsigned char *p12_S 
= NULL
;    // catted salts 
 110                         p12_u 
= kMD5DigestSize
; 
 111                         p12_v 
= kMD5BlockSize
; 
 113                 case CSSM_ALGID_SHA1
: 
 114                         p12_u 
= kSHA1DigestSize
; 
 115                         p12_v 
= kSHA1BlockSize
; 
 118                         return CSSMERR_CSP_INVALID_ALGORITHM
; 
 122          * 1. Construct a string, D (the diversifier), by  
 123          *    concatenating v/8 copies of ID. 
 125         unsigned char *p12_D 
= NULL
;            // diversifier 
 126         p12_D 
= (unsigned char *)coder
.malloc(p12_v
); 
 127         for(unsigned dex
=0; dex
<p12_v
; dex
++) { 
 128                 p12_D
[dex
] = (unsigned char)pbeId
; 
 132          * 2. Concatenate copies of the salt together to create  
 133          *    a string S of length v * ceil(s/v) bits (the final copy  
 134          *    of the salt may be truncated to create S). Note that if  
 135          *    the salt is the empty string, then so is S. 
 137         unsigned p12_Slen 
= p12_v 
* ((saltLen 
+ p12_v 
- 1) / p12_v
); 
 139                 p12_S 
= p12StrCat(salt
, saltLen
, coder
, p12_Slen
); 
 144          * 3. Concatenate copies of the password together to create  
 145          *    a string P of length v * ceil(p/v) bits (the final copy of  
 146          *    the password may be truncated to create P). Note that  
 147          *    if the password is the empty string, then so is P. 
 149         unsigned p12_Plen 
= p12_v 
* ((unipassLen 
+ p12_v 
- 1) / p12_v
); 
 151                 p12_P 
= p12StrCat(unipass
, unipassLen
, coder
, p12_Plen
); 
 155          * 4. Set I= S||P to be the concatenation of S and P. 
 157         unsigned char *p12_I 
=  
 158                 (unsigned char *)coder
.malloc(p12_Slen 
+ p12_Plen
); 
 159         memmove(p12_I
, p12_S
, p12_Slen
); 
 161                 memmove(p12_I 
+ p12_Slen
, p12_P
, p12_Plen
); 
 165          * 5. Set c = ceil(n/u).  
 167         unsigned p12_c 
= (p12_n 
+ p12_u 
- 1) / p12_u
; 
 169         /* allocate c hash-output-size bufs */ 
 170         unsigned char *p12_A 
= (unsigned char *)coder
.malloc(p12_c 
* p12_u
); 
 172         /* one reusable hash object */ 
 174         DigestCtx 
*hashHand 
= &ourDigest
; 
 176         /* reused inside the loop */ 
 177         unsigned char *p12_B 
= (unsigned char *)coder
.malloc(p12_v 
+ 1); 
 178         BIGNUM 
*Ij 
= BN_new(); 
 179         BIGNUM 
*Bpl1 
= BN_new(); 
 182          * 6. For i=1, 2, ..., p12_c, do the following: 
 184         for(unsigned p12_i
=0; p12_i
<p12_c
; p12_i
++) { 
 185                 unsigned char *p12_AsubI 
= p12_A 
+ (p12_i 
* p12_u
); 
 188                  * a) Set A[i] = H**r(D||I). (i.e. the rth hash of D||I,  
 189                  *    H(H(H(...H(D||I)))) 
 191                 irtn 
= DigestCtxInit(hashHand
, hashAlg
);  
 193                         ourRtn 
= CSSMERR_CSP_INTERNAL_ERROR
; 
 196                 DigestCtxUpdate(hashHand
, p12_D
, p12_v
);  
 197                 DigestCtxUpdate(hashHand
, p12_I
, p12_Slen 
+ p12_Plen
); 
 198                 DigestCtxFinal(hashHand
, p12_AsubI
);  
 200                 for(unsigned iter
=1; iter
<p12_r
; iter
++) { 
 201                         irtn 
= DigestCtxInit(hashHand
, hashAlg
); 
 203                                 ourRtn 
= CSSMERR_CSP_INTERNAL_ERROR
; 
 206                         DigestCtxUpdate(hashHand
, p12_AsubI
, p12_u
); 
 207                         DigestCtxFinal(hashHand
, p12_AsubI
); 
 211                  * b) Concatenate copies of A[i] to create a string B of  
 212                  *    length v bits (the final copy of A[i]i may be truncated  
 215                 p12StrCat(p12_AsubI
, p12_u
, coder
, p12_v
, p12_B
); 
 218                  * c) Treating I as a concatenation I[0], I[1], ...,  
 219                  *    I[k-1] of v-bit blocks, where k = ceil(s/v) + ceil(p/v), 
 220                  *    modify I by setting I[j]=(I[j]+B+1) mod (2 ** v) 
 223                  * Copied from PKCS12_key_gen_uni() from openssl... 
 225                 /* Work out B + 1 first then can use B as tmp space */ 
 226                 BN_bin2bn (p12_B
, p12_v
, Bpl1
); 
 227                 BN_add_word (Bpl1
, 1); 
 228                 unsigned Ilen 
= p12_Slen 
+ p12_Plen
; 
 230                 for (unsigned j 
= 0; j 
< Ilen
; j
+=p12_v
) { 
 231                         BN_bin2bn (p12_I 
+ j
, p12_v
, Ij
); 
 232                         BN_add (Ij
, Ij
, Bpl1
); 
 233                         BN_bn2bin (Ij
, p12_B
); 
 234                         unsigned Ijlen 
= BN_num_bytes (Ij
); 
 235                         /* If more than 2^(v*8) - 1 cut off MSB */ 
 237                                 BN_bn2bin (Ij
, p12_B
); 
 238                                 memcpy (p12_I 
+ j
, p12_B 
+ 1, p12_v
); 
 239                         /* If less than v bytes pad with zeroes */ 
 240                         } else if (Ijlen 
< p12_v
) { 
 241                                 memset(p12_I 
+ j
, 0, p12_v 
- Ijlen
); 
 242                                 BN_bn2bin(Ij
, p12_I 
+ j 
+ p12_v 
- Ijlen
);  
 243                         } else BN_bn2bin (Ij
, p12_I 
+ j
); 
 247         if(ourRtn 
== CSSM_OK
) {  
 249                  * 7. Concatenate A[1], A[2], ..., A[c] together to form a  
 250                  *    pseudo-random bit string, A. 
 252                  * 8. Use the first n bits of A as the output of this entire  
 255                 memmove(outbuf
, p12_A
, outbufLen
); 
 258         /* clear all these strings */ 
 260                 memset(p12_D
, 0, p12_v
); 
 263                 memset(p12_S
, 0, p12_Slen
); 
 266                 memset(p12_P
, 0, p12_Plen
); 
 269                 memset(p12_I
, 0, p12_Slen 
+ p12_Plen
); 
 272                 memset(p12_A
, 0, p12_c 
* p12_u
); 
 275                 memset(p12_B
, 0, p12_v
); 
 278                 DigestCtxFree(hashHand
); 
 286  * Public P12 derive key function, called out from  
 287  * AppleCSPSession::DeriveKey() 
 291  * Context parameters: 
 294  *              CSSM_CRYPTO_DATA.Param - Unicode passphrase, double-NULL terminated 
 295  *              Algorithm - CSSM_ALGID_PKCS12_PBE_{ENCR,MAC} 
 296  * Passed explicitly from DeriveKey(): 
 297  *              CSSM_DATA Param - IN/OUT - optional IV - caller mallocs space to 
 298  *                      tell us to generate an IV. The param itself is not  
 299  *                      optional; the presence or absence of allocated data in it 
 300  *                      is our IV indicator (present/absent as well as size) 
 301  *              KeyData - mallocd by caller, we fill in keyData->Length bytes 
 303 void DeriveKey_PKCS12 ( 
 304         const Context 
&context
, 
 305         AppleCSPSession 
&session
, 
 306         const CssmData 
&Param
,                  // other's public key 
 307         CSSM_DATA 
*keyData
)                             // mallocd by caller 
 308                                                                         // we fill in keyData->Length bytes 
 310         SecNssCoder tmpCoder
; 
 313          * According to the spec, both passphrase and salt are optional. 
 314          * Get them from context if they're present. In practical terms 
 315          * the user really should supply a passphrase either in the  
 316          * seed attribute (as a Unicode passphrase) or as the BaseKey 
 317          * as a CSSM_ALGID_SECURE_PASSPHRASE key).  
 319         CSSM_DATA pwd 
= {0, NULL
}; 
 320         CSSM_DATA appPwd 
= {0, NULL
}; 
 321         CssmCryptoData 
*cryptData 
=  
 322                 context
.get
<CssmCryptoData
>(CSSM_ATTRIBUTE_SEED
); 
 323         if((cryptData 
!= NULL
) && (cryptData
->Param
.Length 
!= 0)) { 
 324                 appPwd 
= cryptData
->Param
; 
 327                 /* Get pwd from base key */ 
 328                 CssmKey 
*passKey 
= context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
); 
 329                 if (passKey 
!= NULL
) { 
 330                         AppleCSPContext::symmetricKeyBits(context
, session
, 
 331                                 CSSM_ALGID_SECURE_PASSPHRASE
, CSSM_KEYUSE_DERIVE
,  
 332                                 appPwd
.Data
, appPwd
.Length
); 
 337                  * The incoming passphrase is a UTF8 encoded enternal representation 
 338                  * of a CFString. Convert to CFString and obtain the unicode characters 
 341                 CFDataRef cfData 
= CFDataCreate(NULL
, appPwd
.Data
, appPwd
.Length
); 
 342                 CFStringRef cfStr 
= CFStringCreateFromExternalRepresentation(NULL
, 
 343                         cfData
, kCFStringEncodingUTF8
); 
 347                         CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED
); 
 350                 /* convert unicode to chars with an extra double-NULL */ 
 351                 CFIndex len 
= CFStringGetLength(cfStr
); 
 352                 tmpCoder
.allocItem(pwd
, sizeof(UniChar
) * (len 
+ 1)); 
 353                 unsigned char *cp 
= pwd
.Data
; 
 355                 for(CFIndex dex
=0; dex
<len
; dex
++) { 
 356                         uc 
= CFStringGetCharacterAtIndex(cfStr
, dex
); 
 360                 /* CFString tends to include a NULL at the end; add it if it's not there */ 
 363                                 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED
); 
 375         /* salt from context */ 
 378         CssmData 
*csalt 
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_SALT
); 
 381                 saltLen 
= (uint32
)csalt
->Length
; 
 385          * Iteration count, from context, required. 
 386          * The spec's ASN1 definition says this is optional with a default 
 387          * of one but that's a BER encode/decode issue. Here we require 
 390         uint32 iterCount 
= context
.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT
, 
 391                 CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT
); 
 393                 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT
); 
 397          * Algorithm determines which of {PBE_ID_Key,PBE_ID_MAC} we now 
 398          * generate. We'll also do an optional PBE_ID_IV later. 
 400         P12_PBE_ID pbeId 
= PBE_ID_Key
; 
 401         switch(context
.algorithm()) { 
 402                 case CSSM_ALGID_PKCS12_PBE_ENCR
: 
 405                 case CSSM_ALGID_PKCS12_PBE_MAC
: 
 409                         /* really should not be here */ 
 411                         CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
); 
 415         CSSM_RETURN crtn 
= p12PbeGen(pwd
, 
 419                 CSSM_ALGID_SHA1
,                        // all we support for now 
 422                 (unsigned)keyData
->Length
); 
 424                 CssmError::throwMe(crtn
); 
 428          * Optional IV - makes no sense if we just did PBE_ID_MAC, but why 
 429          * bother restricting? 
 432                 crtn 
= p12PbeGen(pwd
, 
 436                         CSSM_ALGID_SHA1
,                        // all we support for now 
 439                         (unsigned)Param
.Length
); 
 441                         CssmError::throwMe(crtn
);