1/*
2 * Copyright (c) 2001,2003-2011 Apple, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * srCdsaUtils.cpp -- common CDSA access utilities
24 */
25
26#include "srCdsaUtils.h"
27#include <stdlib.h>
28#include <stdio.h>
29#include <Security/SecCertificate.h>
30#include <Security/cssmapple.h>				/* for cssmPerror() */
31#include <Security/oidsalg.h>				/* for cssmPerror() */
32#include <strings.h>
33
34static CSSM_VERSION vers = {2, 0};
35static const CSSM_GUID testGuid = { 0xFADE, 0, 0, { 1,2,3,4,5,6,7,0 }};
36
37/*
38 * Standard app-level memory functions required by CDSA.
39 */
40void * srAppMalloc (CSSM_SIZE size, void *allocRef) {
41	return( malloc(size) );
42}
43
44void srAppFree (void *mem_ptr, void *allocRef) {
45	free(mem_ptr);
46 	return;
47}
48
49void * srAppRealloc (void *ptr, CSSM_SIZE size, void *allocRef) {
50	return( realloc( ptr, size ) );
51}
52
53void * srAppCalloc (uint32 num, CSSM_SIZE size, void *allocRef) {
54	return( calloc( num, size ) );
55}
56
57static CSSM_API_MEMORY_FUNCS memFuncs = {
58	srAppMalloc,
59	srAppFree,
60	srAppRealloc,
61 	srAppCalloc,
62 	NULL
63 };
64
65CSSM_BOOL srCompareCssmData(const CSSM_DATA *d1,
66	const CSSM_DATA *d2)
67{
68	if(d1->Length != d2->Length) {
69		return CSSM_FALSE;
70	}
71	if(memcmp(d1->Data, d2->Data, d1->Length)) {
72		return CSSM_FALSE;
73	}
74	return CSSM_TRUE;
75}
76
77/*
78 * Init CSSM; returns CSSM_FALSE on error. Reusable.
79 */
80static CSSM_BOOL cssmInitd = CSSM_FALSE;
81
82CSSM_BOOL srCssmStartup()
83{
84	CSSM_RETURN  crtn;
85    CSSM_PVC_MODE pvcPolicy = CSSM_PVC_NONE;
86
87	if(cssmInitd) {
88		return CSSM_TRUE;
89	}
90	crtn = CSSM_Init (&vers,
91		CSSM_PRIVILEGE_SCOPE_NONE,
92		&testGuid,
93		CSSM_KEY_HIERARCHY_NONE,
94		&pvcPolicy,
95		NULL /* reserved */);
96	if(crtn != CSSM_OK)
97	{
98		srPrintError("CSSM_Init", crtn);
99		return CSSM_FALSE;
100	}
101	else {
102		cssmInitd = CSSM_TRUE;
103		return CSSM_TRUE;
104	}
105}
106
107/*
108 * Attach to CSP. Returns zero on error.
109 */
110CSSM_CSP_HANDLE srCspStartup(
111	CSSM_BOOL bareCsp)		// true ==> CSP, false ==> CSP/DL
112{
113	CSSM_CSP_HANDLE cspHand;
114	CSSM_RETURN		crtn;
115	const CSSM_GUID *guid;
116
117	/* common CSSM init */
118	if(srCssmStartup() == CSSM_FALSE) {
119		return 0;
120	}
121	if(bareCsp) {
122		guid = &gGuidAppleCSP;
123	}
124	else {
125		guid = &gGuidAppleCSPDL;
126	}
127	crtn = CSSM_ModuleLoad(guid,
128		CSSM_KEY_HIERARCHY_NONE,
129		NULL,			// eventHandler
130		NULL);			// AppNotifyCallbackCtx
131	if(crtn) {
132		srPrintError("CSSM_ModuleLoad()", crtn);
133		return 0;
134	}
135	crtn = CSSM_ModuleAttach (guid,
136		&vers,
137		&memFuncs,			// memFuncs
138		0,					// SubserviceID
139		CSSM_SERVICE_CSP,
140		0,					// AttachFlags
141		CSSM_KEY_HIERARCHY_NONE,
142		NULL,				// FunctionTable
143		0,					// NumFuncTable
144		NULL,				// reserved
145		&cspHand);
146	if(crtn) {
147		srPrintError("CSSM_ModuleAttach()", crtn);
148		return 0;
149	}
150	return cspHand;
151}
152
153/* Attach to DL side of CSPDL */
154CSSM_DL_HANDLE srDlStartup()
155{
156	CSSM_DL_HANDLE 	dlHand = 0;
157	CSSM_RETURN		crtn;
158
159	if(srCssmStartup() == CSSM_FALSE) {
160		return 0;
161	}
162	crtn = CSSM_ModuleLoad(&gGuidAppleCSPDL,
163		CSSM_KEY_HIERARCHY_NONE,
164		NULL,			// eventHandler
165		NULL);			// AppNotifyCallbackCtx
166	if(crtn) {
167		srPrintError("CSSM_ModuleLoad(Apple CSPDL)", crtn);
168		return 0;
169	}
170	crtn = CSSM_ModuleAttach (&gGuidAppleCSPDL,
171		&vers,
172		&memFuncs,			// memFuncs
173		0,					// SubserviceID
174		CSSM_SERVICE_DL,
175		0,					// AttachFlags
176		CSSM_KEY_HIERARCHY_NONE,
177		NULL,				// FunctionTable
178		0,					// NumFuncTable
179		NULL,				// reserved
180		&dlHand);
181	if(crtn) {
182		srPrintError("CSSM_ModuleAttach(Apple CSPDL)", crtn);
183		return 0;
184	}
185	return dlHand;
186}
187
188CSSM_CL_HANDLE srClStartup()
189{
190	CSSM_CL_HANDLE clHand;
191	CSSM_RETURN crtn;
192
193	if(srCssmStartup() == CSSM_FALSE) {
194		return 0;
195	}
196	crtn = CSSM_ModuleLoad(&gGuidAppleX509CL,
197		CSSM_KEY_HIERARCHY_NONE,
198		NULL,			// eventHandler
199		NULL);			// AppNotifyCallbackCtx
200	if(crtn) {
201		srPrintError("CSSM_ModuleLoad(AppleCL)", crtn);
202		return 0;
203	}
204	crtn = CSSM_ModuleAttach (&gGuidAppleX509CL,
205		&vers,
206		&memFuncs,				// memFuncs
207		0,						// SubserviceID
208		CSSM_SERVICE_CL,		// SubserviceFlags - Where is this used?
209		0,						// AttachFlags
210		CSSM_KEY_HIERARCHY_NONE,
211		NULL,					// FunctionTable
212		0,						// NumFuncTable
213		NULL,					// reserved
214		&clHand);
215	if(crtn) {
216		srPrintError("CSSM_ModuleAttach(AppleCL)", crtn);
217		return 0;
218	}
219	else {
220		return clHand;
221	}
222}
223
224CSSM_TP_HANDLE srTpStartup()
225{
226	CSSM_TP_HANDLE tpHand;
227	CSSM_RETURN crtn;
228
229	if(srCssmStartup() == CSSM_FALSE) {
230		return 0;
231	}
232	crtn = CSSM_ModuleLoad(&gGuidAppleX509TP,
233		CSSM_KEY_HIERARCHY_NONE,
234		NULL,			// eventHandler
235		NULL);			// AppNotifyCallbackCtx
236	if(crtn) {
237		srPrintError("CSSM_ModuleLoad(AppleTP)", crtn);
238		return 0;
239	}
240	crtn = CSSM_ModuleAttach (&gGuidAppleX509TP,
241		&vers,
242		&memFuncs,				// memFuncs
243		0,						// SubserviceID
244		CSSM_SERVICE_TP,		// SubserviceFlags
245		0,						// AttachFlags
246		CSSM_KEY_HIERARCHY_NONE,
247		NULL,					// FunctionTable
248		0,						// NumFuncTable
249		NULL,					// reserved
250		&tpHand);
251	if(crtn) {
252		srPrintError("CSSM_ModuleAttach(AppleTP)", crtn);
253		return 0;
254	}
255	else {
256		return tpHand;
257	}
258}
259
260/*
261 * Given a context specified via a CSSM_CC_HANDLE, add a new
262 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
263 * AttributeLength, and an untyped pointer.
264 */
265CSSM_RETURN srAddContextAttribute(CSSM_CC_HANDLE CCHandle,
266	uint32 AttributeType,
267	uint32 AttributeLength,
268	const void *AttributePtr)
269{
270	CSSM_CONTEXT_ATTRIBUTE		newAttr;
271	CSSM_RETURN					crtn;
272
273	newAttr.AttributeType     = AttributeType;
274	newAttr.AttributeLength   = AttributeLength;
275	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
276	crtn = CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
277	if(crtn) {
278		srPrintError("CSSM_UpdateContextAttributes", crtn);
279	}
280	return crtn;
281}
282
283
284/*
285 * Derive symmetric key.
286 * Note in the X CSP, we never return an IV.
287 */
288CSSM_RETURN srCspDeriveKey(CSSM_CSP_HANDLE cspHand,
289		uint32				keyAlg,			// CSSM_ALGID_RC5, etc.
290		const char 			*keyLabel,
291		unsigned 			keyLabelLen,
292		uint32 				keyUsage,		// CSSM_KEYUSE_ENCRYPT, etc.
293		uint32 				keySizeInBits,
294		CSSM_DATA_PTR		password,		// in PKCS-5 lingo
295		CSSM_DATA_PTR		salt,			// ditto
296		uint32				iterationCnt,	// ditto
297		CSSM_KEY_PTR		key)
298{
299	CSSM_RETURN					crtn;
300	CSSM_CC_HANDLE 				ccHand;
301	uint32						keyAttr;
302	CSSM_DATA					dummyLabel;
303	CSSM_PKCS5_PBKDF2_PARAMS 	pbeParams;
304	CSSM_DATA					pbeData;
305	CSSM_ACCESS_CREDENTIALS		creds;
306
307	memset(key, 0, sizeof(CSSM_KEY));
308	memset(&creds, 0, sizeof(CSSM_ACCESS_CREDENTIALS));
309	crtn = CSSM_CSP_CreateDeriveKeyContext(cspHand,
310		CSSM_ALGID_PKCS5_PBKDF2,
311		keyAlg,
312		keySizeInBits,
313		&creds,
314		NULL,			// BaseKey
315		iterationCnt,
316		salt,
317		NULL,			// seed
318		&ccHand);
319	if(crtn) {
320		srPrintError("CSSM_CSP_CreateDeriveKeyContext", crtn);
321		return crtn;
322	}
323	keyAttr = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF |
324			  CSSM_KEYATTR_SENSITIVE;
325	dummyLabel.Length = keyLabelLen;
326	dummyLabel.Data = (uint8 *)keyLabel;
327
328	/* passing in password is pretty strange....*/
329	pbeParams.Passphrase = *password;
330	pbeParams.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
331	pbeData.Data = (uint8 *)&pbeParams;
332	pbeData.Length = sizeof(pbeParams);
333	crtn = CSSM_DeriveKey(ccHand,
334		&pbeData,
335		keyUsage,
336		keyAttr,
337		&dummyLabel,
338		NULL,			// cred and acl
339		key);
340	if(crtn) {
341		srPrintError("CSSM_DeriveKey", crtn);
342		return crtn;
343	}
344	crtn = CSSM_DeleteContext(ccHand);
345	if(crtn) {
346		srPrintError("CSSM_DeleteContext", crtn);
347	}
348	return crtn;
349}
350
351/*
352 * Generate key pair of arbitrary algorithm.
353 */
354
355/* CSP DL currently does not perform DSA generate params; let CSP do it implicitly */
356#define DO_DSA_GEN_PARAMS		0
357
358CSSM_RETURN srCspGenKeyPair(CSSM_CSP_HANDLE cspHand,
359	CSSM_DL_DB_HANDLE *dlDbHand,	// optional
360	uint32 algorithm,
361	const char *keyLabel,
362	unsigned keyLabelLen,
363	uint32 keySize,					// in bits
364	CSSM_KEY_PTR pubKey,			// mallocd by caller
365	CSSM_KEYUSE pubKeyUsage,		// CSSM_KEYUSE_ENCRYPT, etc.
366	CSSM_KEYATTR_FLAGS pubAttrs,	// CSSM_KEYATTR_EXTRACTABLE, etc.
367	CSSM_KEY_PTR privKey,			// mallocd by caller
368	CSSM_KEYUSE privKeyUsage,		// CSSM_KEYUSE_DECRYPT, etc.
369	CSSM_KEYATTR_FLAGS privAttrs)	// CSSM_KEYATTR_EXTRACTABLE, etc.
370{
371	CSSM_RETURN				crtn;
372	CSSM_RETURN				ocrtn;
373	CSSM_CC_HANDLE 			ccHand;
374	CSSM_DATA				keyLabelData;
375
376	keyLabelData.Data        = (uint8 *)keyLabel,
377	keyLabelData.Length      = keyLabelLen;
378	memset(pubKey, 0, sizeof(CSSM_KEY));
379	memset(privKey, 0, sizeof(CSSM_KEY));
380
381	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
382		algorithm,
383		keySize,
384		NULL,					// Seed
385		NULL,					// Salt
386		NULL,					// StartDate
387		NULL,					// EndDate
388		NULL,					// Params
389		&ccHand);
390	if(crtn) {
391		srPrintError("CSSM_CSP_CreateKeyGenContext", crtn);
392		return crtn;
393	}
394
395	/* post-context-create algorithm-specific stuff */
396	switch(algorithm) {
397		#if DO_DSA_GEN_PARAMS
398		case CSSM_ALGID_DSA:
399			/*
400			 * extra step - generate params - this just adds some
401			 * info to the context
402			 */
403			{
404				CSSM_DATA dummy = {0, NULL};
405				crtn = CSSM_GenerateAlgorithmParams(ccHand,
406					keySize, &dummy);
407				if(crtn) {
408					srPrintError("CSSM_GenerateAlgorithmParams", crtn);
409					CSSM_DeleteContext(ccHand);
410					return crtn;
411				}
412				srAppFree(dummy.Data, NULL);
413			}
414			break;
415		#endif	/* DO_DSA_GEN_PARAMS */
416		default:
417			break;
418	}
419
420	/* optionally specify DL/DB storage location */
421	if(dlDbHand) {
422		crtn = srAddContextAttribute(ccHand,
423			CSSM_ATTRIBUTE_DL_DB_HANDLE,
424			sizeof(CSSM_ATTRIBUTE_DL_DB_HANDLE),
425			dlDbHand);
426		if(crtn) {
427			CSSM_DeleteContext(ccHand);
428			return crtn;
429		}
430	}
431	ocrtn = CSSM_GenerateKeyPair(ccHand,
432		pubKeyUsage,
433		pubAttrs,
434		&keyLabelData,
435		pubKey,
436		privKeyUsage,
437		privAttrs,
438		&keyLabelData,			// same labels
439		NULL,					// CredAndAclEntry
440		privKey);
441	if(ocrtn) {
442		srPrintError("CSSM_GenerateKeyPair", ocrtn);
443	}
444	crtn = CSSM_DeleteContext(ccHand);
445	if(crtn) {
446		srPrintError("CSSM_DeleteContext", crtn);
447		if(ocrtn == CSSM_OK) {
448			/* error on CSSM_GenerateKeyPair takes precedence */
449			ocrtn = crtn;
450		}
451	}
452	return ocrtn;
453}
454
455
456/*
457 * Add a certificate to an open Keychain.
458 */
459CSSM_RETURN srAddCertToKC(
460	SecKeychainRef		keychain,
461	const CSSM_DATA		*cert,
462	CSSM_CERT_TYPE		certType,
463	CSSM_CERT_ENCODING	certEncoding,
464	const char			*printName,		// C string
465	const CSSM_DATA		*keyLabel)		// ??
466{
467	SecCertificateRef certificate;
468
469	OSStatus rslt = SecCertificateCreateFromData(cert, certType, certEncoding, &certificate);
470	if (!rslt)
471	{
472		rslt = SecCertificateAddToKeychain(certificate, keychain);
473		CFRelease(certificate);
474	}
475
476	return rslt;
477}
478
479/*
480 * Convert a CSSM_DATA_PTR, referring to a DER-encoded int, to an
481 * unsigned.
482 */
483unsigned srDER_ToInt(const CSSM_DATA *DER_Data)
484{
485	uint32		rtn = 0;
486	unsigned	i = 0;
487
488	while(i < DER_Data->Length) {
489		rtn |= DER_Data->Data[i];
490		if(++i == DER_Data->Length) {
491			break;
492		}
493		rtn <<= 8;
494	}
495	return rtn;
496}
497
498/*
499 * Log CSSM error.
500 */
501void srPrintError(const char *op, CSSM_RETURN err)
502{
503	cssmPerror(op, err);
504}
505
506/*
507 * Convert a CFString into a C string as safely as we can. Caller must
508 * free the result.
509 */
510char *srCfStrToCString(
511	CFStringRef cfStr)
512{
513	CFIndex len = CFStringGetLength(cfStr) + 1;
514	char *cstr = (char *)malloc(len);
515	if(cstr == NULL) {
516		return NULL;
517	}
518	if(!CFStringGetCString(cfStr, cstr, len, kCFStringEncodingASCII)) {
519		printf("***CFStringGetCString error\n");
520		free(cstr);
521		return NULL;
522	}
523	return cstr;
524}
525
526