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