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	 */
118	size_t tempLen = salt.Length + 4;
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,
129		passphrase, (uint32)passphraseLen,
130		salt.Data, (uint32)salt.Length,
131		iterCount,
132		keyData->Data, (uint32)keyData->Length,
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;
236	size_t keyBytesToGo = keyData->Length;
237	uint8 *ivDataP		= iv.Data;
238	size_t ivBytesToGo  = iv.Length;
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 */
252		DigestCtxUpdate(&ctx, pwd.Data, (uint32)pwd.Length);
253		DigestCtxUpdate(&ctx, salt.Data, (uint32)salt.Length);
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;
266		size_t toMove = (keyBytesToGo > bytesAvail) ? bytesAvail : keyBytesToGo;
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 */
424	hdr.LogicalKeySizeInBits = (uint32)(keyData->Length * 8);
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
437