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