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 * FEEKeys.cpp - FEE-related asymmetric key pair classes.
21 *
22 */
23
24#ifdef	CRYPTKIT_CSP_ENABLE
25
26#include "FEEKeys.h"
27#include "FEECSPUtils.h"
28#include "CryptKitSpace.h"
29#include <security_cryptkit/feePublicKey.h>
30#include <security_cryptkit/falloc.h>
31#include <security_cdsa_utilities/cssmdata.h>
32#include "AppleCSPSession.h"
33#include "AppleCSPUtils.h"
34#include <assert.h>
35#include <security_utilities/debugging.h>
36
37#define feeKeyDebug(args...)	secdebug("feeKey", ## args)
38
39/***
40 *** FEE-style BinaryKey
41 ***/
42
43/* constructor with optional existing feePubKey */
44CryptKit::FEEBinaryKey::FEEBinaryKey(feePubKey feeKey)
45	: mFeeKey(feeKey)
46{
47	if(mFeeKey == NULL) {
48		mFeeKey = feePubKeyAlloc();
49		if(mFeeKey == NULL) {
50			CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
51		}
52	}
53}
54
55CryptKit::FEEBinaryKey::~FEEBinaryKey()
56{
57	if(mFeeKey) {
58		feePubKeyFree(mFeeKey);
59		mFeeKey = NULL;
60	}
61}
62
63void CryptKit::FEEBinaryKey::generateKeyBlob(
64		Allocator			&allocator,
65		CssmData			&blob,
66		CSSM_KEYBLOB_FORMAT	&format,
67		AppleCSPSession		&session,
68		const CssmKey		*paramKey,	/* optional, unused here */
69		CSSM_KEYATTR_FLAGS 	&attrFlags)	/* IN/OUT */
70{
71	unsigned char 	*keyBlob;
72	unsigned 		len;
73	feeReturn		frtn = FR_Internal;
74	bool			freeTheKey = false;
75	feePubKey		keyToEncode = mFeeKey;
76
77	assert(mFeeKey != NULL);
78	if((format == CSSM_KEYBLOB_RAW_FORMAT_DIGEST) &&
79	   (mKeyHeader.KeyClass == CSSM_KEYCLASS_PRIVATE_KEY)) {
80		/* key digest calculation; special case for private keys: cook
81		 * up the associated public key and encode that */
82		keyToEncode = feePubKeyAlloc();
83		if(keyToEncode == NULL) {
84			CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
85		}
86		frtn = feePubKeyInitPubKeyFromPriv(mFeeKey, keyToEncode);
87		if(frtn) {
88			feePubKeyFree(keyToEncode);
89			throwCryptKit(frtn, "feePubKeyInitPubKeyFromPriv");
90		}
91		freeTheKey = true;
92	}
93
94	bool badFormat = false;
95	int isPrivate = feePubKeyIsPrivate(keyToEncode);
96
97	switch(mKeyHeader.AlgorithmId) {
98		case CSSM_ALGID_FEE:
99			if(isPrivate) {
100				/* FEE private key */
101				switch(format) {
102					case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
103						format = CSSM_KEYBLOB_RAW_FORMAT_NONE;
104						/* and drop thru */
105					case CSSM_KEYBLOB_RAW_FORMAT_NONE:
106						frtn = feePubKeyCreateDERPrivBlob(keyToEncode, &keyBlob, &len);
107						break;
108					case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING:
109						frtn = feePubKeyCreatePrivBlob(keyToEncode, &keyBlob, &len);
110						break;
111					default:
112						badFormat = true;
113						break;
114				}
115			}
116			else {
117				/* FEE Public key */
118				switch(format) {
119					case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
120						format = CSSM_KEYBLOB_RAW_FORMAT_NONE;
121						/* and drop thru */
122					case CSSM_KEYBLOB_RAW_FORMAT_NONE:
123						frtn = feePubKeyCreateDERPubBlob(keyToEncode, &keyBlob, &len);
124						break;
125					case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING:
126						frtn = feePubKeyCreatePubBlob(keyToEncode, &keyBlob, &len);
127						break;
128					default:
129						badFormat = true;
130						break;
131				}
132			}
133			/* end of base ALGID_FEE */
134			break;
135
136		case CSSM_ALGID_ECDSA:
137			if(isPrivate) {
138				/* ECDSA/ECDH private key */
139				switch(format) {
140					case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
141						/* ECDSA private key: PKCS8 */
142						frtn = feePubKeyCreatePKCS8Blob(keyToEncode, &keyBlob, &len);
143						break;
144					case CSSM_KEYBLOB_RAW_FORMAT_NONE:
145						/* set to default format, drop thru */
146						format = CSSM_KEYBLOB_RAW_FORMAT_OPENSSL;
147					case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL:
148						/* ECDSA private key, SEC1/OpenSSL format */
149						frtn = feePubKeyCreateOpenSSLBlob(keyToEncode, &keyBlob, &len);
150						break;
151					case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
152						format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
153						/* and drop thru */
154					case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING:
155						/* raw private key bytes */
156						frtn = feeCreateECDSAPrivBlob(keyToEncode, &keyBlob, &len);
157						break;
158					default:
159						badFormat = true;
160						break;
161				}
162			}
163			else {
164				/*
165				 * ECDSA public key.
166				 * Note there is no OpenSSL case here, that format is only generated for
167				 * private keys.
168				 */
169				switch(format) {
170					case CSSM_KEYBLOB_RAW_FORMAT_NONE:
171						/* set to default format, drop thru */
172						format = CSSM_KEYBLOB_RAW_FORMAT_X509;
173					case CSSM_KEYBLOB_RAW_FORMAT_X509:
174						/* ECDSA, public key, default: X509 */
175						frtn = feePubKeyCreateX509Blob(keyToEncode, &keyBlob, &len);
176						break;
177					case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
178						format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
179						/* and drop thru */
180					case CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING:
181						/* raw x|y string */
182						frtn = feeCreateECDSAPubBlob(keyToEncode, &keyBlob, &len);
183						break;
184					default:
185						badFormat = true;
186						break;
187				}
188			}
189			/* end of case CSSM_ALGID_ECDSA */
190			break;
191		default:
192			/* not reached */
193			break;
194	}
195
196	if(badFormat) {
197		CssmError::throwMe(isPrivate ?
198			CSSMERR_CSP_INVALID_ATTR_PRIVATE_KEY_FORMAT :
199			CSSMERR_CSP_INVALID_ATTR_PUBLIC_KEY_FORMAT);
200	}
201	if(frtn) {
202		throwCryptKit(frtn, "feePubKeyCreate*Blob");
203	}
204	setUpCssmData(blob, len, allocator);
205	memmove(blob.data(), keyBlob, len);
206	blob.length(len);
207	ffree(keyBlob);
208	if(freeTheKey) {
209		/* free the temp pub key we created here */
210		feePubKeyFree(keyToEncode);
211	}
212}
213
214/***
215 *** FEE-style AppleKeyPairGenContext
216 ***/
217
218/*
219 * This one is specified in, and called from, CSPFullPluginSession. Our
220 * only job is to prepare two subclass-specific BinaryKeys and call up to
221 * AppleKeyPairGenContext.
222 */
223void CryptKit::FEEKeyPairGenContext::generate(
224	const Context 	&context,
225	CssmKey 		&pubKey,
226	CssmKey 		&privKey)
227{
228	FEEBinaryKey *pubBinKey  = new FEEBinaryKey();
229	FEEBinaryKey *privBinKey = new FEEBinaryKey();
230
231	try {
232		AppleKeyPairGenContext::generate(context,
233			session(),
234			pubKey,
235			pubBinKey,
236			privKey,
237			privBinKey);
238	}
239	catch (...) {
240		delete pubBinKey;
241		delete privBinKey;
242		throw;
243	}
244
245}
246
247// this one is specified in, and called from, AppleKeyPairGenContext
248void CryptKit::FEEKeyPairGenContext::generate(
249	const Context 	&context,
250	BinaryKey		&pubBinKey,
251	BinaryKey		&privBinKey,
252	uint32			&keyBits)
253{
254	/*
255	 * These casts throw exceptions if the keys are of the
256	 * wrong classes, which would be a major bogon, since we created
257	 * the keys in the above generate() function.
258	 */
259	FEEBinaryKey &fPubBinKey =
260		dynamic_cast<FEEBinaryKey &>(pubBinKey);
261	FEEBinaryKey &fPrivBinKey =
262		dynamic_cast<FEEBinaryKey &>(privBinKey);
263
264	/*
265	 * Two parameters from context. Key size in bits is required;
266	 * seed is optional. If not present, we cook up random private data.
267	 */
268	keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
269				CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
270	CssmCryptoData *cseed = context.get<CssmCryptoData>(CSSM_ATTRIBUTE_SEED);
271	CssmData *seed;
272	bool haveSeed;
273	CssmAutoData aSeed(session());		// malloc on demand
274	if(cseed) {
275		/* caller specified seed */
276		haveSeed = true;
277		seed = &cseed->param();
278	}
279	else {
280		/* generate random seed */
281		haveSeed = false;
282		unsigned keyBytes = ((keyBits + 7) / 8) + 1;
283		aSeed.malloc(keyBytes);
284		session().getRandomBytes(keyBytes, aSeed);
285		seed = &aSeed.get();
286	}
287
288	CSSM_ALGORITHMS algId = context.algorithm();
289
290	/* Curve and prime types - optional */
291	feePrimeType primeType = FPT_Default;
292	uint32 uPrimeType = context.getInt(CSSM_ATTRIBUTE_FEE_PRIME_TYPE);
293	switch(uPrimeType) {
294		case CSSM_FEE_PRIME_TYPE_DEFAULT:
295			break;
296		case CSSM_FEE_PRIME_TYPE_MERSENNE:
297			primeType = FPT_Mersenne;
298			break;
299		case CSSM_FEE_PRIME_TYPE_FEE:
300			primeType = FPT_FEE;
301			break;
302		case CSSM_FEE_PRIME_TYPE_GENERAL:
303			primeType = FPT_General;
304			break;
305		default:
306			/* FIXME - maybe we should be more specific */
307			CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
308	}
309	feeCurveType curveType = FCT_Default;
310	switch(algId) {
311		case CSSM_ALGID_ECDSA:
312			/* no options */
313			curveType = FCT_ANSI;
314			break;
315		default:
316		{
317			uint32 uCurveType = context.getInt(CSSM_ATTRIBUTE_FEE_CURVE_TYPE);
318			switch(uCurveType) {
319				case CSSM_FEE_CURVE_TYPE_DEFAULT:
320					break;
321				case CSSM_FEE_CURVE_TYPE_MONTGOMERY:
322					curveType = FCT_Montgomery;
323					break;
324				case CSSM_FEE_CURVE_TYPE_WEIERSTRASS:
325					curveType = FCT_Weierstrass;
326					break;
327				case CSSM_FEE_CURVE_TYPE_ANSI_X9_62:
328					curveType = FCT_ANSI;
329					break;
330				default:
331					/* FIXME - maybe we should be more specific */
332					CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
333			}
334			break;
335		}
336	}
337	feeReturn frtn = feePubKeyInitFromPrivDataKeyBits(
338		fPrivBinKey.feeKey(),
339		(unsigned char *)seed->data(),
340		(unsigned int)seed->length(),
341		keyBits,
342		primeType,
343		curveType,
344		/*
345		 * our random seed: trust it
346		 * caller's seed: hash it
347		 */
348		haveSeed ? 1 : 0);
349	if(frtn) {
350		throwCryptKit(frtn, "feePubKeyInitFromPrivDataKeyBits");
351	}
352	frtn = feePubKeyInitPubKeyFromPriv(fPrivBinKey.feeKey(),
353		fPubBinKey.feeKey());
354	if(frtn) {
355		throwCryptKit(frtn, "feePubKeyInitPubKeyFromPriv");
356	}
357}
358
359
360/***
361 *** FEE-style CSPKeyInfoProvider.
362 ***/
363CryptKit::FEEKeyInfoProvider::FEEKeyInfoProvider(
364	const CssmKey 	&cssmKey,
365	AppleCSPSession	&session) :
366		CSPKeyInfoProvider(cssmKey, session)
367{
368}
369CSPKeyInfoProvider *FEEKeyInfoProvider::provider(
370	const CssmKey 	&cssmKey,
371	AppleCSPSession	&session)
372{
373	switch(cssmKey.algorithm()) {
374		case CSSM_ALGID_FEE:
375		case CSSM_ALGID_ECDSA:
376			break;
377		default:
378			return NULL;
379	}
380	switch(cssmKey.keyClass()) {
381		case CSSM_KEYCLASS_PUBLIC_KEY:
382		case CSSM_KEYCLASS_PRIVATE_KEY:
383			/* FIXME - verify proper CSSM_KEYBLOB_RAW_FORMAT_xx */
384			break;
385		default:
386			return NULL;
387	}
388	/* OK, we'll handle this one */
389	return new FEEKeyInfoProvider(cssmKey, session);
390}
391
392/* Given a raw key, cook up a Binary key */
393void CryptKit::FEEKeyInfoProvider::CssmKeyToBinary(
394	CssmKey				*paramKey,		// optional, ignored
395	CSSM_KEYATTR_FLAGS	&attrFlags,		// IN/OUT
396	BinaryKey 			**binKey)
397{
398	*binKey = NULL;
399	feePubKey feeKey = NULL;
400
401	/* first cook up a feePubKey, then drop that into a BinaryKey */
402	feeKey = rawCssmKeyToFee(mKey);
403	FEEBinaryKey *feeBinKey = new FEEBinaryKey(feeKey);
404	*binKey = feeBinKey;
405}
406
407/*
408 * Obtain key size in bits.
409 * Currently only raw public keys are dealt with (they're the ones
410 * which come from certs, the only current use for this function).
411 * Note that if we need to handle ref keys, we'll need a session ref...
412 */
413void CryptKit::FEEKeyInfoProvider::QueryKeySizeInBits(
414	CSSM_KEY_SIZE &keySize)
415{
416	feePubKey feeKey = NULL;
417
418	if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
419		CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
420	}
421	feeKey = rawCssmKeyToFee(mKey);
422	keySize.LogicalKeySizeInBits = feePubKeyBitsize(feeKey);
423	keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits;
424	feePubKeyFree(feeKey);
425}
426
427/*
428 * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST
429 * passthrough.
430 */
431bool CryptKit::FEEKeyInfoProvider::getHashableBlob(
432	Allocator 	&allocator,
433	CssmData		&blob)			// blob to hash goes here
434{
435	/*
436	 * The optimized case, a raw key in the "proper" format already.
437	 * Currently this is:
438	 * FEE public key in default/NONE form (which happens to be DER)
439	 */
440	assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
441	if((mKey.algorithm() == CSSM_ALGID_FEE) &&
442	   (mKey.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_NONE) &&
443	   (mKey.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY)) {
444		const CssmData &keyBlob = CssmData::overlay(mKey.KeyData);
445		copyCssmData(keyBlob, blob, allocator);
446		return true;
447	}
448
449	/* caller converts to binary and proceeds */
450	return false;
451}
452
453#endif	/* CRYPTKIT_CSP_ENABLE */
454