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// AppleCSP.cpp - top-level plugin and session implementation
21//
22#include "AppleCSP.h"
23#include "AppleCSPSession.h"
24#include "AppleCSPUtils.h"
25#include <stdio.h>
26#include "cspdebugging.h"
27#include <security_cdsa_plugin/CSPsession.h>
28#include <security_utilities/alloc.h>
29#ifdef	BSAFE_CSP_ENABLE
30#include "bsafecsp.h"
31#include "bsafecspi.h"
32#endif
33#ifdef	CRYPTKIT_CSP_ENABLE
34#include "cryptkitcsp.h"
35#include "FEEKeys.h"
36#endif
37#include <miscAlgFactory.h>
38#ifdef	ASC_CSP_ENABLE
39#include "ascFactory.h"
40#endif
41#include <RSA_DSA_csp.h>
42#include <RSA_DSA_keys.h>
43#include <DH_csp.h>
44#include <DH_keys.h>
45
46#include "YarrowConnection.h"
47
48//
49// Make and break the plugin object
50//
51AppleCSPPlugin::AppleCSPPlugin() :
52	normAllocator(Allocator::standard(Allocator::normal)),
53	privAllocator(Allocator::standard(Allocator::sensitive)),
54	#ifdef	BSAFE_CSP_ENABLE
55	bSafe4Factory(new BSafeFactory(&normAllocator, &privAllocator)),
56	#endif
57	#ifdef	CRYPTKIT_CSP_ENABLE
58	cryptKitFactory(new CryptKitFactory(&normAllocator, &privAllocator)),
59	#endif
60	miscAlgFactory(new MiscAlgFactory(&normAllocator, &privAllocator)),
61	#ifdef	ASC_CSP_ENABLE
62	ascAlgFactory(new AscAlgFactory(&normAllocator, &privAllocator)),
63	#endif
64	rsaDsaAlgFactory(new RSA_DSA_Factory(&normAllocator, &privAllocator)),
65	dhAlgFactory(new DH_Factory(&normAllocator, &privAllocator))
66{
67	// misc. once-per-address-space cruft...
68}
69
70AppleCSPPlugin::~AppleCSPPlugin()
71{
72	#ifdef	BSAFE_CSP_ENABLE
73	delete bSafe4Factory;
74	#endif
75	#ifdef	CRYPTKIT_CSP_ENABLE
76	delete cryptKitFactory;
77	#endif
78	delete miscAlgFactory;
79	#ifdef	ASC_CSP_ENABLE
80	delete ascAlgFactory;
81	#endif
82	delete rsaDsaAlgFactory;
83	delete dhAlgFactory;
84}
85
86
87//
88// Create a new plugin session, our way
89//
90PluginSession *AppleCSPPlugin::makeSession(
91	CSSM_MODULE_HANDLE handle,
92    const CSSM_VERSION &version,
93    uint32 subserviceId,
94    CSSM_SERVICE_TYPE subserviceType,
95    CSSM_ATTACH_FLAGS attachFlags,
96    const CSSM_UPCALLS &upcalls)
97{
98    switch (subserviceType) {
99        case CSSM_SERVICE_CSP:
100            return new AppleCSPSession(handle,
101				*this,
102				version,
103				subserviceId,
104				subserviceType,
105				attachFlags,
106				upcalls);
107        default:
108            CssmError::throwMe(CSSMERR_CSSM_INVALID_SERVICE_MASK);
109            return 0;	// placebo
110    }
111}
112
113
114//
115// Session constructor
116//
117AppleCSPSession::AppleCSPSession(
118	CSSM_MODULE_HANDLE handle,
119	AppleCSPPlugin &plug,
120	const CSSM_VERSION &version,
121	uint32 subserviceId,
122	CSSM_SERVICE_TYPE subserviceType,
123	CSSM_ATTACH_FLAGS attachFlags,
124	const CSSM_UPCALLS &upcalls)
125		: CSPFullPluginSession(handle,
126			plug,
127			version,
128			subserviceId,
129			subserviceType,
130			attachFlags,
131			upcalls),
132		#ifdef	BSAFE_CSP_ENABLE
133		bSafe4Factory(*(dynamic_cast<BSafeFactory *>(plug.bSafe4Factory))),
134		#endif
135		#ifdef	CRYPTKIT_CSP_ENABLE
136		cryptKitFactory(*(dynamic_cast<CryptKitFactory *>(plug.cryptKitFactory))),
137		#endif
138		miscAlgFactory(*(dynamic_cast<MiscAlgFactory *>(plug.miscAlgFactory))),
139		#ifdef	ASC_CSP_ENABLE
140		ascAlgFactory(*(dynamic_cast<AscAlgFactory *>(plug.ascAlgFactory))),
141		#endif
142		rsaDsaAlgFactory(*(dynamic_cast<RSA_DSA_Factory *>(plug.rsaDsaAlgFactory))),
143		dhAlgFactory(*(dynamic_cast<DH_Factory *>(plug.dhAlgFactory))),
144		normAllocator(*this),
145		privAllocator(plug.privAlloc())
146{
147	// anything?
148}
149
150AppleCSPSession::~AppleCSPSession()
151{
152	// anything?
153}
154
155//
156// Called at (CSSM) context create time. This is ignored; we do a full
157// context setup later, at setupContext time.
158//
159CSPFullPluginSession::CSPContext *
160AppleCSPSession::contextCreate(
161	CSSM_CC_HANDLE handle,
162	const Context &context)
163{
164	return NULL;
165}
166
167//
168// Called by CSPFullPluginSession when an op is actually commencing.
169// Context can safely assumed to be fully formed and stable for the
170// duration of the  op; thus we wait until now to set up our
171// CSPContext as appropriate to the op.
172//
173void AppleCSPSession::setupContext(
174	CSPContext * &cspCtx,
175	const Context &context,
176	bool encoding)
177{
178	/*
179	 * Note we leave the decision as to whether it's OK to
180	 * reuse a context to the individual factories.
181	 */
182	#ifdef	BSAFE_CSP_ENABLE
183	/* Give BSAFE the firsrt shot if it's present */
184	if (bSafe4Factory.setup(*this, cspCtx, context)) {
185		CASSERT(cspCtx != NULL);
186		return;
187	}
188	#endif
189	if (rsaDsaAlgFactory.setup(*this, cspCtx, context)) {
190		CASSERT(cspCtx != NULL);
191		return;
192	}
193	if (miscAlgFactory.setup(*this, cspCtx, context)) {
194		CASSERT(cspCtx != NULL);
195		return;
196	}
197	if (dhAlgFactory.setup(*this, cspCtx, context)) {
198		CASSERT(cspCtx != NULL);
199		return;
200	}
201	#ifdef	CRYPTKIT_CSP_ENABLE
202	if (cryptKitFactory.setup(*this, cspCtx, context)) {
203		CASSERT(cspCtx != NULL);
204		return;
205	}
206	#endif
207	#ifdef	ASC_CSP_ENABLE
208	if (ascAlgFactory.setup(*this, cspCtx, context)) {
209		CASSERT(cspCtx != NULL);
210		return;
211	}
212	#endif
213	if(setup(cspCtx, context)) {
214		CASSERT(cspCtx != NULL);
215		return;
216	}
217	dprintf0("AppleCSPSession::setupContext: invalid algorithm\n");
218	CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
219}
220
221/*
222 * Used for generating crypto contexts at this level.
223 * Analogous to AlgorithmFactory.setup().
224 */
225bool AppleCSPSession::setup(
226	CSPFullPluginSession::CSPContext * &cspCtx,
227	const Context &context)
228{
229	if (cspCtx) {
230		return false;	// not ours or already set
231	}
232
233	switch(context.type()) {
234		case CSSM_ALGCLASS_RANDOMGEN:
235			switch (context.algorithm()) {
236				case CSSM_ALGID_APPLE_YARROW:
237					cspCtx = new YarrowContext(*this);
238					return true;
239				/* other random algs here */
240				default:
241					return false;
242			}
243		/* other contexts here */
244		default:
245			return false;
246	}
247	/* NOT REACHED */
248	return false;
249
250}
251
252//
253// Context for CSSM_ALGID_APPLE_YARROW.
254//
255YarrowContext::YarrowContext(AppleCSPSession &session)
256	: AppleCSPContext(session)
257{
258	// nothing for now
259}
260
261YarrowContext::~YarrowContext()
262{
263	// nothing for now
264}
265
266//
267// Only job here is to snag the length and process the optional seed argument
268//
269void YarrowContext::init(
270	const Context &context,
271	bool encoding)
272{
273	/* stash requested length for use later in outputSize() */
274	outSize = context.getInt(CSSM_ATTRIBUTE_OUTPUT_SIZE,
275		CSSMERR_CSP_INVALID_ATTR_OUTPUT_SIZE);
276
277	/* optional seed */
278	CssmCryptoData *cseed = context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED);
279	if(cseed == NULL) {
280		/* we're done */
281		return;
282	}
283	CssmData seed = (*cseed)();
284	if((seed.Length == 0) ||
285	   (seed.Data == NULL)) {
286		CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED);
287	}
288	session().addEntropy((size_t)seed.Length, seed.Data);
289}
290
291void YarrowContext::final(
292	CssmData &out)
293{
294	session().getRandomBytes((size_t)out.Length, out.Data);
295}
296
297/***
298 *** Binary Key support.
299 ***/
300
301// Given a CSSM_DATA, extract its KeyRef.
302static KeyRef CssmDataToKeyRef(
303	const CSSM_DATA &data)
304{
305	if(data.Length != sizeof(KeyRef)) {
306		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
307	}
308
309	uint8 *cp = data.Data + sizeof(KeyRef) - 1;
310	KeyRef keyRef = 0;
311	for(unsigned dex=0; dex<sizeof(KeyRef); dex++) {
312		keyRef <<= 8;
313		keyRef |= *cp--;
314	}
315	return keyRef;
316}
317
318// Place a KeyRef into a CSSM_DATA, mallocing if necessary.
319static void keyRefToCssmData(
320	KeyRef			keyRef,
321	CSSM_DATA		&data,
322	Allocator	&allocator)
323{
324	if(data.Length > sizeof(keyRef)) {
325		/* don't leave old raw key material lying around */
326		memset(data.Data + sizeof(keyRef), 0, data.Length - sizeof(keyRef));
327	}
328	else if(data.Length < sizeof(keyRef)) {
329		/* not enough space for even a keyRef, force realloc */
330		allocator.free(data.Data);
331		data.Data = NULL;
332		data.Length = 0;
333	}
334	setUpData(data, sizeof(keyRef), allocator);
335
336	uint8 *cp = data.Data;
337	for(unsigned i=0; i<sizeof(keyRef); i++) {
338		*cp++ = keyRef & 0xff;
339		keyRef >>= 8;
340	}
341}
342
343// Look up a BinaryKey by its KeyRef. Returns NULL if not
344// found. refKeyMapLock held on entry and exit.
345BinaryKey *AppleCSPSession::lookupKeyRef(
346	KeyRef	keyRef)
347{
348	const BinaryKey *binKey;
349
350	// use safe version, don't create new entry if this key
351	// isn't there
352	keyMap::iterator it = refKeyMap.find(keyRef);
353	if(it == refKeyMap.end()) {
354		return NULL;
355	}
356	binKey = it->second;
357	assert(binKey == reinterpret_cast<const BinaryKey *>(keyRef));
358	assert(binKey->mKeyRef == keyRef);
359	return const_cast<BinaryKey *>(binKey);
360}
361
362// add a BinaryKey to our refKeyMap. Sets up cssmKey
363// as appropriate.
364void AppleCSPSession::addRefKey(
365	BinaryKey	&binKey,
366	CssmKey		&cssmKey)
367{
368	// for now, KeyRef is just the address of the BinaryKey
369	KeyRef			keyRef = reinterpret_cast<KeyRef>(&binKey);
370
371	binKey.mKeyRef = keyRef;
372	binKey.mKeyHeader = CssmKey::Header::overlay(cssmKey.KeyHeader);
373	{
374		StLock<Mutex> _(refKeyMapLock);
375		assert(lookupKeyRef(keyRef) == NULL);
376		refKeyMap[keyRef] = &binKey;
377	}
378	cssmKey.KeyHeader.BlobType = CSSM_KEYBLOB_REFERENCE;
379	cssmKey.KeyHeader.Format = CSSM_KEYBLOB_REF_FORMAT_INTEGER;
380	keyRefToCssmData(keyRef, cssmKey.KeyData, normAllocator);
381	secdebug("freeKey", "CSP addRefKey key %p keyData %p keyRef %p",
382		&cssmKey, cssmKey.KeyData.Data, &binKey);
383}
384
385// Given a CssmKey in reference form, obtain the associated
386// BinaryKey. Throws CSSMERR_CSP_INVALID_KEY_REFERENCE if
387// key not found in session key map.
388BinaryKey & AppleCSPSession::lookupRefKey(
389	const CssmKey		&cssmKey)
390{
391	KeyRef 		keyRef;
392	BinaryKey	*binKey;
393
394	keyRef = CssmDataToKeyRef(cssmKey.KeyData);
395	{
396		StLock<Mutex> _(refKeyMapLock);
397		binKey = lookupKeyRef(keyRef);
398	}
399	if(binKey == NULL) {
400		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
401	}
402	assert(Guid::overlay(binKey->mKeyHeader.CspId) == plugin.myGuid());
403
404	/*
405	 * Verify sensitive fields have not changed between when the BinaryKey was
406	 * created/stored and when the caller passed in the ref key.
407	 * Some fields were changed by addRefKey, so make a local copy....
408	 */
409	CSSM_KEYHEADER localHdr = cssmKey.KeyHeader;
410	localHdr.BlobType = binKey->mKeyHeader.BlobType;
411	localHdr.Format = binKey->mKeyHeader.Format;
412	if(memcmp(&localHdr, &binKey->mKeyHeader, sizeof(CSSM_KEYHEADER))) {
413		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_REFERENCE);
414	}
415	return (*binKey);
416}
417
418// CSPFullPluginSession declares & implements this.
419// Note that we ignore the delete argument; since we don't
420// store anything, freeing is the same as deleting.
421void AppleCSPSession::FreeKey(
422	const AccessCredentials *AccessCred,
423	CssmKey &KeyPtr,
424	CSSM_BOOL Delete)
425{
426
427	if((KeyPtr.blobType() == CSSM_KEYBLOB_REFERENCE) &&
428	   (KeyPtr.cspGuid() == plugin.myGuid())) {
429		// it's a ref key we generated - delete associated BinaryKey
430		KeyRef keyRef = CssmDataToKeyRef(KeyPtr.KeyData);
431		{
432			StLock<Mutex> _(refKeyMapLock);
433			BinaryKey *binKey = lookupKeyRef(keyRef);
434			if(binKey != NULL) {
435				secdebug("freeKey", "CSP FreeKey key %p keyData %p binKey %p",
436					&KeyPtr, KeyPtr.KeyData.Data, binKey);
437				try {
438					refKeyMap.erase(keyRef);
439					delete binKey;
440				}
441				catch (...)  {
442					errorLog0("Error deleting/erasing known "
443							"ref key\n");
444				}
445			}
446			else {
447				secdebug("freeKey", "CSP freeKey unknown key");
448			}
449		}
450	}
451	CSPFullPluginSession::FreeKey(AccessCred, KeyPtr, Delete);
452}
453
454/* Passthrough, used for key digest */
455void AppleCSPSession::PassThrough(
456	CSSM_CC_HANDLE CCHandle,
457	const Context &Context,
458	uint32 PassThroughId,
459	const void *InData,
460	void **OutData)
461{
462	*OutData = NULL;
463
464	/* validate context */
465	if(Context.type() != CSSM_ALGCLASS_NONE) {
466		CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
467	}
468
469	switch(PassThroughId) {
470		case CSSM_APPLECSP_KEYDIGEST:
471		{
472			CssmKey &key = Context.get<CssmKey>(
473				CSSM_ATTRIBUTE_KEY,
474				CSSMERR_CSP_MISSING_ATTR_KEY);
475
476			/* validate key as best we can */
477			switch(key.keyClass()) {
478				case CSSM_KEYCLASS_PUBLIC_KEY:
479				case CSSM_KEYCLASS_PRIVATE_KEY:
480				case CSSM_KEYCLASS_SESSION_KEY:
481					break;
482				default:
483					CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
484			}
485
486			/*
487			 * Ref key: obtain binary, ask it for blob
488			 * Raw key: get info provider, ask it for the blob. This
489			 *          allows for an optimized path which avoids
490			 *		    converting to a BinaryKey.
491			 */
492			CssmData blobToHash;
493			switch(key.blobType()) {
494				case CSSM_KEYBLOB_RAW:
495				{
496					CSPKeyInfoProvider *provider = infoProvider(key);
497					bool converted =
498						provider->getHashableBlob(privAllocator, blobToHash);
499					if(converted) {
500						/* took optimized case; proceed */
501						delete provider;
502						break;
503					}
504
505					/* convert to BinaryKey and ask it to do the work */
506					BinaryKey *binKey;
507					CSSM_KEYATTR_FLAGS flags = 0;	// not used
508					provider->CssmKeyToBinary(NULL,	// no paramKey
509						flags,
510						&binKey);
511					binKey->mKeyHeader =
512						CssmKey::Header::overlay(key.KeyHeader);
513					CSSM_KEYBLOB_FORMAT rawFormat;
514					rawFormat = CSSM_KEYBLOB_RAW_FORMAT_DIGEST;
515					CSSM_KEYATTR_FLAGS	attrFlags = 0;
516					binKey->generateKeyBlob(privAllocator,
517							blobToHash,
518							rawFormat,
519							*this,
520							NULL,
521							attrFlags);
522					delete binKey;
523					delete provider;
524					break;
525				}
526				case CSSM_KEYBLOB_REFERENCE:
527					{
528						BinaryKey &binKey = lookupRefKey(key);
529						CSSM_KEYBLOB_FORMAT rawFormat;
530						rawFormat = CSSM_KEYBLOB_RAW_FORMAT_DIGEST;
531						CSSM_KEYATTR_FLAGS attrFlags = 0;
532						binKey.generateKeyBlob(privAllocator,
533							blobToHash,
534							rawFormat,
535							*this,
536							NULL,
537							attrFlags);
538					}
539					break;
540				default:
541					CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
542			}
543
544			/* obtain sha1 hash of blobToHash */
545
546			CSSM_DATA_PTR outHash = NULL;
547			try {
548				outHash =
549					(CSSM_DATA_PTR)normAllocator.malloc(sizeof(CSSM_DATA));
550				outHash->Data =
551					(uint8 *)normAllocator.malloc(SHA1_DIGEST_SIZE);
552				outHash->Length = SHA1_DIGEST_SIZE;
553			}
554			catch(...) {
555				freeCssmData(blobToHash, privAllocator);
556				throw;
557			}
558			cspGenSha1Hash(blobToHash.data(), blobToHash.length(),
559				outHash->Data);
560			freeCssmData(blobToHash, privAllocator);
561			*OutData = outHash;
562			return;
563		}
564		default:
565			CssmError::throwMe(CSSMERR_CSP_INVALID_PASSTHROUGH_ID);
566	}
567	/* NOT REACHED */
568}
569
570/*
571 * CSPSession version of QueryKeySizeInBits.
572 */
573void AppleCSPSession::getKeySize(const CssmKey &key,
574	CSSM_KEY_SIZE &size)
575{
576	CSPKeyInfoProvider *provider = infoProvider(key);
577	try {
578		provider->QueryKeySizeInBits(size);
579	}
580	catch(...) {
581		/* don't leak this on error */
582		delete provider;
583		throw;
584	}
585	delete provider;
586}
587
588void AppleCSPSession::getRandomBytes(size_t length, uint8 *cp)
589{
590	try {
591		cspGetRandomBytes(cp, (unsigned)length);
592	}
593	catch(...) {
594		errorLog0("CSP: YarrowClient failure\n");
595	}
596}
597
598void AppleCSPSession::addEntropy(size_t length, const uint8 *cp)
599{
600	try {
601		cspAddEntropy(cp, (unsigned)length);
602	}
603	catch(...) {
604		#if		CSP_ALLOW_FEE_RNG
605		return;
606		#else
607		throw;
608		#endif
609	}
610}
611
612/***
613 *** CSPKeyInfoProvider support.
614 ***/
615
616/*
617 * Find a CSPKeyInfoProvider subclass for the specified key.
618 */
619CSPKeyInfoProvider *AppleCSPSession::infoProvider(
620	const CssmKey	&key)
621{
622	CSPKeyInfoProvider *provider = NULL;
623
624	#ifdef	BSAFE_CSP_ENABLE
625	/* Give BSAFE first shot, if it's here */
626	provider = BSafe::BSafeKeyInfoProvider::provider(key, *this);
627	if(provider != NULL) {
628		return provider;
629	}
630	#endif
631
632	provider = RSAKeyInfoProvider::provider(key, *this);
633	if(provider != NULL) {
634		return provider;
635	}
636
637	provider = SymmetricKeyInfoProvider::provider(key, *this);
638	if(provider != NULL) {
639		return provider;
640	}
641
642	#ifdef	CRYPTKIT_CSP_ENABLE
643	provider = CryptKit::FEEKeyInfoProvider::provider(key, *this);
644	if(provider != NULL) {
645		return provider;
646	}
647	#endif
648
649	provider = DSAKeyInfoProvider::provider(key, *this);
650	if(provider != NULL) {
651		return provider;
652	}
653
654	provider = DHKeyInfoProvider::provider(key, *this);
655	if(provider != NULL) {
656		return provider;
657	}
658
659	CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
660}
661
662