2 * Copyright (c) 2000-2001 Apple Computer, 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 obtain
7 * a copy of the License at http://www.apple.com/publicsource and read it before
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 EXPRESS
12 * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT
13 * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
14 * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the
15 * specific language governing rights and limitations under the License.
22 Contains: CSSM_DeriveKey functions
24 Copyright: (C) 2000 by Apple Computer, Inc., all rights reserved
26 Written by: Doug Mitchell <dmitch@apple.com>
31 #include <pbkdDigest.h>
32 #include <pkcs12Derive.h>
33 #include "AppleCSPSession.h"
34 #include "AppleCSPUtils.h"
35 #include "AppleCSPContext.h"
36 #include "cspdebugging.h"
37 #include <security_cdsa_utilities/context.h>
38 #include <DH_exchange.h>
39 #include "FEEAsymmetricContext.h"
41 /* minimum legal values */
42 #define PBKDF2_MIN_SALT 8 /* bytes */
43 #define PBKDF2_MIN_ITER_CNT 1000 /* iteration count */
45 #define ALLOW_ZERO_PASSWORD 1
47 void AppleCSPSession::DeriveKey_PBKDF2(
48 const Context
&context
,
49 const CssmData
&Param
,
52 /* validate algorithm-specific arguments */
54 /* Param must point to a CSSM_PKCS5_PBKDF2_PARAMS */
55 if(Param
.Length
!= sizeof(CSSM_PKCS5_PBKDF2_PARAMS
)) {
56 errorLog0("DeriveKey_PBKDF2: Param wrong size\n");
57 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER
);
59 const CSSM_PKCS5_PBKDF2_PARAMS
*pbkdf2Params
=
60 reinterpret_cast<const CSSM_PKCS5_PBKDF2_PARAMS
*>(Param
.Data
);
61 if(pbkdf2Params
== NULL
) {
62 errorLog0("DeriveKey_PBKDF2: null Param.Data\n");
63 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA
);
66 /* Get passphrase from either baseKey or from CSSM_PKCS5_PBKDF2_PARAMS */
67 CssmKey
*passKey
= context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
);
68 CSSM_SIZE passphraseLen
= 0;
69 uint8
*passphrase
= NULL
;
71 AppleCSPContext::symmetricKeyBits(context
, *this,
72 CSSM_ALGID_SECURE_PASSPHRASE
, CSSM_KEYUSE_DERIVE
,
73 passphrase
, passphraseLen
);
76 passphraseLen
= pbkdf2Params
->Passphrase
.Length
;
77 passphrase
= pbkdf2Params
->Passphrase
.Data
;
80 #if !ALLOW_ZERO_PASSWORD
81 /* passphrase required */
82 if(passphrase
== NULL
) {
83 errorLog0("DeriveKey_PBKDF2: null Passphrase\n");
84 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA
);
86 if(passphraseLen
== 0) {
87 /* FIXME - enforce minimum length? */
88 errorLog0("DeriveKey_PBKDF2: zero length passphrase\n");
89 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER
);
91 #endif /* ALLOW_ZERO_PASSWORD */
93 if(pbkdf2Params
->PseudoRandomFunction
!=
94 CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
) {
95 errorLog0("DeriveKey_PBKDF2: invalid PRF\n");
96 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
99 /* salt, from context, required */
100 CssmData salt
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_SALT
,
101 CSSMERR_CSP_MISSING_ATTR_SALT
);
102 if((salt
.Data
== NULL
) || (salt
.Length
< PBKDF2_MIN_SALT
)){
103 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT
);
106 /* iteration count, from context, required */
107 uint32 iterCount
= context
.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT
,
108 CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT
);
109 if(iterCount
< PBKDF2_MIN_ITER_CNT
) {
110 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT
);
114 * allocate a temp buffer, length
115 * = MAX (hLen, saltLen + 4) + 2 * hLen
116 * = MAX (kSHA1DigestSize, saltLen + 4) + 2 * kSHA1DigestSize
118 uint32 tempLen
= salt
.Length
+ 4;
119 if(tempLen
< kSHA1DigestSize
) {
120 tempLen
= kSHA1DigestSize
;
122 tempLen
+= (2 * kSHA1DigestSize
);
123 CSSM_DATA tempData
= {0, NULL
};
124 setUpData(tempData
, tempLen
, privAllocator
);
129 passphrase
, passphraseLen
,
130 salt
.Data
, salt
.Length
,
132 keyData
->Data
, keyData
->Length
,
134 freeData(&tempData
, privAllocator
, false);
138 * PKCS5 v1.5 key derivation. Also used for traditional openssl key
139 * derivation, which is mighty similar to PKCS5 v1.5, with the addition
140 * of the ability to generate more than (keysize + ivsize) bytes.
142 void AppleCSPSession::DeriveKey_PKCS5_V1_5(
143 const Context
&context
,
144 CSSM_ALGORITHMS algId
,
145 const CssmData
&Param
, // IV optional, mallocd by app to indicate
147 CSSM_DATA
*keyData
) // mallocd by caller to indicate size
149 CSSM_DATA pwd
= {0, NULL
};
151 /* password from either Seed.Param or from base key */
152 CssmCryptoData
*cryptData
=
153 context
.get
<CssmCryptoData
>(CSSM_ATTRIBUTE_SEED
);
154 if((cryptData
!= NULL
) && (cryptData
->Param
.Length
!= 0)) {
155 pwd
= cryptData
->Param
;
158 /* Get secure passphrase from base key */
159 CssmKey
*passKey
= context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
);
160 if (passKey
!= NULL
) {
161 AppleCSPContext::symmetricKeyBits(context
, *this,
162 CSSM_ALGID_SECURE_PASSPHRASE
, CSSM_KEYUSE_DERIVE
,
163 pwd
.Data
, pwd
.Length
);
167 if(pwd
.Data
== NULL
) {
168 errorLog0("DeriveKey_PKCS5_V1_5: null Passphrase\n");
169 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA
);
171 if(pwd
.Length
== 0) {
172 errorLog0("DeriveKey_PKCS5_V1_5: zero length passphrase\n");
173 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER
);
176 CSSM_ALGORITHMS hashAlg
;
178 bool opensslAlg
= false;
180 case CSSM_ALGID_PKCS5_PBKDF1_MD5
:
181 hashAlg
= CSSM_ALGID_MD5
;
182 digestLen
= kMD5DigestSize
;
184 case CSSM_ALGID_PKCS5_PBKDF1_MD2
:
185 hashAlg
= CSSM_ALGID_MD2
;
186 digestLen
= kMD2DigestSize
;
188 case CSSM_ALGID_PKCS5_PBKDF1_SHA1
:
189 hashAlg
= CSSM_ALGID_SHA1
;
190 digestLen
= kSHA1DigestSize
;
192 case CSSM_ALGID_PBE_OPENSSL_MD5
:
193 hashAlg
= CSSM_ALGID_MD5
;
194 digestLen
= kMD5DigestSize
;
198 /* should not have been called */
200 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
204 CSSM_DATA iv
= Param
;
206 /* total requested length can't exceed digest size for struct PKCS5 v1.5*/
207 if(!opensslAlg
&& ((keyData
->Length
+ iv
.Length
) > digestLen
)) {
208 errorLog0("DeriveKey_PKCS5_V1_5: requested length larger than digest\n");
209 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH
);
212 /* salt, from context, required */
213 CssmData salt
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_SALT
,
214 CSSMERR_CSP_MISSING_ATTR_SALT
);
215 if(salt
.Data
== NULL
) {
216 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT
);
219 /* iteration count, from context, required */
220 uint32 iterCount
= context
.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT
,
221 CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT
);
224 * Apply the underlying hash function Hash for c iterations to
225 * the concatenation of the password P and the salt S, then
226 * extract the first dkLen octets to produce a derived key DK:
228 * T1 = Hash (P || S) ,
232 * DK = Tc<0..dkLen-1> .
235 uint8
*keyDataP
= keyData
->Data
;
236 uint32 keyBytesToGo
= keyData
->Length
;
237 uint8
*ivDataP
= iv
.Data
;
238 uint32 ivBytesToGo
= iv
.Length
;
239 bool looping
= false; // true for additional bytes for openssl
240 unsigned char digestOut
[kMaxDigestSize
];
243 /* this loop guaranteed to only run once if !opensslAlg */
244 DigestCtxInit(&ctx
, hashAlg
);
247 /* openssl addition: re-digest the digest here */
248 DigestCtxUpdate(&ctx
, digestOut
, digestLen
);
251 /* digest password then salt */
252 DigestCtxUpdate(&ctx
, pwd
.Data
, pwd
.Length
);
253 DigestCtxUpdate(&ctx
, salt
.Data
, salt
.Length
);
255 DigestCtxFinal(&ctx
, digestOut
);
257 /* now iterCount-1 more iterations */
258 for(unsigned dex
=1; dex
<iterCount
; dex
++) {
259 DigestCtxInit(&ctx
, hashAlg
);
260 DigestCtxUpdate(&ctx
, digestOut
, digestLen
);
261 DigestCtxFinal(&ctx
, digestOut
);
264 /* first n bytes to the key */
265 uint32 bytesAvail
= digestLen
;
266 uint32 toMove
= (keyBytesToGo
> bytesAvail
) ? bytesAvail
: keyBytesToGo
;
267 memmove(keyDataP
, digestOut
, toMove
);
268 uint8
*remainder
= digestOut
+ toMove
;
269 bytesAvail
-= toMove
;
271 keyBytesToGo
-= toMove
;
273 /* then optionally some to IV */
274 if(ivBytesToGo
&& bytesAvail
) {
275 toMove
= (ivBytesToGo
> bytesAvail
) ? bytesAvail
: ivBytesToGo
;
276 memmove(ivDataP
, remainder
, toMove
);
278 ivBytesToGo
-= toMove
;
280 if((keyBytesToGo
== 0) && (ivBytesToGo
== 0)) {
281 /* guaranteed true for PKCS5 v1.5 */
285 assert(opensslAlg
== true);
292 * Member function initially declared for CSPAbstractPluginSession;
293 * we're overriding the null version in CSPFullPluginSession.
295 * We'll generate any type of key (for now).
297 void AppleCSPSession::DeriveKey(
298 CSSM_CC_HANDLE CCHandle
,
299 const Context
&context
,
303 const CssmData
*KeyLabel
,
304 const CSSM_RESOURCE_CONTROL_CONTEXT
*CredAndAclEntry
,
307 /* validate input args, common to all algorithms */
308 switch(context
.algorithm()) {
309 case CSSM_ALGID_PKCS5_PBKDF2
:
311 case CSSM_ALGID_PKCS12_PBE_ENCR
:
312 case CSSM_ALGID_PKCS12_PBE_MAC
:
313 case CSSM_ALGID_PKCS5_PBKDF1_MD5
:
314 case CSSM_ALGID_PKCS5_PBKDF1_MD2
:
315 case CSSM_ALGID_PKCS5_PBKDF1_SHA1
:
316 case CSSM_ALGID_PBE_OPENSSL_MD5
:
317 case CSSM_ALGID_OPENSSH1
:
318 #if CRYPTKIT_CSP_ENABLE
319 case CSSM_ALGID_ECDH
:
320 case CSSM_ALGID_ECDH_X963_KDF
:
323 /* maybe more here, later */
325 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
327 DerivedKey
.KeyData
.Data
= NULL
;
328 DerivedKey
.KeyData
.Length
= 0;
329 cspKeyStorage keyStorage
= cspParseKeyAttr(CKT_Session
, KeyAttr
);
330 cspValidateKeyUsageBits(CKT_Session
, KeyUsage
);
332 /* outgoing key type, required (though any algorithm is OK) */
333 uint32 keyType
= context
.getInt(CSSM_ATTRIBUTE_KEY_TYPE
,
334 CSSMERR_CSP_MISSING_ATTR_KEY_TYPE
);
336 /* outgoing key size, required - any nonzero value is OK */
337 uint32 reqKeySize
= context
.getInt(
338 CSSM_ATTRIBUTE_KEY_LENGTH
,
339 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH
);
341 /* cook up a place to put the key data */
342 uint32 keySizeInBytes
= (reqKeySize
+ 7) / 8;
343 SymmetricBinaryKey
*binKey
= NULL
;
344 CSSM_DATA_PTR keyData
= NULL
;
349 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK
);
351 /* cook up a symmetric binary key */
352 binKey
= new SymmetricBinaryKey(reqKeySize
);
353 keyData
= &binKey
->mKeyData
;
356 /* key bytes --> caller's cssmKey */
357 keyData
= &DerivedKey
.KeyData
;
358 setUpData(*keyData
, keySizeInBytes
,
363 /* break off to algorithm-specific code, whose job it is
364 * to fill in keyData->Data with keyData->Length bytes */
365 switch(context
.algorithm()) {
366 case CSSM_ALGID_PKCS5_PBKDF2
:
367 DeriveKey_PBKDF2(context
,
372 DeriveKey_DH(context
,
377 case CSSM_ALGID_PKCS12_PBE_ENCR
:
378 case CSSM_ALGID_PKCS12_PBE_MAC
:
379 DeriveKey_PKCS12(context
,
384 case CSSM_ALGID_PKCS5_PBKDF1_MD5
:
385 case CSSM_ALGID_PKCS5_PBKDF1_MD2
:
386 case CSSM_ALGID_PKCS5_PBKDF1_SHA1
:
387 case CSSM_ALGID_PBE_OPENSSL_MD5
:
388 DeriveKey_PKCS5_V1_5(context
,
393 case CSSM_ALGID_OPENSSH1
:
394 DeriveKey_OpenSSH1(context
,
399 #if CRYPTKIT_CSP_ENABLE
400 case CSSM_ALGID_ECDH
:
401 case CSSM_ALGID_ECDH_X963_KDF
:
402 CryptKit::DeriveKey_ECDH(context
,
409 /* maybe more here, later */
414 /* set up outgoing header */
415 KeyAttr
&= ~KEY_ATTR_RETURN_MASK
;
416 CSSM_KEYHEADER
&hdr
= DerivedKey
.KeyHeader
;
420 CSSM_KEYCLASS_SESSION_KEY
,
423 /* handle derived size < requested size, legal for Diffie-Hellman */
424 hdr
.LogicalKeySizeInBits
= keyData
->Length
* 8;
426 if(keyStorage
== CKS_Ref
) {
427 /* store and convert to ref key */
428 addRefKey(*binKey
, DerivedKey
);
432 hdr
.BlobType
= CSSM_KEYBLOB_RAW
;
433 hdr
.Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;