]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_csp/lib/deriveKey.cpp
Security-57337.40.85.tar.gz
[apple/security.git] / OSX / libsecurity_apple_csp / lib / deriveKey.cpp
1 /*
2 * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved.
3 *
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
8 * using this file.
9 *
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.
16 */
17
18
19 /*
20 File: deriveKey.cpp
21
22 Contains: CSSM_DeriveKey functions
23
24 Copyright (c) 2000,2011-2012,2014 Apple Inc. All Rights Reserved.
25
26 */
27
28 #include <HMACSHA1.h>
29 #include <pbkdf2.h>
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"
39
40 /* minimum legal values */
41 #define PBKDF2_MIN_SALT 8 /* bytes */
42 #define PBKDF2_MIN_ITER_CNT 1000 /* iteration count */
43
44 #define ALLOW_ZERO_PASSWORD 1
45
46 void AppleCSPSession::DeriveKey_PBKDF2(
47 const Context &context,
48 const CssmData &Param,
49 CSSM_DATA *keyData)
50 {
51 /* validate algorithm-specific arguments */
52
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);
57 }
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);
63 }
64
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;
69 if(passKey != NULL) {
70 AppleCSPContext::symmetricKeyBits(context, *this,
71 CSSM_ALGID_SECURE_PASSPHRASE, CSSM_KEYUSE_DERIVE,
72 passphrase, passphraseLen);
73 }
74 else {
75 passphraseLen = pbkdf2Params->Passphrase.Length;
76 passphrase = pbkdf2Params->Passphrase.Data;
77 }
78
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);
84 }
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);
89 }
90 #endif /* ALLOW_ZERO_PASSWORD */
91
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);
96 }
97
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);
103 }
104
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);
110 }
111
112 /*
113 * allocate a temp buffer, length
114 * = MAX (hLen, saltLen + 4) + 2 * hLen
115 * = MAX (kSHA1DigestSize, saltLen + 4) + 2 * kSHA1DigestSize
116 */
117 size_t tempLen = salt.Length + 4;
118 if(tempLen < kSHA1DigestSize) {
119 tempLen = kSHA1DigestSize;
120 }
121 tempLen += (2 * kSHA1DigestSize);
122 CSSM_DATA tempData = {0, NULL};
123 setUpData(tempData, tempLen, privAllocator);
124
125 /* go */
126 pbkdf2 (hmacsha1,
127 kSHA1DigestSize,
128 passphrase, (uint32)passphraseLen,
129 salt.Data, (uint32)salt.Length,
130 iterCount,
131 keyData->Data, (uint32)keyData->Length,
132 tempData.Data);
133 freeData(&tempData, privAllocator, false);
134 }
135
136 /*
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.
140 */
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
145 // size
146 CSSM_DATA *keyData) // mallocd by caller to indicate size
147 {
148 CSSM_DATA pwd = {0, NULL};
149
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;
155 }
156 else {
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);
163 }
164 }
165
166 if(pwd.Data == NULL) {
167 errorLog0("DeriveKey_PKCS5_V1_5: null Passphrase\n");
168 CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
169 }
170 if(pwd.Length == 0) {
171 errorLog0("DeriveKey_PKCS5_V1_5: zero length passphrase\n");
172 CssmError::throwMe(CSSMERR_CSP_INVALID_INPUT_POINTER);
173 }
174
175 CSSM_ALGORITHMS hashAlg;
176 unsigned digestLen;
177 bool opensslAlg = false;
178 switch(algId) {
179 case CSSM_ALGID_PKCS5_PBKDF1_MD5:
180 hashAlg = CSSM_ALGID_MD5;
181 digestLen = kMD5DigestSize;
182 break;
183 case CSSM_ALGID_PKCS5_PBKDF1_MD2:
184 hashAlg = CSSM_ALGID_MD2;
185 digestLen = kMD2DigestSize;
186 break;
187 case CSSM_ALGID_PKCS5_PBKDF1_SHA1:
188 hashAlg = CSSM_ALGID_SHA1;
189 digestLen = kSHA1DigestSize;
190 break;
191 case CSSM_ALGID_PBE_OPENSSL_MD5:
192 hashAlg = CSSM_ALGID_MD5;
193 digestLen = kMD5DigestSize;
194 opensslAlg = true;
195 break;
196 default:
197 /* should not have been called */
198 assert(0);
199 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
200 }
201
202 /* IV optional */
203 CSSM_DATA iv = Param;
204
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);
209 }
210
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);
216 }
217
218 /* iteration count, from context, required */
219 uint32 iterCount = context.getInt(CSSM_ATTRIBUTE_ITERATION_COUNT,
220 CSSMERR_CSP_MISSING_ATTR_ITERATION_COUNT);
221
222 /*
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:
226 *
227 * T1 = Hash (P || S) ,
228 * T2 = Hash (T1) ,
229 * ...
230 * Tc = Hash (Tc-1) ,
231 * DK = Tc<0..dkLen-1> .
232 */
233 DigestCtx ctx;
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];
240
241 for(;;) {
242 /* this loop guaranteed to only run once if !opensslAlg */
243 DigestCtxInit(&ctx, hashAlg);
244
245 if(looping) {
246 /* openssl addition: re-digest the digest here */
247 DigestCtxUpdate(&ctx, digestOut, digestLen);
248 }
249
250 /* digest password then salt */
251 DigestCtxUpdate(&ctx, pwd.Data, (uint32)pwd.Length);
252 DigestCtxUpdate(&ctx, salt.Data, (uint32)salt.Length);
253
254 DigestCtxFinal(&ctx, digestOut);
255
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);
261 }
262
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;
269 keyDataP += toMove;
270 keyBytesToGo -= toMove;
271
272 /* then optionally some to IV */
273 if(ivBytesToGo && bytesAvail) {
274 toMove = (ivBytesToGo > bytesAvail) ? bytesAvail : ivBytesToGo;
275 memmove(ivDataP, remainder, toMove);
276 ivDataP += toMove;
277 ivBytesToGo -= toMove;
278 }
279 if((keyBytesToGo == 0) && (ivBytesToGo == 0)) {
280 /* guaranteed true for PKCS5 v1.5 */
281 break;
282 }
283
284 assert(opensslAlg == true);
285 looping = true;
286 }
287 DigestCtxFree(&ctx);
288 }
289
290 /*
291 * Member function initially declared for CSPAbstractPluginSession;
292 * we're overriding the null version in CSPFullPluginSession.
293 *
294 * We'll generate any type of key (for now).
295 */
296 void AppleCSPSession::DeriveKey(
297 CSSM_CC_HANDLE CCHandle,
298 const Context &context,
299 CssmData &Param,
300 uint32 KeyUsage,
301 uint32 KeyAttr,
302 const CssmData *KeyLabel,
303 const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
304 CssmKey &DerivedKey)
305 {
306 /* validate input args, common to all algorithms */
307 switch(context.algorithm()) {
308 case CSSM_ALGID_PKCS5_PBKDF2:
309 case CSSM_ALGID_DH:
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:
320 #endif
321 break;
322 /* maybe more here, later */
323 default:
324 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
325 }
326 DerivedKey.KeyData.Data = NULL;
327 DerivedKey.KeyData.Length = 0;
328 cspKeyStorage keyStorage = cspParseKeyAttr(CKT_Session, KeyAttr);
329 cspValidateKeyUsageBits(CKT_Session, KeyUsage);
330
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);
334
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);
339
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;
344
345 switch(keyStorage) {
346 case CKS_None:
347 /* no way */
348 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
349 case CKS_Ref:
350 /* cook up a symmetric binary key */
351 binKey = new SymmetricBinaryKey(reqKeySize);
352 keyData = &binKey->mKeyData;
353 break;
354 case CKS_Data:
355 /* key bytes --> caller's cssmKey */
356 keyData = &DerivedKey.KeyData;
357 setUpData(*keyData, keySizeInBytes,
358 normAllocator);
359 break;
360 }
361
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,
367 Param,
368 keyData);
369 break;
370 case CSSM_ALGID_DH:
371 DeriveKey_DH(context,
372 Param,
373 keyData,
374 *this);
375 break;
376 case CSSM_ALGID_PKCS12_PBE_ENCR:
377 case CSSM_ALGID_PKCS12_PBE_MAC:
378 DeriveKey_PKCS12(context,
379 *this,
380 Param,
381 keyData);
382 break;
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,
388 context.algorithm(),
389 Param,
390 keyData);
391 break;
392 case CSSM_ALGID_OPENSSH1:
393 DeriveKey_OpenSSH1(context,
394 context.algorithm(),
395 Param,
396 keyData);
397 break;
398 #if CRYPTKIT_CSP_ENABLE
399 case CSSM_ALGID_ECDH:
400 case CSSM_ALGID_ECDH_X963_KDF:
401 CryptKit::DeriveKey_ECDH(context,
402 context.algorithm(),
403 Param,
404 keyData,
405 *this);
406 break;
407 #endif
408 /* maybe more here, later */
409 default:
410 assert(0);
411 }
412
413 /* set up outgoing header */
414 KeyAttr &= ~KEY_ATTR_RETURN_MASK;
415 CSSM_KEYHEADER &hdr = DerivedKey.KeyHeader;
416 setKeyHeader(hdr,
417 plugin.myGuid(),
418 keyType,
419 CSSM_KEYCLASS_SESSION_KEY,
420 KeyAttr,
421 KeyUsage);
422 /* handle derived size < requested size, legal for Diffie-Hellman */
423 hdr.LogicalKeySizeInBits = (uint32)(keyData->Length * 8);
424
425 if(keyStorage == CKS_Ref) {
426 /* store and convert to ref key */
427 addRefKey(*binKey, DerivedKey);
428 }
429 else {
430 /* Raw data */
431 hdr.BlobType = CSSM_KEYBLOB_RAW;
432 hdr.Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
433 }
434 }
435