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
46void 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 */
141void 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 */
296void 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
436