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 * FEECSPUtils.h - Misc. utility function for FEE/CryptKit CSP.
21 *
22 * Created 2/20/2001 by dmitch.
23 */
24
25#ifdef	CRYPTKIT_CSP_ENABLE
26
27#include <security_utilities/debugging.h>
28#include <security_utilities/logging.h>
29#include "FEECSPUtils.h"
30#include "FEEKeys.h"
31#include <security_cryptkit/feeFunctions.h>
32#include <security_cryptkit/feePublicKey.h>
33
34#define feeMiscDebug(args...)	secdebug("feeMisc", ## args)
35
36/* Given a FEE error, throw appropriate CssmError */
37void CryptKit::throwCryptKit(
38	feeReturn 	frtn,
39	const char	*op)		/* optional */
40{
41	if(op) {
42		Security::Syslog::error("Apple CSP %s: %s", op, feeReturnString(frtn));
43	}
44	switch(frtn) {
45		case FR_Success:
46			return;
47		case FR_BadPubKey:
48		case FR_BadPubKeyString:
49		case FR_IncompatibleKey:
50		case FR_BadKeyBlob:
51			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
52		case FR_IllegalDepth:
53			CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE);
54		case FR_BadSignatureFormat:		/* signature corrupted */
55			CssmError::throwMe(CSSMERR_CSP_INVALID_SIGNATURE);
56		case FR_InvalidSignature:		/* signature intact, but not valid */
57			CssmError::throwMe(CSSMERR_CSP_VERIFY_FAILED);
58		case FR_IllegalArg:			/* illegal argument */
59			CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
60		case FR_BadCipherText:		/* malformed ciphertext */
61		case FR_BadEnc64:			/* bad enc64() format */
62			CssmError::throwMe(CSSMERR_CSP_INVALID_DATA);
63		case FR_Unimplemented:		/* unimplemented function */
64			CssmError::throwMe(CSSMERR_CSP_FUNCTION_NOT_IMPLEMENTED);
65		case FR_Memory:		/* unimplemented function */
66			CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
67		case FR_ShortPrivData:
68			CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_SEED);
69		case FR_IllegalCurve:		/* e.g., ECDSA with Montgomery curve */
70			CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY);
71
72		/* I don't think we should ever see these no matter what the
73		 * caller throws at us */
74		case FR_WrongSignatureType:	/* ElGamal vs. ECDSA */
75		case FR_BadUsageName:		/* bad usageName */
76		case FR_BadCipherFile:
77		case FR_Internal:			/* internal library error */
78			CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
79	}
80}
81
82/*
83 * Given a Context:
84 * -- obtain CSSM key of specified CSSM_ATTRIBUTE_TYPE
85 * -- validate keyClass
86 * -- validate keyUsage
87 * -- convert to feePubKey, allocating the feePubKey if necessary
88 *
89 * Returned key can be of algorithm CSSM_ALGID_ECDSA or CSSM_ALGID_FEE;
90 * caller has to verify proper algorithm for operation.
91 */
92feePubKey CryptKit::contextToFeeKey(
93	const Context 		&context,
94	AppleCSPSession	 	&session,
95	CSSM_ATTRIBUTE_TYPE	attrType,	  // CSSM_ATTRIBUTE_KEY, CSSM_ATTRIBUTE_PUBLIC_KEY
96	CSSM_KEYCLASS		keyClass,	  // CSSM_KEYCLASS_{PUBLIC,PRIVATE}_KEY
97	CSSM_KEYUSE			usage,		  // CSSM_KEYUSE_ENCRYPT, CSSM_KEYUSE_SIGN, etc.
98	bool				&mallocdKey)  // RETURNED
99{
100    CssmKey &cssmKey =
101		context.get<CssmKey>(attrType, CSSMERR_CSP_MISSING_ATTR_KEY);
102	const CSSM_KEYHEADER &hdr = cssmKey.KeyHeader;
103	switch(hdr.AlgorithmId) {
104		case CSSM_ALGID_FEE:
105		case CSSM_ALGID_ECDSA:
106			break;
107		default:
108			CssmError::throwMe(CSSMERR_CSP_ALGID_MISMATCH);
109	}
110	if(hdr.KeyClass != keyClass) {
111		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
112	}
113	cspValidateIntendedKeyUsage(&hdr, usage);
114	cspVerifyKeyTimes(hdr);
115	return cssmKeyToFee(cssmKey, session, mallocdKey);
116}
117
118/*
119 * Convert a CssmKey to a feePubKey. May result in the creation of a new
120 * feePubKey (when cssmKey is a raw key); allocdKey is true in that case
121 * in which case the caller generally has to free the allocd key).
122 */
123feePubKey CryptKit::cssmKeyToFee(
124	const CssmKey	&cssmKey,
125	AppleCSPSession	&session,
126	bool			&allocdKey)	// RETURNED
127{
128	feePubKey feeKey = NULL;
129	allocdKey = false;
130
131	const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
132	switch(hdr->AlgorithmId) {
133		case CSSM_ALGID_FEE:
134		case CSSM_ALGID_ECDSA:
135			break;
136		default:
137			// someone else's key (should never happen)
138			CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
139	}
140	switch(hdr->BlobType) {
141		case CSSM_KEYBLOB_RAW:
142			feeKey = rawCssmKeyToFee(cssmKey);
143			allocdKey = true;
144			break;
145		case CSSM_KEYBLOB_REFERENCE:
146		{
147			BinaryKey &binKey = session.lookupRefKey(cssmKey);
148			FEEBinaryKey *feeBinKey = dynamic_cast<FEEBinaryKey *>(&binKey);
149			/* this cast failing means that this is some other
150			 * kind of binary key */
151			if(feeBinKey == NULL) {
152				feeMiscDebug("CryptKit::cssmKeyToFee: wrong BinaryKey subclass\n");
153				CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
154			}
155			assert(feeBinKey->feeKey() != NULL);
156			feeKey = feeBinKey->feeKey();
157			break;
158		}
159		default:
160			CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
161	}
162	return feeKey;
163}
164
165/*
166 * Convert a raw CssmKey to a newly alloc'd feePubKey.
167 */
168feePubKey CryptKit::rawCssmKeyToFee(
169	const CssmKey	&cssmKey)
170{
171	const CSSM_KEYHEADER *hdr = &cssmKey.KeyHeader;
172	assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
173
174	switch(hdr->AlgorithmId) {
175		case CSSM_ALGID_FEE:
176		case CSSM_ALGID_ECDSA:
177			break;
178		default:
179			// someone else's key (should never happen)
180			CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
181	}
182
183	switch(hdr->KeyClass) {
184		case CSSM_KEYCLASS_PUBLIC_KEY:
185		case CSSM_KEYCLASS_PRIVATE_KEY:
186			break;
187		default:
188			// someone else's key
189			CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
190	}
191
192	feePubKey feeKey = feePubKeyAlloc();
193	if(feeKey == NULL) {
194		CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
195	}
196
197	feeReturn frtn = FR_IllegalArg;
198	bool badFormat = false;
199
200	/*
201	 * The actual key init depends on key type and incoming format
202	 */
203	switch(hdr->AlgorithmId) {
204		case CSSM_ALGID_FEE:
205			switch(hdr->KeyClass) {
206				case CSSM_KEYCLASS_PUBLIC_KEY:
207					switch(hdr->Format) {
208						case FEE_KEYBLOB_DEFAULT_FORMAT:
209							/* FEE, public key, default: custom DER */
210							frtn = feePubKeyInitFromDERPubBlob(feeKey,
211								cssmKey.KeyData.Data,
212								cssmKey.KeyData.Length);
213							break;
214						case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING:
215							/* FEE, public key, native byte stream */
216							frtn = feePubKeyInitFromPubBlob(feeKey,
217								cssmKey.KeyData.Data,
218								(unsigned int)cssmKey.KeyData.Length);
219							break;
220						default:
221							badFormat = true;
222							break;
223					}
224					break;
225				case CSSM_KEYCLASS_PRIVATE_KEY:
226					switch(hdr->Format) {
227						case FEE_KEYBLOB_DEFAULT_FORMAT:
228							/* FEE, private key, default: custom DER */
229							frtn = feePubKeyInitFromDERPrivBlob(feeKey,
230								cssmKey.KeyData.Data,
231								cssmKey.KeyData.Length);
232							break;
233						case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING:
234							/* FEE, private key, native byte stream */
235							frtn = feePubKeyInitFromPrivBlob(feeKey,
236								cssmKey.KeyData.Data,
237								(unsigned int)cssmKey.KeyData.Length);
238							break;
239						default:
240							badFormat = true;
241							break;
242					}
243					break;
244				default:
245					/* not reached, we already checked */
246					break;
247			}
248			/* end of case ALGID_FEE */
249			break;
250
251		case CSSM_ALGID_ECDSA:
252			switch(hdr->KeyClass) {
253				case CSSM_KEYCLASS_PUBLIC_KEY:
254					switch(hdr->Format) {
255						case CSSM_KEYBLOB_RAW_FORMAT_NONE:
256						case CSSM_KEYBLOB_RAW_FORMAT_X509:
257							/* ECDSA, public key, default: X509 */
258							frtn = feePubKeyInitFromX509Blob(feeKey,
259								cssmKey.KeyData.Data,
260								cssmKey.KeyData.Length);
261							break;
262
263						case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
264							/*
265							 * An oddity here: we can parse this incoming key, but
266							 * it contains both private and public parts. We throw
267							 * out the private component here.
268							 */
269							frtn = feePubKeyInitFromOpenSSLBlob(feeKey,
270								1,		/* pubOnly */
271								cssmKey.KeyData.Data,
272								cssmKey.KeyData.Length);
273							break;
274						/*
275						 * NOTE: we cannot *import* a key in raw X9.62 format.
276						 * We'd need to know the curve, i.e., the feeDepth.
277						 * I suppose we could infer that from the blob length but
278						 * a better way would be to have a new context attribute
279						 * specifying which curve.
280						 * For now, imported raw keys have to be in X509 format.
281						 */
282						case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING:
283						default:
284							badFormat = true;
285							break;
286					}
287					break;
288				case CSSM_KEYCLASS_PRIVATE_KEY:
289					switch(hdr->Format) {
290						case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
291							/* ECDSA, private key, PKCS8 */
292							frtn = feePubKeyInitFromPKCS8Blob(feeKey,
293								cssmKey.KeyData.Data,
294								cssmKey.KeyData.Length);
295							break;
296
297						case CSSM_KEYBLOB_RAW_FORMAT_NONE:
298						case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
299							/* ECDSA, private, default: OpenSSL */
300							/* see comment above re: OpenSSL public/private keys */
301							frtn = feePubKeyInitFromOpenSSLBlob(feeKey,
302								0,		/* pubOnly */
303								cssmKey.KeyData.Data,
304								cssmKey.KeyData.Length);
305							break;
306						/* see comment above about X9.62 format public key blobs */
307						case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING:
308						default:
309							badFormat = true;
310							break;
311					}
312					break;
313				default:
314					/* not reached, we already checked */
315					break;
316			}
317			/* end of case CSSM_ALGID_ECDSA */
318			break;
319	}
320	if(badFormat) {
321		CssmError::throwMe(hdr->KeyClass == CSSM_KEYCLASS_PRIVATE_KEY ?
322			CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT :
323			CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT);
324	}
325	if(frtn) {
326		feePubKeyFree(feeKey);
327		throwCryptKit(frtn, "feePubKeyInitFromKeyBlob");
328	}
329	return feeKey;
330}
331
332/*
333 * Glue function which allows C code to use AppleCSPSession
334 * as an RNG. A ptr to this function gets passed down to
335 * CryptKit C functions as a feeRandFcn.
336 */
337feeReturn CryptKit::feeRandCallback(
338	void *ref,					// actually an AppleCSPSession *
339	unsigned char *bytes,		// must be alloc'd by caller
340	unsigned numBytes)
341{
342	AppleCSPSession *session =
343		reinterpret_cast<AppleCSPSession *>(ref);
344	try {
345		session->getRandomBytes(numBytes, bytes);
346	}
347	catch(...) {
348		return FR_Internal;
349	}
350	return FR_Success;
351}
352
353#endif	/* CRYPTKIT_CSP_ENABLE */
354