]> git.saurik.com Git - apple/security.git/blob - AppleCSP/RSA_DSA/RSA_DSA_keys.cpp
Security-54.1.tar.gz
[apple/security.git] / AppleCSP / RSA_DSA / RSA_DSA_keys.cpp
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 * RSA_DSA_Keys.cpp - RSA, DSA related asymmetric key pair classes.
21 */
22
23 #include "RSA_DSA_keys.h"
24 #include <opensslUtils/opensslUtils.h>
25 #include <opensslUtils/openRsaSnacc.h>
26 #include <Security/cssmdata.h>
27 #include <AppleCSP/AppleCSPSession.h>
28 #include <AppleCSP/AppleCSPUtils.h>
29 #include <assert.h>
30 #include <Security/debugging.h>
31 #include "RSA_DSA_utils.h"
32 #include <AppleCSP/YarrowConnection.h>
33 #include <Security/appleoids.h>
34 #include <Security/cdsaUtils.h>
35
36 #define RSA_PUB_EXPONENT 0x10001 /* recommended by RSA */
37
38 #define rsaKeyDebug(args...) debug("rsaKey", ## args)
39
40 /***
41 *** RSA-style BinaryKey
42 ***/
43
44 /* constructor with optional existing RSA key */
45 RSABinaryKey::RSABinaryKey(RSA *rsaKey)
46 : mRsaKey(rsaKey)
47 {
48 }
49
50 RSABinaryKey::~RSABinaryKey()
51 {
52 if(mRsaKey) {
53 RSA_free(mRsaKey);
54 mRsaKey = NULL;
55 }
56 }
57
58 void RSABinaryKey::generateKeyBlob(
59 CssmAllocator &allocator,
60 CssmData &blob,
61 CSSM_KEYBLOB_FORMAT &format)
62 {
63 bool isPub;
64 CSSM_RETURN crtn;
65
66 switch(mKeyHeader.KeyClass) {
67 case CSSM_KEYCLASS_PUBLIC_KEY:
68 isPub = true;
69 format = RSA_PUB_KEY_FORMAT;
70 break;
71 case CSSM_KEYCLASS_PRIVATE_KEY:
72 isPub = false;
73 format = RSA_PRIV_KEY_FORMAT;
74 break;
75 default:
76 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
77 }
78
79 CssmAutoData encodedKey(allocator);
80 if(isPub) {
81 crtn = RSAPublicKeyEncode(mRsaKey, encodedKey);
82 }
83 else {
84 crtn = RSAPrivateKeyEncode(mRsaKey, encodedKey);
85 }
86 if(crtn) {
87 CssmError::throwMe(crtn);
88 }
89 blob = encodedKey.release();
90 }
91
92 /***
93 *** RSA-style AppleKeyPairGenContext
94 ***/
95
96 /*
97 * This one is specified in, and called from, CSPFullPluginSession. Our
98 * only job is to prepare two subclass-specific BinaryKeys and call up to
99 * AppleKeyPairGenContext.
100 */
101 void RSAKeyPairGenContext::generate(
102 const Context &context,
103 CssmKey &pubKey,
104 CssmKey &privKey)
105 {
106 RSABinaryKey *pubBinKey = new RSABinaryKey();
107 RSABinaryKey *privBinKey = new RSABinaryKey();
108
109 try {
110 AppleKeyPairGenContext::generate(context,
111 session(),
112 pubKey,
113 pubBinKey,
114 privKey,
115 privBinKey);
116 }
117 catch (...) {
118 delete pubBinKey;
119 delete privBinKey;
120 throw;
121 }
122
123 }
124
125 // this one is specified in, and called from, AppleKeyPairGenContext
126 void RSAKeyPairGenContext::generate(
127 const Context &context,
128 BinaryKey &pubBinKey,
129 BinaryKey &privBinKey,
130 uint32 &keyBits)
131 {
132 /*
133 * These casts throw exceptions if the keys are of the
134 * wrong classes, which would be a major bogon, since we created
135 * the keys in the above generate() function.
136 */
137 RSABinaryKey &rPubBinKey =
138 dynamic_cast<RSABinaryKey &>(pubBinKey);
139 RSABinaryKey &rPrivBinKey =
140 dynamic_cast<RSABinaryKey &>(privBinKey);
141
142 /*
143 * One parameter from context: Key size in bits is required.
144 * FIXME - get public exponent from context?
145 */
146 keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
147 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
148
149 /* generate the private key */
150 rPrivBinKey.mRsaKey = RSA_generate_key(keyBits,
151 RSA_PUB_EXPONENT,
152 NULL, // no callback
153 NULL);
154 if(rPrivBinKey.mRsaKey == NULL) {
155 rsaKeyDebug("RSA_generate_key returned NULL");
156 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); // ???
157 }
158
159 /* public key is subset of private key */
160 rPubBinKey.mRsaKey = RSA_new();
161 if(rPrivBinKey.mRsaKey == NULL) {
162 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
163 }
164 RSA *pub = rPubBinKey.mRsaKey;
165 RSA *priv = rPrivBinKey.mRsaKey;
166 pub->n = BN_dup(priv->n);
167 pub->e = BN_dup(priv->e);
168 if((pub->n == NULL) || (pub->e == NULL)) {
169 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
170 }
171 }
172
173
174 /***
175 *** RSA-style CSPKeyInfoProvider.
176 ***/
177 RSAKeyInfoProvider::RSAKeyInfoProvider(
178 const CssmKey &cssmKey) :
179 CSPKeyInfoProvider(cssmKey)
180 {
181 }
182
183 CSPKeyInfoProvider *RSAKeyInfoProvider::provider(
184 const CssmKey &cssmKey)
185 {
186 switch(cssmKey.algorithm()) {
187 case CSSM_ALGID_RSA:
188 break;
189 default:
190 return NULL;
191 }
192 switch(cssmKey.keyClass()) {
193 case CSSM_KEYCLASS_PUBLIC_KEY:
194 case CSSM_KEYCLASS_PRIVATE_KEY:
195 break;
196 default:
197 return NULL;
198 }
199 /* OK, we'll handle this one */
200 return new RSAKeyInfoProvider(cssmKey);
201 }
202
203 /* Given a raw key, cook up a Binary key */
204 void RSAKeyInfoProvider::CssmKeyToBinary(
205 BinaryKey **binKey)
206 {
207 *binKey = NULL;
208 RSA *rsaKey = NULL;
209
210 /* first cook up an RSA key, then drop that into a BinaryKey */
211 rsaKey = rawCssmKeyToRsa(mKey);
212 RSABinaryKey *rsaBinKey = new RSABinaryKey(rsaKey);
213 *binKey = rsaBinKey;
214 }
215
216 /*
217 * Obtain key size in bits.
218 */
219 void RSAKeyInfoProvider::QueryKeySizeInBits(
220 CSSM_KEY_SIZE &keySize)
221 {
222 RSA *rsaKey = NULL;
223
224 if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
225 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
226 }
227 rsaKey = rawCssmKeyToRsa(mKey);
228 keySize.LogicalKeySizeInBits = RSA_size(rsaKey) * 8;
229 keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits;
230 RSA_free(rsaKey);
231 }
232
233 /***
234 *** DSA key support
235 ***/
236
237
238 /***
239 *** DSA-style BinaryKey
240 ***/
241
242 /* constructor with optional existing DSA key */
243 DSABinaryKey::DSABinaryKey(DSA *dsaKey)
244 : mDsaKey(dsaKey)
245 {
246 }
247
248 DSABinaryKey::~DSABinaryKey()
249 {
250 if(mDsaKey) {
251 DSA_free(mDsaKey);
252 mDsaKey = NULL;
253 }
254 }
255
256 void DSABinaryKey::generateKeyBlob(
257 CssmAllocator &allocator,
258 CssmData &blob,
259 CSSM_KEYBLOB_FORMAT &format)
260 {
261 bool isPub;
262 CSSM_RETURN crtn;
263
264 switch(mKeyHeader.KeyClass) {
265 case CSSM_KEYCLASS_PUBLIC_KEY:
266 isPub = true;
267 format = DSA_PUB_KEY_FORMAT;
268 break;
269 case CSSM_KEYCLASS_PRIVATE_KEY:
270 isPub = false;
271 format = DSA_PRIV_KEY_FORMAT;
272 break;
273 default:
274 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
275 }
276
277 CssmAutoData encodedKey(allocator);
278 if(isPub) {
279 crtn = DSAPublicKeyEncode(mDsaKey, encodedKey);
280 }
281 else {
282 crtn = DSAPrivateKeyEncode(mDsaKey, encodedKey);
283 }
284 if(crtn) {
285 CssmError::throwMe(crtn);
286 }
287 blob = encodedKey.release();
288 }
289
290 /***
291 *** DSA-style AppleKeyPairGenContext
292 ***/
293
294 /*
295 * This one is specified in, and called from, CSPFullPluginSession. Our
296 * only job is to prepare two subclass-specific BinaryKeys and call up to
297 * AppleKeyPairGenContext.
298 */
299 void DSAKeyPairGenContext::generate(
300 const Context &context,
301 CssmKey &pubKey,
302 CssmKey &privKey)
303 {
304 DSABinaryKey *pubBinKey = new DSABinaryKey();
305 DSABinaryKey *privBinKey = new DSABinaryKey();
306
307 try {
308 AppleKeyPairGenContext::generate(context,
309 session(),
310 pubKey,
311 pubBinKey,
312 privKey,
313 privBinKey);
314 }
315 catch (...) {
316 delete pubBinKey;
317 delete privBinKey;
318 throw;
319 }
320
321 }
322
323 /*
324 * This one is specified in, and called from, AppleKeyPairGenContext
325 */
326 void DSAKeyPairGenContext::generate(
327 const Context &context,
328 BinaryKey &pubBinKey,
329 BinaryKey &privBinKey,
330 uint32 &keyBits)
331 {
332 /*
333 * These casts throw exceptions if the keys are of the
334 * wrong classes, which would be a major bogon, since we created
335 * the keys in the above generate() function.
336 */
337 DSABinaryKey &rPubBinKey =
338 dynamic_cast<DSABinaryKey &>(pubBinKey);
339 DSABinaryKey &rPrivBinKey =
340 dynamic_cast<DSABinaryKey &>(privBinKey);
341
342 /*
343 * Parameters from context:
344 * Key size in bits, required;
345 * {p,q,g} from generateParams, optional
346 */
347 keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH,
348 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH);
349 CssmData *paramData = context.get<CssmData>(CSSM_ATTRIBUTE_ALG_PARAMS);
350
351 DSAAlgParams algParams;
352 if(paramData != NULL) {
353 /* this contains the DER encoding of a DSAAlgParams */
354 try {
355 SC_decodeAsnObj(*paramData, algParams);
356 }
357 catch(...) {
358 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
359 }
360 }
361 else {
362 /* no alg params specified; generate them now using null (random) seed */
363 dsaGenParams(keyBits, NULL, 0, algParams);
364 }
365
366 /* create key, stuff params into it */
367 rPrivBinKey.mDsaKey = DSA_new();
368 if(rPrivBinKey.mDsaKey == NULL) {
369 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
370 }
371 DSA *dsaKey = rPrivBinKey.mDsaKey;
372 dsaKey->p = bigIntStrToBn(algParams.p);
373 dsaKey->q = bigIntStrToBn(algParams.q);
374 dsaKey->g = bigIntStrToBn(algParams.g);
375
376 /* generate the key (both public and private capabilities) */
377 int irtn = DSA_generate_key(dsaKey);
378 if(!irtn) {
379 throwRsaDsa("DSA_generate_key");
380 }
381
382 /* public key is subset of private key */
383 rPubBinKey.mDsaKey = DSA_new();
384 if(rPrivBinKey.mDsaKey == NULL) {
385 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
386 }
387 DSA *pub = rPubBinKey.mDsaKey;
388 DSA *priv = rPrivBinKey.mDsaKey;
389 pub->p = BN_dup(priv->p);
390 pub->q = BN_dup(priv->q);
391 pub->g = BN_dup(priv->g);
392 pub->pub_key = BN_dup(priv->pub_key);
393 if((pub->p == NULL) || (pub->q == NULL) || (pub->g == NULL) ||
394 (pub->pub_key == NULL)) {
395 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
396 }
397 }
398
399 /*
400 * Generate keygen parameters, stash them in a context attr array for later use
401 * when actually generating the keys.
402 */
403 void DSAKeyPairGenContext::generate(
404 const Context &context,
405 uint32 bitSize,
406 CssmData &params,
407 uint32 &attrCount,
408 Context::Attr * &attrs)
409 {
410 void *seed = NULL;
411 unsigned seedLen = 0;
412
413 /* optional seed from context */
414 CssmData *seedData = context.get<CssmData>(CSSM_ATTRIBUTE_SEED);
415 if(seedData) {
416 seed = seedData->data();
417 seedLen = seedData->length();
418 }
419
420 /* generate the params */
421 DSAAlgParams algParams;
422 dsaGenParams(bitSize, seed, seedLen, algParams);
423
424 /*
425 * Here comes the fun part.
426 * We "return" the DER encoding of these generated params in two ways:
427 * 1. Copy out to app via the params argument, mallocing if Data ptr is NULL.
428 * The app must free this.
429 * 2. Cook up a 1-element Context::attr array containing one ALG_PARAM attr,
430 * a CSSM_DATA_PTR containing the DER encoding. We have to save a ptr to
431 * this attr array and free it, the CSSM_DATA it points to, and the DER
432 * encoding *that* points to, in our destructor.
433 *
434 * First, DER encode.
435 */
436 size_t maxSize = sizeofBigInt(algParams.p) +
437 sizeofBigInt(algParams.q) +
438 sizeofBigInt(algParams.g) +
439 10;
440 CssmAutoData aDerData(session());
441 SC_encodeAsnObj(algParams, aDerData, maxSize);
442
443 /* copy/release that into a mallocd CSSM_DATA. */
444 CSSM_DATA_PTR derData = (CSSM_DATA_PTR)session().malloc(sizeof(CSSM_DATA));
445 *derData = aDerData.release();
446
447 /* stuff that into a one-element Attr array which we keep after returning */
448 freeGenAttrs();
449 mGenAttrs = (Context::Attr *)session().malloc(sizeof(Context::Attr));
450 mGenAttrs->AttributeType = CSSM_ATTRIBUTE_ALG_PARAMS;
451 mGenAttrs->AttributeLength = sizeof(CSSM_DATA);
452 mGenAttrs->Attribute.Data = derData;
453
454 /* and "return" this stuff */
455 copyCssmData(CssmData::overlay(*derData), params, session());
456 attrCount = 1;
457 attrs = mGenAttrs;
458 }
459
460 /* free mGenAttrs and its referents if present */
461 void DSAKeyPairGenContext::freeGenAttrs()
462 {
463 if(mGenAttrs == NULL) {
464 return;
465 }
466 if(mGenAttrs->Attribute.Data) {
467 if(mGenAttrs->Attribute.Data->Data) {
468 session().free(mGenAttrs->Attribute.Data->Data);
469 }
470 session().free(mGenAttrs->Attribute.Data);
471 }
472 session().free(mGenAttrs);
473 }
474
475 /*
476 * Generate DSA algorithm parameters from optional seed input, returning result
477 * into DSAAlgParams.[pqg]. This is called from both GenerateParameters and from
478 * KeyPairGenerate (if no GenerateParameters has yet been called).
479 */
480 void DSAKeyPairGenContext::dsaGenParams(
481 uint32 keySizeInBits,
482 const void *inSeed, // optional
483 unsigned inSeedLen,
484 DSAAlgParams &algParams)
485 {
486 unsigned char seedBuf[SHA1_DIGEST_SIZE];
487 void *seedPtr;
488
489 /* validate key size */
490 if((keySizeInBits < DSA_MIN_KEY_SIZE) ||
491 (keySizeInBits > DSA_MAX_KEY_SIZE) ||
492 (keySizeInBits & DSA_KEY_BITS_MASK)) {
493 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH);
494 }
495
496 /* seed from one of three sources */
497 if(inSeed == NULL) {
498 /* 20 random seed bytes */
499 session().getRandomBytes(SHA1_DIGEST_SIZE, seedBuf);
500 seedPtr = seedBuf;
501 }
502 else if(inSeedLen == SHA1_DIGEST_SIZE) {
503 /* perfect */
504 seedPtr = (void *)inSeed;
505 }
506 else {
507 /* hash caller's seed */
508 cspGenSha1Hash(inSeed, inSeedLen, seedBuf);
509 seedPtr = seedBuf;
510 }
511
512 DSA *dsaKey = DSA_generate_parameters(keySizeInBits,
513 (unsigned char *)seedPtr,
514 SHA1_DIGEST_SIZE,
515 NULL, // counter_ret
516 NULL, // h_ret
517 NULL,
518 NULL);
519 if(dsaKey == NULL) {
520 throwRsaDsa("DSA_generate_parameters");
521 }
522
523 /* stuff dsaKey->[pqg] into a caller's DSAAlgParams */
524 bnToBigIntStr(dsaKey->p, algParams.p);
525 bnToBigIntStr(dsaKey->q, algParams.q);
526 bnToBigIntStr(dsaKey->g, algParams.g);
527
528 DSA_free(dsaKey);
529 }
530
531 /***
532 *** DSA-style CSPKeyInfoProvider.
533 ***/
534 DSAKeyInfoProvider::DSAKeyInfoProvider(
535 const CssmKey &cssmKey) :
536 CSPKeyInfoProvider(cssmKey)
537 {
538
539 }
540 CSPKeyInfoProvider *DSAKeyInfoProvider::provider(
541 const CssmKey &cssmKey)
542 {
543 switch(cssmKey.algorithm()) {
544 case CSSM_ALGID_DSA:
545 break;
546 default:
547 return NULL;
548 }
549 switch(cssmKey.keyClass()) {
550 case CSSM_KEYCLASS_PUBLIC_KEY:
551 case CSSM_KEYCLASS_PRIVATE_KEY:
552 break;
553 default:
554 return NULL;
555 }
556 /* OK, we'll handle this one */
557 return new DSAKeyInfoProvider(cssmKey);
558 }
559
560 /* Given a raw key, cook up a Binary key */
561 void DSAKeyInfoProvider::CssmKeyToBinary(
562 BinaryKey **binKey)
563 {
564 *binKey = NULL;
565 DSA *dsaKey = NULL;
566
567 /* first cook up an DSA key, then drop that into a BinaryKey */
568 dsaKey = rawCssmKeyToDsa(mKey);
569 DSABinaryKey *dsaBinKey = new DSABinaryKey(dsaKey);
570 *binKey = dsaBinKey;
571 }
572
573 /*
574 * Obtain key size in bits.
575 */
576 void DSAKeyInfoProvider::QueryKeySizeInBits(
577 CSSM_KEY_SIZE &keySize)
578 {
579 DSA *dsaKey = NULL;
580
581 if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
582 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
583 }
584 dsaKey = rawCssmKeyToDsa(mKey);
585 keySize.LogicalKeySizeInBits = BN_num_bits(dsaKey->p);
586 keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits;
587 DSA_free(dsaKey);
588 }