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