2 * Copyright (c) 2000-2001,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 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,2011-2012,2014 Apple Inc. All Rights Reserved.
30 #include <pbkdDigest.h>
31 #include <pkcs12Derive.h>
32 #include "AppleCSPSession.h"
33 #include "AppleCSPUtils.h"
34 #include "AppleCSPContext.h"
35 #include "cspdebugging.h"
36 #include <security_cdsa_utilities/context.h>
37 #include <DH_exchange.h>
38 #include "FEEAsymmetricContext.h"
40 /* minimum legal values */
41 #define PBKDF2_MIN_SALT 8 /* bytes */
42 #define PBKDF2_MIN_ITER_CNT 1000 /* iteration count */
44 #define ALLOW_ZERO_PASSWORD 1
46 void AppleCSPSession::DeriveKey_PBKDF2(
47 const Context
&context
,
48 const CssmData
&Param
,
51 /* validate algorithm-specific arguments */
53 /* Param must point to a CSSM_PKCS5_PBKDF2_PARAMS */
54 if(Param
.Length
!= sizeof(CSSM_PKCS5_PBKDF2_PARAMS
)) {
55 errorLog0("DeriveKey_PBKDF2: Param wrong size\n");
56 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER
);
58 const CSSM_PKCS5_PBKDF2_PARAMS
*pbkdf2Params
=
59 reinterpret_cast<const CSSM_PKCS5_PBKDF2_PARAMS
*>(Param
.Data
);
60 if(pbkdf2Params
== NULL
) {
61 errorLog0("DeriveKey_PBKDF2: null Param.Data\n");
62 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA
);
65 /* Get passphrase from either baseKey or from CSSM_PKCS5_PBKDF2_PARAMS */
66 CssmKey
*passKey
= context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
);
67 CSSM_SIZE passphraseLen
= 0;
68 uint8
*passphrase
= NULL
;
70 AppleCSPContext::symmetricKeyBits(context
, *this,
71 CSSM_ALGID_SECURE_PASSPHRASE
, CSSM_KEYUSE_DERIVE
,
72 passphrase
, passphraseLen
);
75 passphraseLen
= pbkdf2Params
->Passphrase
.Length
;
76 passphrase
= pbkdf2Params
->Passphrase
.Data
;
79 #if !ALLOW_ZERO_PASSWORD
80 /* passphrase required */
81 if(passphrase
== NULL
) {
82 errorLog0("DeriveKey_PBKDF2: null Passphrase\n");
83 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA
);
85 if(passphraseLen
== 0) {
86 /* FIXME - enforce minimum length? */
87 errorLog0("DeriveKey_PBKDF2: zero length passphrase\n");
88 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER
);
90 #endif /* ALLOW_ZERO_PASSWORD */
92 if(pbkdf2Params
->PseudoRandomFunction
!=
93 CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1
) {
94 errorLog0("DeriveKey_PBKDF2: invalid PRF\n");
95 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
98 /* salt, from context, required */
99 CssmData salt
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_SALT
,
100 CSSMERR_CSP_MISSING_ATTR_SALT
);
101 if((salt
.Data
== NULL
) || (salt
.Length
< PBKDF2_MIN_SALT
)){
102 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT
);
105 /* iteration count, from context, required */
106 uint32 iterCount
= context
.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT
,
107 CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT
);
108 if(iterCount
< PBKDF2_MIN_ITER_CNT
) {
109 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ITERATION_COUNT
);
113 * allocate a temp buffer, length
114 * = MAX (hLen, saltLen + 4) + 2 * hLen
115 * = MAX (kSHA1DigestSize, saltLen + 4) + 2 * kSHA1DigestSize
117 size_t tempLen
= salt
.Length
+ 4;
118 if(tempLen
< kSHA1DigestSize
) {
119 tempLen
= kSHA1DigestSize
;
121 tempLen
+= (2 * kSHA1DigestSize
);
122 CSSM_DATA tempData
= {0, NULL
};
123 setUpData(tempData
, tempLen
, privAllocator
);
128 passphrase
, (uint32
)passphraseLen
,
129 salt
.Data
, (uint32
)salt
.Length
,
131 keyData
->Data
, (uint32
)keyData
->Length
,
133 freeData(&tempData
, privAllocator
, false);
137 * PKCS5 v1.5 key derivation. Also used for traditional openssl key
138 * derivation, which is mighty similar to PKCS5 v1.5, with the addition
139 * of the ability to generate more than (keysize + ivsize) bytes.
141 void AppleCSPSession::DeriveKey_PKCS5_V1_5(
142 const Context
&context
,
143 CSSM_ALGORITHMS algId
,
144 const CssmData
&Param
, // IV optional, mallocd by app to indicate
146 CSSM_DATA
*keyData
) // mallocd by caller to indicate size
148 CSSM_DATA pwd
= {0, NULL
};
150 /* password from either Seed.Param or from base key */
151 CssmCryptoData
*cryptData
=
152 context
.get
<CssmCryptoData
>(CSSM_ATTRIBUTE_SEED
);
153 if((cryptData
!= NULL
) && (cryptData
->Param
.Length
!= 0)) {
154 pwd
= cryptData
->Param
;
157 /* Get secure passphrase from base key */
158 CssmKey
*passKey
= context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
);
159 if (passKey
!= NULL
) {
160 AppleCSPContext::symmetricKeyBits(context
, *this,
161 CSSM_ALGID_SECURE_PASSPHRASE
, CSSM_KEYUSE_DERIVE
,
162 pwd
.Data
, pwd
.Length
);
166 if(pwd
.Data
== NULL
) {
167 errorLog0("DeriveKey_PKCS5_V1_5: null Passphrase\n");
168 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA
);
170 if(pwd
.Length
== 0) {
171 errorLog0("DeriveKey_PKCS5_V1_5: zero length passphrase\n");
172 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER
);
175 CSSM_ALGORITHMS hashAlg
;
177 bool opensslAlg
= false;
179 case CSSM_ALGID_PKCS5_PBKDF1_MD5
:
180 hashAlg
= CSSM_ALGID_MD5
;
181 digestLen
= kMD5DigestSize
;
183 case CSSM_ALGID_PKCS5_PBKDF1_MD2
:
184 hashAlg
= CSSM_ALGID_MD2
;
185 digestLen
= kMD2DigestSize
;
187 case CSSM_ALGID_PKCS5_PBKDF1_SHA1
:
188 hashAlg
= CSSM_ALGID_SHA1
;
189 digestLen
= kSHA1DigestSize
;
191 case CSSM_ALGID_PBE_OPENSSL_MD5
:
192 hashAlg
= CSSM_ALGID_MD5
;
193 digestLen
= kMD5DigestSize
;
197 /* should not have been called */
199 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
203 CSSM_DATA iv
= Param
;
205 /* total requested length can't exceed digest size for struct PKCS5 v1.5*/
206 if(!opensslAlg
&& ((keyData
->Length
+ iv
.Length
) > digestLen
)) {
207 errorLog0("DeriveKey_PKCS5_V1_5: requested length larger than digest\n");
208 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH
);
211 /* salt, from context, required */
212 CssmData salt
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_SALT
,
213 CSSMERR_CSP_MISSING_ATTR_SALT
);
214 if(salt
.Data
== NULL
) {
215 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SALT
);
218 /* iteration count, from context, required */
219 uint32 iterCount
= context
.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT
,
220 CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT
);
223 * Apply the underlying hash function Hash for c iterations to
224 * the concatenation of the password P and the salt S, then
225 * extract the first dkLen octets to produce a derived key DK:
227 * T1 = Hash (P || S) ,
231 * DK = Tc<0..dkLen-1> .
234 uint8
*keyDataP
= keyData
->Data
;
235 size_t keyBytesToGo
= keyData
->Length
;
236 uint8
*ivDataP
= iv
.Data
;
237 size_t ivBytesToGo
= iv
.Length
;
238 bool looping
= false; // true for additional bytes for openssl
239 unsigned char digestOut
[kMaxDigestSize
];
242 /* this loop guaranteed to only run once if !opensslAlg */
243 DigestCtxInit(&ctx
, hashAlg
);
246 /* openssl addition: re-digest the digest here */
247 DigestCtxUpdate(&ctx
, digestOut
, digestLen
);
250 /* digest password then salt */
251 DigestCtxUpdate(&ctx
, pwd
.Data
, (uint32
)pwd
.Length
);
252 DigestCtxUpdate(&ctx
, salt
.Data
, (uint32
)salt
.Length
);
254 DigestCtxFinal(&ctx
, digestOut
);
256 /* now iterCount-1 more iterations */
257 for(unsigned dex
=1; dex
<iterCount
; dex
++) {
258 DigestCtxInit(&ctx
, hashAlg
);
259 DigestCtxUpdate(&ctx
, digestOut
, digestLen
);
260 DigestCtxFinal(&ctx
, digestOut
);
263 /* first n bytes to the key */
264 uint32 bytesAvail
= digestLen
;
265 size_t toMove
= (keyBytesToGo
> bytesAvail
) ? bytesAvail
: keyBytesToGo
;
266 memmove(keyDataP
, digestOut
, toMove
);
267 uint8
*remainder
= digestOut
+ toMove
;
268 bytesAvail
-= toMove
;
270 keyBytesToGo
-= toMove
;
272 /* then optionally some to IV */
273 if(ivBytesToGo
&& bytesAvail
) {
274 toMove
= (ivBytesToGo
> bytesAvail
) ? bytesAvail
: ivBytesToGo
;
275 memmove(ivDataP
, remainder
, toMove
);
277 ivBytesToGo
-= toMove
;
279 if((keyBytesToGo
== 0) && (ivBytesToGo
== 0)) {
280 /* guaranteed true for PKCS5 v1.5 */
284 assert(opensslAlg
== true);
291 * Member function initially declared for CSPAbstractPluginSession;
292 * we're overriding the null version in CSPFullPluginSession.
294 * We'll generate any type of key (for now).
296 void AppleCSPSession::DeriveKey(
297 CSSM_CC_HANDLE CCHandle
,
298 const Context
&context
,
302 const CssmData
*KeyLabel
,
303 const CSSM_RESOURCE_CONTROL_CONTEXT
*CredAndAclEntry
,
306 /* validate input args, common to all algorithms */
307 switch(context
.algorithm()) {
308 case CSSM_ALGID_PKCS5_PBKDF2
:
310 case CSSM_ALGID_PKCS12_PBE_ENCR
:
311 case CSSM_ALGID_PKCS12_PBE_MAC
:
312 case CSSM_ALGID_PKCS5_PBKDF1_MD5
:
313 case CSSM_ALGID_PKCS5_PBKDF1_MD2
:
314 case CSSM_ALGID_PKCS5_PBKDF1_SHA1
:
315 case CSSM_ALGID_PBE_OPENSSL_MD5
:
316 case CSSM_ALGID_OPENSSH1
:
317 #if CRYPTKIT_CSP_ENABLE
318 case CSSM_ALGID_ECDH
:
319 case CSSM_ALGID_ECDH_X963_KDF
:
322 /* maybe more here, later */
324 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
326 DerivedKey
.KeyData
.Data
= NULL
;
327 DerivedKey
.KeyData
.Length
= 0;
328 cspKeyStorage keyStorage
= cspParseKeyAttr(CKT_Session
, KeyAttr
);
329 cspValidateKeyUsageBits(CKT_Session
, KeyUsage
);
331 /* outgoing key type, required (though any algorithm is OK) */
332 uint32 keyType
= context
.getInt(CSSM_ATTRIBUTE_KEY_TYPE
,
333 CSSMERR_CSP_MISSING_ATTR_KEY_TYPE
);
335 /* outgoing key size, required - any nonzero value is OK */
336 uint32 reqKeySize
= context
.getInt(
337 CSSM_ATTRIBUTE_KEY_LENGTH
,
338 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH
);
340 /* cook up a place to put the key data */
341 uint32 keySizeInBytes
= (reqKeySize
+ 7) / 8;
342 SymmetricBinaryKey
*binKey
= NULL
;
343 CSSM_DATA_PTR keyData
= NULL
;
348 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK
);
350 /* cook up a symmetric binary key */
351 binKey
= new SymmetricBinaryKey(reqKeySize
);
352 keyData
= &binKey
->mKeyData
;
355 /* key bytes --> caller's cssmKey */
356 keyData
= &DerivedKey
.KeyData
;
357 setUpData(*keyData
, keySizeInBytes
,
362 /* break off to algorithm-specific code, whose job it is
363 * to fill in keyData->Data with keyData->Length bytes */
364 switch(context
.algorithm()) {
365 case CSSM_ALGID_PKCS5_PBKDF2
:
366 DeriveKey_PBKDF2(context
,
371 DeriveKey_DH(context
,
376 case CSSM_ALGID_PKCS12_PBE_ENCR
:
377 case CSSM_ALGID_PKCS12_PBE_MAC
:
378 DeriveKey_PKCS12(context
,
383 case CSSM_ALGID_PKCS5_PBKDF1_MD5
:
384 case CSSM_ALGID_PKCS5_PBKDF1_MD2
:
385 case CSSM_ALGID_PKCS5_PBKDF1_SHA1
:
386 case CSSM_ALGID_PBE_OPENSSL_MD5
:
387 DeriveKey_PKCS5_V1_5(context
,
392 case CSSM_ALGID_OPENSSH1
:
393 DeriveKey_OpenSSH1(context
,
398 #if CRYPTKIT_CSP_ENABLE
399 case CSSM_ALGID_ECDH
:
400 case CSSM_ALGID_ECDH_X963_KDF
:
401 CryptKit::DeriveKey_ECDH(context
,
408 /* maybe more here, later */
413 /* set up outgoing header */
414 KeyAttr
&= ~KEY_ATTR_RETURN_MASK
;
415 CSSM_KEYHEADER
&hdr
= DerivedKey
.KeyHeader
;
419 CSSM_KEYCLASS_SESSION_KEY
,
422 /* handle derived size < requested size, legal for Diffie-Hellman */
423 hdr
.LogicalKeySizeInBits
= (uint32
)(keyData
->Length
* 8);
425 if(keyStorage
== CKS_Ref
) {
426 /* store and convert to ref key */
427 addRefKey(*binKey
, DerivedKey
);
431 hdr
.BlobType
= CSSM_KEYBLOB_RAW
;
432 hdr
.Format
= CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;