]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
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/opensslAsn1.h> | |
26 | #include <security_cdsa_utilities/cssmdata.h> | |
27 | #include <AppleCSPSession.h> | |
28 | #include <AppleCSPUtils.h> | |
29 | #include <assert.h> | |
30 | #include <security_utilities/debugging.h> | |
31 | #include "RSA_DSA_utils.h" | |
32 | #include <YarrowConnection.h> | |
33 | #include <security_asn1/SecNssCoder.h> | |
34 | ||
35 | #define RSA_PUB_EXPONENT 0x10001 /* recommended by RSA */ | |
36 | ||
37 | #define rsaKeyDebug(args...) secdebug("rsaKey", ## args) | |
38 | ||
39 | ||
40 | /*** | |
41 | *** RSA-style BinaryKey | |
42 | ***/ | |
43 | ||
44 | /* constructor with optional existing RSA key */ | |
45 | /* FIXME how to transmit OAEP params? */ | |
46 | RSABinaryKey::RSABinaryKey(RSA *rsaKey) | |
47 | : mRsaKey(rsaKey), | |
48 | mOaep(false), | |
49 | mLabel(Allocator::standard()) | |
50 | { | |
51 | } | |
52 | ||
53 | RSABinaryKey::~RSABinaryKey() | |
54 | { | |
55 | if(mRsaKey) { | |
56 | RSA_free(mRsaKey); | |
57 | mRsaKey = NULL; | |
58 | } | |
59 | } | |
60 | ||
61 | void RSABinaryKey::setOaep( | |
62 | const CSSM_DATA &label) | |
63 | { | |
64 | mLabel.copy(label); | |
65 | mOaep = true; | |
66 | } | |
67 | ||
68 | void RSABinaryKey::generateKeyBlob( | |
69 | Allocator &allocator, | |
70 | CssmData &blob, | |
71 | CSSM_KEYBLOB_FORMAT &format, /* IN/OUT */ | |
72 | AppleCSPSession &session, | |
73 | const CssmKey *paramKey, /* optional, unused here */ | |
74 | CSSM_KEYATTR_FLAGS &attrFlags) /* IN/OUT */ | |
75 | { | |
76 | bool isPub; | |
77 | CSSM_RETURN crtn; | |
78 | ||
79 | /* FIXME get label from context here for OAEP */ | |
80 | ||
81 | /* | |
82 | * Here, the incoming default of CSSM_KEYBLOB_RAW_FORMAT_NONE | |
83 | * is translated to our AppleCSP-custom defaults. App can override. | |
84 | */ | |
85 | switch(mKeyHeader.KeyClass) { | |
86 | case CSSM_KEYCLASS_PUBLIC_KEY: | |
87 | isPub = true; | |
88 | switch(format) { | |
89 | case CSSM_KEYBLOB_RAW_FORMAT_NONE: | |
90 | format = RSA_PUB_KEY_FORMAT; // default | |
91 | break; | |
92 | case CSSM_KEYBLOB_RAW_FORMAT_DIGEST: | |
93 | if(mOaep) { | |
94 | /* have to take digest of the whole thing including label */ | |
95 | format = CSSM_KEYBLOB_RAW_FORMAT_X509; | |
96 | } | |
97 | else { | |
98 | /* calculate digest on PKCS1 blob */ | |
99 | format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; | |
100 | } | |
101 | break; | |
102 | case CSSM_KEYBLOB_RAW_FORMAT_PKCS1: | |
103 | case CSSM_KEYBLOB_RAW_FORMAT_X509: | |
104 | case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH: | |
105 | case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2: | |
106 | break; | |
107 | default: | |
108 | CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT); | |
109 | } | |
110 | break; | |
111 | case CSSM_KEYCLASS_PRIVATE_KEY: | |
112 | isPub = false; | |
113 | switch(format) { | |
114 | case CSSM_KEYBLOB_RAW_FORMAT_NONE: // default | |
115 | format = RSA_PRIV_KEY_FORMAT; | |
116 | break; | |
117 | case CSSM_KEYBLOB_RAW_FORMAT_DIGEST: | |
118 | if(mOaep) { | |
119 | /* have to take digest of the whole thing including label */ | |
120 | format = CSSM_KEYBLOB_RAW_FORMAT_X509; | |
121 | } | |
122 | else { | |
123 | /* calculate digest on PKCS1 blob */ | |
124 | format = CSSM_KEYBLOB_RAW_FORMAT_PKCS1; | |
125 | } | |
126 | isPub = true; | |
127 | break; | |
128 | case CSSM_KEYBLOB_RAW_FORMAT_PKCS1: | |
129 | case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: | |
130 | case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH: | |
131 | break; | |
132 | default: | |
133 | CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT); | |
134 | } | |
135 | break; | |
136 | default: | |
137 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); | |
138 | } | |
139 | ||
140 | CssmAutoData encodedKey(allocator); | |
141 | if(mOaep) { | |
142 | CSSM_DATA label = mLabel; | |
143 | if(isPub) { | |
144 | crtn = RSAOAEPPublicKeyEncode(mRsaKey, &label, encodedKey); | |
145 | } | |
146 | else { | |
147 | crtn = RSAOAEPPrivateKeyEncode(mRsaKey, &label, encodedKey); | |
148 | } | |
149 | } | |
150 | else { | |
151 | if(isPub) { | |
152 | crtn = RSAPublicKeyEncode(mRsaKey, format, descData(), encodedKey); | |
153 | } | |
154 | else { | |
155 | crtn = RSAPrivateKeyEncode(mRsaKey, format, descData(), encodedKey); | |
156 | } | |
157 | } | |
158 | if(crtn) { | |
159 | CssmError::throwMe(crtn); | |
160 | } | |
161 | blob = encodedKey.release(); | |
162 | } | |
163 | ||
164 | /*** | |
165 | *** RSA-style AppleKeyPairGenContext | |
166 | ***/ | |
167 | ||
168 | /* | |
169 | * This one is specified in, and called from, CSPFullPluginSession. Our | |
170 | * only job is to prepare two subclass-specific BinaryKeys and call up to | |
171 | * AppleKeyPairGenContext. | |
172 | */ | |
173 | void RSAKeyPairGenContext::generate( | |
174 | const Context &context, | |
175 | CssmKey &pubKey, | |
176 | CssmKey &privKey) | |
177 | { | |
178 | RSABinaryKey *pubBinKey = new RSABinaryKey(); | |
179 | RSABinaryKey *privBinKey = new RSABinaryKey(); | |
180 | ||
181 | try { | |
182 | AppleKeyPairGenContext::generate(context, | |
183 | session(), | |
184 | pubKey, | |
185 | pubBinKey, | |
186 | privKey, | |
187 | privBinKey); | |
188 | } | |
189 | catch (...) { | |
190 | delete pubBinKey; | |
191 | delete privBinKey; | |
192 | throw; | |
193 | } | |
194 | ||
195 | } | |
196 | ||
197 | // this one is specified in, and called from, AppleKeyPairGenContext | |
198 | void RSAKeyPairGenContext::generate( | |
199 | const Context &context, | |
200 | BinaryKey &pubBinKey, | |
201 | BinaryKey &privBinKey, | |
202 | uint32 &keyBits) | |
203 | { | |
204 | /* | |
205 | * These casts throw exceptions if the keys are of the | |
206 | * wrong classes, which would be a major bogon, since we created | |
207 | * the keys in the above generate() function. | |
208 | */ | |
209 | RSABinaryKey &rPubBinKey = | |
210 | dynamic_cast<RSABinaryKey &>(pubBinKey); | |
211 | RSABinaryKey &rPrivBinKey = | |
212 | dynamic_cast<RSABinaryKey &>(privBinKey); | |
213 | ||
214 | /* | |
215 | * One parameter from context: Key size in bits is required. | |
216 | * FIXME - get public exponent from context? | |
217 | */ | |
218 | keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH, | |
219 | CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH); | |
220 | if(keyBits > rsaMaxKeySize()) { | |
221 | CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH); | |
222 | } | |
223 | ||
224 | /* generate the private key */ | |
225 | rPrivBinKey.mRsaKey = RSA_generate_key(keyBits, | |
226 | RSA_PUB_EXPONENT, | |
227 | NULL, // no callback | |
228 | NULL); | |
229 | if(rPrivBinKey.mRsaKey == NULL) { | |
230 | rsaKeyDebug("RSA_generate_key returned NULL"); | |
231 | CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); // ??? | |
232 | } | |
233 | ||
234 | /* public key is subset of private key */ | |
235 | rPubBinKey.mRsaKey = RSA_new(); | |
236 | if(rPrivBinKey.mRsaKey == NULL) { | |
237 | CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); | |
238 | } | |
239 | RSA *pub = rPubBinKey.mRsaKey; | |
240 | RSA *priv = rPrivBinKey.mRsaKey; | |
241 | pub->n = BN_dup(priv->n); | |
242 | pub->e = BN_dup(priv->e); | |
243 | if((pub->n == NULL) || (pub->e == NULL)) { | |
244 | CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); | |
245 | } | |
246 | } | |
247 | ||
248 | ||
249 | /*** | |
250 | *** RSA-style CSPKeyInfoProvider. | |
251 | ***/ | |
252 | RSAKeyInfoProvider::RSAKeyInfoProvider( | |
253 | const CssmKey &cssmKey, | |
254 | AppleCSPSession &session) : | |
255 | CSPKeyInfoProvider(cssmKey, session) | |
256 | { | |
257 | } | |
258 | ||
259 | CSPKeyInfoProvider *RSAKeyInfoProvider::provider( | |
260 | const CssmKey &cssmKey, | |
261 | AppleCSPSession &session) | |
262 | { | |
263 | switch(cssmKey.algorithm()) { | |
264 | case CSSM_ALGID_RSA: | |
265 | case CSSM_ALGMODE_PKCS1_EME_OAEP: | |
266 | break; | |
267 | default: | |
268 | return NULL; | |
269 | } | |
270 | switch(cssmKey.keyClass()) { | |
271 | case CSSM_KEYCLASS_PUBLIC_KEY: | |
272 | case CSSM_KEYCLASS_PRIVATE_KEY: | |
273 | break; | |
274 | default: | |
275 | return NULL; | |
276 | } | |
277 | /* OK, we'll handle this one */ | |
278 | return new RSAKeyInfoProvider(cssmKey, session); | |
279 | } | |
280 | ||
281 | /* Given a raw key, cook up a Binary key */ | |
282 | void RSAKeyInfoProvider::CssmKeyToBinary( | |
283 | CssmKey *paramKey, // ignored | |
284 | CSSM_KEYATTR_FLAGS &attrFlags, // IN/OUT, unused here | |
285 | BinaryKey **binKey) | |
286 | { | |
287 | *binKey = NULL; | |
288 | RSA *rsaKey = NULL; | |
289 | CSSM_DATA label = {0, NULL}; | |
290 | ||
291 | /* first cook up an RSA key */ | |
292 | rsaKey = rawCssmKeyToRsa(mKey, label); | |
293 | ||
294 | /* now drop that into a BinaryKey */ | |
295 | RSABinaryKey *rsaBinKey = new RSABinaryKey(rsaKey); | |
296 | *binKey = rsaBinKey; | |
297 | if(label.Data) { | |
298 | rsaBinKey->setOaep(label); | |
299 | free(label.Data); | |
300 | } | |
301 | } | |
302 | ||
303 | /* | |
304 | * Obtain key size in bits. | |
305 | */ | |
306 | void RSAKeyInfoProvider::QueryKeySizeInBits( | |
307 | CSSM_KEY_SIZE &keySize) | |
308 | { | |
309 | RSA *rsaKey = NULL; | |
310 | CSSM_DATA label = {0, NULL}; | |
311 | ||
312 | if(mKey.blobType() != CSSM_KEYBLOB_RAW) { | |
313 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); | |
314 | } | |
315 | rsaKey = rawCssmKeyToRsa(mKey, label); | |
316 | keySize.LogicalKeySizeInBits = RSA_size(rsaKey) * 8; | |
317 | keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits; | |
318 | RSA_free(rsaKey); | |
319 | if(label.Data) { | |
320 | free(label.Data); | |
321 | } | |
322 | } | |
323 | ||
324 | /* | |
325 | * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST | |
326 | * passthrough. | |
327 | */ | |
328 | bool RSAKeyInfoProvider::getHashableBlob( | |
329 | Allocator &allocator, | |
330 | CssmData &blob) // blob to hash goes here | |
331 | { | |
332 | /* | |
333 | * The optimized case, a raw key in the "proper" format already. | |
334 | * Only public keys in PKCS1 format fit this bill. | |
335 | */ | |
336 | assert(mKey.blobType() == CSSM_KEYBLOB_RAW); | |
337 | bool useAsIs = false; | |
338 | ||
339 | switch(mKey.keyClass()) { | |
340 | case CSSM_KEYCLASS_PUBLIC_KEY: | |
341 | if(mKey.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_PKCS1) { | |
342 | useAsIs = true; | |
343 | } | |
344 | break; | |
345 | case CSSM_KEYCLASS_PRIVATE_KEY: | |
346 | break; | |
347 | default: | |
348 | /* shouldn't be here */ | |
349 | assert(0); | |
350 | CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR); | |
351 | } | |
352 | if(useAsIs) { | |
353 | const CssmData &keyBlob = CssmData::overlay(mKey.KeyData); | |
354 | copyCssmData(keyBlob, blob, allocator); | |
355 | return true; | |
356 | } | |
357 | ||
358 | /* caller converts to binary and proceeds */ | |
359 | return false; | |
360 | } | |
361 | ||
362 | /*** | |
363 | *** DSA key support | |
364 | ***/ | |
365 | ||
366 | ||
367 | /*** | |
368 | *** DSA-style BinaryKey | |
369 | ***/ | |
370 | ||
371 | /* constructor with optional existing DSA key */ | |
372 | DSABinaryKey::DSABinaryKey(DSA *dsaKey) | |
373 | : mDsaKey(dsaKey) | |
374 | { | |
375 | } | |
376 | ||
377 | DSABinaryKey::~DSABinaryKey() | |
378 | { | |
379 | if(mDsaKey) { | |
380 | DSA_free(mDsaKey); | |
381 | mDsaKey = NULL; | |
382 | } | |
383 | } | |
384 | ||
385 | void DSABinaryKey::generateKeyBlob( | |
386 | Allocator &allocator, | |
387 | CssmData &blob, | |
388 | CSSM_KEYBLOB_FORMAT &format, | |
389 | AppleCSPSession &session, | |
390 | const CssmKey *paramKey, /* optional */ | |
391 | CSSM_KEYATTR_FLAGS &attrFlags) /* IN/OUT */ | |
392 | { | |
393 | bool isPub; | |
394 | CSSM_RETURN crtn; | |
395 | ||
396 | /* | |
397 | * Here, the incoming default of CSSM_KEYBLOB_RAW_FORMAT_NONE | |
398 | * is translated to our AppleCSP-custom defaults. App can override. | |
399 | */ | |
400 | switch(mKeyHeader.KeyClass) { | |
401 | case CSSM_KEYCLASS_PUBLIC_KEY: | |
402 | isPub = true; | |
403 | switch(format) { | |
404 | case CSSM_KEYBLOB_RAW_FORMAT_NONE: | |
405 | format = DSA_PUB_KEY_FORMAT; // default | |
406 | break; | |
407 | case CSSM_KEYBLOB_RAW_FORMAT_FIPS186: | |
408 | case CSSM_KEYBLOB_RAW_FORMAT_X509: | |
409 | case CSSM_KEYBLOB_RAW_FORMAT_DIGEST: | |
410 | case CSSM_KEYBLOB_RAW_FORMAT_OPENSSH2: | |
411 | break; | |
412 | default: | |
413 | CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT); | |
414 | } | |
415 | break; | |
416 | case CSSM_KEYCLASS_PRIVATE_KEY: | |
417 | isPub = false; | |
418 | switch(format) { | |
419 | case CSSM_KEYBLOB_RAW_FORMAT_NONE: | |
420 | format = DSA_PRIV_KEY_FORMAT; // default | |
421 | break; | |
422 | case CSSM_KEYBLOB_RAW_FORMAT_DIGEST: | |
423 | /* | |
424 | * This is calculated on the public key, which | |
425 | * is not always part of a DSA private key's encoding... | |
426 | * so first calculate the public key. | |
427 | */ | |
428 | dsaKeyPrivToPub(mDsaKey); | |
429 | isPub = true; | |
430 | break; | |
431 | case CSSM_KEYBLOB_RAW_FORMAT_FIPS186: | |
432 | case CSSM_KEYBLOB_RAW_FORMAT_PKCS8: | |
433 | case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL: | |
434 | break; | |
435 | default: | |
436 | CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT); | |
437 | } | |
438 | break; | |
439 | default: | |
440 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); | |
441 | } | |
442 | ||
443 | /* possible conversion from partial binary key to fully | |
444 | * formed blob */ | |
445 | DSA *dsaToEncode = mDsaKey; | |
446 | DSA *dsaUpgrade = NULL; | |
447 | if(isPub && | |
448 | (mDsaKey->p == NULL) && | |
449 | (paramKey != NULL)) { | |
450 | /* | |
451 | * Don't modify BinaryKey; make a copy. | |
452 | */ | |
453 | dsaUpgrade = DSA_new(); | |
454 | if(dsaUpgrade == NULL) { | |
455 | CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); | |
456 | } | |
457 | dsaUpgrade->pub_key = BN_dup(mDsaKey->pub_key); | |
458 | crtn = dsaGetParamsFromKey(dsaUpgrade, *paramKey, session); | |
459 | if(crtn) { | |
460 | DSA_free(dsaUpgrade); | |
461 | CssmError::throwMe(crtn); | |
462 | } | |
463 | ||
464 | /* success - switch keys and inform caller of attr change */ | |
465 | dsaToEncode = dsaUpgrade; | |
466 | attrFlags &= ~CSSM_KEYATTR_PARTIAL; | |
467 | } | |
468 | ||
469 | /* | |
470 | * DSA private keys originating from BSAFE form - e.g., DSA private | |
471 | * keys wrapped in a keychain (which have format FIPS186 by default) | |
472 | * have no public key component. Generate the public key if we don't | |
473 | * have one. | |
474 | */ | |
475 | if(!isPub && (dsaToEncode->pub_key == NULL)) { | |
476 | dsaKeyPrivToPub(dsaToEncode); | |
477 | } | |
478 | ||
479 | CssmAutoData encodedKey(allocator); | |
480 | if(isPub) { | |
481 | crtn = DSAPublicKeyEncode(dsaToEncode, format, descData(), encodedKey); | |
482 | } | |
483 | else { | |
484 | crtn = DSAPrivateKeyEncode(dsaToEncode, format, descData(), encodedKey); | |
485 | } | |
486 | if(dsaUpgrade != NULL) { | |
487 | /* temp key, get rid of it */ | |
488 | DSA_free(dsaUpgrade); | |
489 | } | |
490 | if(crtn) { | |
491 | CssmError::throwMe(crtn); | |
492 | } | |
493 | blob = encodedKey.release(); | |
494 | } | |
495 | ||
496 | /*** | |
497 | *** DSA-style AppleKeyPairGenContext | |
498 | ***/ | |
499 | ||
500 | /* | |
501 | * This one is specified in, and called from, CSPFullPluginSession. Our | |
502 | * only job is to prepare two subclass-specific BinaryKeys and call up to | |
503 | * AppleKeyPairGenContext. | |
504 | */ | |
505 | void DSAKeyPairGenContext::generate( | |
506 | const Context &context, | |
507 | CssmKey &pubKey, | |
508 | CssmKey &privKey) | |
509 | { | |
510 | DSABinaryKey *pubBinKey = new DSABinaryKey(); | |
511 | DSABinaryKey *privBinKey = new DSABinaryKey(); | |
512 | ||
513 | try { | |
514 | AppleKeyPairGenContext::generate(context, | |
515 | session(), | |
516 | pubKey, | |
517 | pubBinKey, | |
518 | privKey, | |
519 | privBinKey); | |
520 | } | |
521 | catch (...) { | |
522 | delete pubBinKey; | |
523 | delete privBinKey; | |
524 | throw; | |
525 | } | |
526 | ||
527 | } | |
528 | ||
529 | /* | |
530 | * This one is specified in, and called from, AppleKeyPairGenContext | |
531 | */ | |
532 | void DSAKeyPairGenContext::generate( | |
533 | const Context &context, | |
534 | BinaryKey &pubBinKey, | |
535 | BinaryKey &privBinKey, | |
536 | uint32 &keyBits) | |
537 | { | |
538 | /* | |
539 | * These casts throw exceptions if the keys are of the | |
540 | * wrong classes, which would be a major bogon, since we created | |
541 | * the keys in the above generate() function. | |
542 | */ | |
543 | DSABinaryKey &rPubBinKey = | |
544 | dynamic_cast<DSABinaryKey &>(pubBinKey); | |
545 | DSABinaryKey &rPrivBinKey = | |
546 | dynamic_cast<DSABinaryKey &>(privBinKey); | |
547 | ||
548 | /* | |
549 | * Parameters from context: | |
550 | * Key size in bits, required; | |
551 | * {p,q,g} from generateParams, optional | |
552 | */ | |
553 | keyBits = context.getInt(CSSM_ATTRIBUTE_KEY_LENGTH, | |
554 | CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH); | |
555 | if(keyBits > DSA_MAX_KEY_SIZE) { | |
556 | CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH); | |
557 | } | |
558 | CssmData *paramData = context.get<CssmData>(CSSM_ATTRIBUTE_ALG_PARAMS); | |
559 | ||
560 | NSS_DSAAlgParams algParams; | |
561 | SecNssCoder coder; // generated algParams mallocd from here | |
562 | if(paramData != NULL) { | |
563 | /* this contains the DER encoding of a NSS_DSAAlgParams */ | |
564 | CSSM_RETURN crtn = DSADecodeAlgParams(algParams, paramData->Data, | |
427c49bc | 565 | (unsigned)paramData->Length, coder); |
b1ab9ed8 A |
566 | if(crtn) { |
567 | CssmError::throwMe(crtn); | |
568 | } | |
569 | } | |
570 | else { | |
571 | /* no alg params specified; generate them now using null (random) seed */ | |
572 | dsaGenParams(keyBits, NULL, 0, algParams, coder); | |
573 | } | |
574 | ||
575 | /* create key, stuff params into it */ | |
576 | rPrivBinKey.mDsaKey = DSA_new(); | |
577 | if(rPrivBinKey.mDsaKey == NULL) { | |
578 | CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); | |
579 | } | |
580 | DSA *dsaKey = rPrivBinKey.mDsaKey; | |
581 | dsaKey->p = cssmDataToBn(algParams.p); | |
582 | dsaKey->q = cssmDataToBn(algParams.q); | |
583 | dsaKey->g = cssmDataToBn(algParams.g); | |
584 | ||
585 | /* generate the key (both public and private capabilities) */ | |
586 | int irtn = DSA_generate_key(dsaKey); | |
587 | if(!irtn) { | |
588 | throwRsaDsa("DSA_generate_key"); | |
589 | } | |
590 | ||
591 | /* public key is subset of private key */ | |
592 | rPubBinKey.mDsaKey = DSA_new(); | |
593 | if(rPrivBinKey.mDsaKey == NULL) { | |
594 | CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); | |
595 | } | |
596 | DSA *pub = rPubBinKey.mDsaKey; | |
597 | DSA *priv = rPrivBinKey.mDsaKey; | |
598 | pub->p = BN_dup(priv->p); | |
599 | pub->q = BN_dup(priv->q); | |
600 | pub->g = BN_dup(priv->g); | |
601 | pub->pub_key = BN_dup(priv->pub_key); | |
602 | if((pub->p == NULL) || (pub->q == NULL) || (pub->g == NULL) || | |
603 | (pub->pub_key == NULL)) { | |
604 | CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR); | |
605 | } | |
606 | } | |
607 | ||
608 | /* | |
609 | * Generate keygen parameters, stash them in a context attr array for later use | |
610 | * when actually generating the keys. | |
611 | */ | |
612 | void DSAKeyPairGenContext::generate( | |
613 | const Context &context, | |
614 | uint32 bitSize, | |
615 | CssmData ¶ms, | |
616 | uint32 &attrCount, | |
617 | Context::Attr * &attrs) | |
618 | { | |
619 | void *seed = NULL; | |
620 | unsigned seedLen = 0; | |
621 | ||
622 | /* optional seed from context */ | |
623 | CssmData *seedData = context.get<CssmData>(CSSM_ATTRIBUTE_SEED); | |
624 | if(seedData) { | |
625 | seed = seedData->data(); | |
427c49bc | 626 | seedLen = (unsigned)seedData->length(); |
b1ab9ed8 A |
627 | } |
628 | ||
629 | /* generate the params, temp alloc from SecNssCoder */ | |
630 | NSS_DSAAlgParams algParams; | |
631 | SecNssCoder coder; | |
632 | dsaGenParams(bitSize, seed, seedLen, algParams, coder); | |
633 | ||
634 | /* | |
635 | * Here comes the fun part. | |
636 | * We "return" the DER encoding of these generated params in two ways: | |
637 | * 1. Copy out to app via the params argument, mallocing if Data ptr is NULL. | |
638 | * The app must free this. | |
639 | * 2. Cook up a 1-element Context::attr array containing one ALG_PARAM attr, | |
640 | * a CSSM_DATA_PTR containing the DER encoding. We have to save a ptr to | |
641 | * this attr array and free it, the CSSM_DATA it points to, and the DER | |
642 | * encoding *that* points to, in our destructor. | |
643 | * | |
644 | * First, DER encode. | |
645 | */ | |
646 | CssmAutoData aDerData(session()); | |
647 | DSAEncodeAlgParams(algParams, aDerData); | |
648 | ||
649 | /* copy/release that into a mallocd CSSM_DATA. */ | |
650 | CSSM_DATA_PTR derData = (CSSM_DATA_PTR)session().malloc(sizeof(CSSM_DATA)); | |
651 | *derData = aDerData.release(); | |
652 | ||
653 | /* stuff that into a one-element Attr array which we keep after returning */ | |
654 | freeGenAttrs(); | |
655 | mGenAttrs = (Context::Attr *)session().malloc(sizeof(Context::Attr)); | |
656 | mGenAttrs->AttributeType = CSSM_ATTRIBUTE_ALG_PARAMS; | |
657 | mGenAttrs->AttributeLength = sizeof(CSSM_DATA); | |
658 | mGenAttrs->Attribute.Data = derData; | |
659 | ||
660 | /* and "return" this stuff */ | |
661 | copyCssmData(CssmData::overlay(*derData), params, session()); | |
662 | attrCount = 1; | |
663 | attrs = mGenAttrs; | |
664 | } | |
665 | ||
666 | /* free mGenAttrs and its referents if present */ | |
667 | void DSAKeyPairGenContext::freeGenAttrs() | |
668 | { | |
669 | if(mGenAttrs == NULL) { | |
670 | return; | |
671 | } | |
672 | if(mGenAttrs->Attribute.Data) { | |
673 | if(mGenAttrs->Attribute.Data->Data) { | |
674 | session().free(mGenAttrs->Attribute.Data->Data); | |
675 | } | |
676 | session().free(mGenAttrs->Attribute.Data); | |
677 | } | |
678 | session().free(mGenAttrs); | |
679 | } | |
680 | ||
681 | /* | |
682 | * Generate DSA algorithm parameters from optional seed input, returning result | |
683 | * into NSS_DSAAlgParamss.[pqg]. This is called from both GenerateParameters and from | |
684 | * KeyPairGenerate (if no GenerateParameters has yet been called). | |
685 | */ | |
686 | void DSAKeyPairGenContext::dsaGenParams( | |
687 | uint32 keySizeInBits, | |
688 | const void *inSeed, // optional | |
689 | unsigned inSeedLen, | |
690 | NSS_DSAAlgParams &algParams, | |
691 | SecNssCoder &coder) // contents of algParams mallocd from here | |
692 | { | |
693 | unsigned char seedBuf[SHA1_DIGEST_SIZE]; | |
694 | void *seedPtr; | |
695 | ||
696 | /* validate key size */ | |
697 | if((keySizeInBits < DSA_MIN_KEY_SIZE) || | |
698 | (keySizeInBits > DSA_MAX_KEY_SIZE) || | |
699 | (keySizeInBits & DSA_KEY_BITS_MASK)) { | |
700 | CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH); | |
701 | } | |
702 | ||
703 | /* seed from one of three sources */ | |
704 | if(inSeed == NULL) { | |
705 | /* 20 random seed bytes */ | |
706 | session().getRandomBytes(SHA1_DIGEST_SIZE, seedBuf); | |
707 | seedPtr = seedBuf; | |
708 | } | |
709 | else if(inSeedLen == SHA1_DIGEST_SIZE) { | |
710 | /* perfect */ | |
711 | seedPtr = (void *)inSeed; | |
712 | } | |
713 | else { | |
714 | /* hash caller's seed */ | |
715 | cspGenSha1Hash(inSeed, inSeedLen, seedBuf); | |
716 | seedPtr = seedBuf; | |
717 | } | |
718 | ||
719 | DSA *dsaKey = DSA_generate_parameters(keySizeInBits, | |
720 | (unsigned char *)seedPtr, | |
721 | SHA1_DIGEST_SIZE, | |
722 | NULL, // counter_ret | |
723 | NULL, // h_ret | |
724 | NULL, | |
725 | NULL); | |
726 | if(dsaKey == NULL) { | |
727 | throwRsaDsa("DSA_generate_parameters"); | |
728 | } | |
729 | ||
730 | /* stuff dsaKey->[pqg] into a caller's NSS_DSAAlgParams */ | |
731 | bnToCssmData(dsaKey->p, algParams.p, coder); | |
732 | bnToCssmData(dsaKey->q, algParams.q, coder); | |
733 | bnToCssmData(dsaKey->g, algParams.g, coder); | |
734 | ||
735 | DSA_free(dsaKey); | |
736 | } | |
737 | ||
738 | /*** | |
739 | *** DSA-style CSPKeyInfoProvider. | |
740 | ***/ | |
741 | DSAKeyInfoProvider::DSAKeyInfoProvider( | |
742 | const CssmKey &cssmKey, | |
743 | AppleCSPSession &session) : | |
744 | CSPKeyInfoProvider(cssmKey, session) | |
745 | { | |
746 | ||
747 | } | |
748 | CSPKeyInfoProvider *DSAKeyInfoProvider::provider( | |
749 | const CssmKey &cssmKey, | |
750 | AppleCSPSession &session) | |
751 | { | |
752 | switch(cssmKey.algorithm()) { | |
753 | case CSSM_ALGID_DSA: | |
754 | break; | |
755 | default: | |
756 | return NULL; | |
757 | } | |
758 | switch(cssmKey.keyClass()) { | |
759 | case CSSM_KEYCLASS_PUBLIC_KEY: | |
760 | case CSSM_KEYCLASS_PRIVATE_KEY: | |
761 | break; | |
762 | default: | |
763 | return NULL; | |
764 | } | |
765 | /* OK, we'll handle this one */ | |
766 | return new DSAKeyInfoProvider(cssmKey, session); | |
767 | } | |
768 | ||
769 | /* Given a raw key, cook up a Binary key */ | |
770 | void DSAKeyInfoProvider::CssmKeyToBinary( | |
771 | CssmKey *paramKey, // optional | |
772 | CSSM_KEYATTR_FLAGS &attrFlags, // IN/OUT | |
773 | BinaryKey **binKey) | |
774 | { | |
775 | *binKey = NULL; | |
776 | DSA *dsaKey = NULL; | |
777 | ||
778 | /* first cook up an DSA key, then drop that into a BinaryKey */ | |
779 | dsaKey = rawCssmKeyToDsa(mKey, mSession, paramKey); | |
780 | if(dsaKey->p == NULL) { | |
781 | attrFlags |= CSSM_KEYATTR_PARTIAL; | |
782 | } | |
783 | else { | |
784 | attrFlags &= ~CSSM_KEYATTR_PARTIAL; | |
785 | } | |
786 | DSABinaryKey *dsaBinKey = new DSABinaryKey(dsaKey); | |
787 | *binKey = dsaBinKey; | |
788 | } | |
789 | ||
790 | /* | |
791 | * Obtain key size in bits. | |
792 | */ | |
793 | void DSAKeyInfoProvider::QueryKeySizeInBits( | |
794 | CSSM_KEY_SIZE &keySize) | |
795 | { | |
796 | DSA *dsaKey = NULL; | |
797 | ||
798 | if(mKey.blobType() != CSSM_KEYBLOB_RAW) { | |
799 | CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT); | |
800 | } | |
801 | dsaKey = rawCssmKeyToDsa(mKey, | |
802 | mSession, | |
803 | NULL); // no param key allowed here | |
804 | if(dsaKey->p != NULL) { | |
805 | /* normal fully-formed key */ | |
806 | keySize.LogicalKeySizeInBits = BN_num_bits(dsaKey->p); | |
807 | keySize.EffectiveKeySizeInBits = keySize.LogicalKeySizeInBits; | |
808 | DSA_free(dsaKey); | |
809 | } | |
810 | else { | |
811 | /* partial key, get an approximation from pub_key */ | |
812 | keySize.LogicalKeySizeInBits = BN_num_bits(dsaKey->pub_key); | |
813 | DSA_free(dsaKey); | |
814 | /* and indicate this anomaly like so */ | |
815 | CssmError::throwMe(CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE); | |
816 | } | |
817 | } | |
818 | ||
819 | /* | |
820 | * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST | |
821 | * passthrough. | |
822 | */ | |
823 | bool DSAKeyInfoProvider::getHashableBlob( | |
824 | Allocator &allocator, | |
825 | CssmData &blob) // blob to hash goes here | |
826 | { | |
827 | /* No optimized case for DSA keys */ | |
828 | return false; | |
829 | } |