2 * Copyright (c) 2000-2001 Apple Computer, Inc. All Rights Reserved.
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
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.
20 * RSA_DSA_Keys.cpp - RSA, DSA related asymmetric key pair classes.
23 #include "RSA_DSA_keys.h"
24 #include <opensslUtils/opensslUtils.h>
25 #include <opensslUtils/opensslAsn1.h>
26 #include <Security/cssmdata.h>
27 #include <AppleCSP/AppleCSPSession.h>
28 #include <AppleCSP/AppleCSPUtils.h>
30 #include <Security/debugging.h>
31 #include "RSA_DSA_utils.h"
32 #include <AppleCSP/YarrowConnection.h>
33 #include <SecurityNssAsn1/SecNssCoder.h>
35 #define RSA_PUB_EXPONENT 0x10001 /* recommended by RSA */
37 #define rsaKeyDebug(args...) secdebug("rsaKey", ## args)
40 *** RSA-style BinaryKey
43 /* constructor with optional existing RSA key */
44 RSABinaryKey::RSABinaryKey(RSA
*rsaKey
)
49 RSABinaryKey::~RSABinaryKey()
57 void RSABinaryKey::generateKeyBlob(
58 CssmAllocator
&allocator
,
60 CSSM_KEYBLOB_FORMAT
&format
, /* IN/OUT */
61 AppleCSPSession
&session
,
62 const CssmKey
*paramKey
, /* optional, unused here */
63 CSSM_KEYATTR_FLAGS
&attrFlags
) /* IN/OUT */
69 * Here, the incoming default of CSSM_KEYBLOB_RAW_FORMAT_NONE
70 * is translated to our AppleCSP-custom defaults. App can override.
72 switch(mKeyHeader
.KeyClass
) {
73 case CSSM_KEYCLASS_PUBLIC_KEY
:
76 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
77 format
= RSA_PUB_KEY_FORMAT
; // default
79 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST
:
80 /* calculate digest on PKCS1 blob */
81 format
= CSSM_KEYBLOB_RAW_FORMAT_PKCS1
;
83 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1
:
84 case CSSM_KEYBLOB_RAW_FORMAT_X509
:
87 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT
);
90 case CSSM_KEYCLASS_PRIVATE_KEY
:
93 case CSSM_KEYBLOB_RAW_FORMAT_NONE
: // default
94 format
= RSA_PRIV_KEY_FORMAT
;
96 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST
:
97 /* calculate digest on Public PKCS1 blob */
98 format
= CSSM_KEYBLOB_RAW_FORMAT_PKCS1
;
101 case CSSM_KEYBLOB_RAW_FORMAT_PKCS1
:
102 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
105 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT
);
109 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
112 CssmAutoData
encodedKey(allocator
);
114 crtn
= RSAPublicKeyEncode(mRsaKey
, format
, encodedKey
);
117 crtn
= RSAPrivateKeyEncode(mRsaKey
, format
, encodedKey
);
120 CssmError::throwMe(crtn
);
122 blob
= encodedKey
.release();
126 *** RSA-style AppleKeyPairGenContext
130 * This one is specified in, and called from, CSPFullPluginSession. Our
131 * only job is to prepare two subclass-specific BinaryKeys and call up to
132 * AppleKeyPairGenContext.
134 void RSAKeyPairGenContext::generate(
135 const Context
&context
,
139 RSABinaryKey
*pubBinKey
= new RSABinaryKey();
140 RSABinaryKey
*privBinKey
= new RSABinaryKey();
143 AppleKeyPairGenContext::generate(context
,
158 // this one is specified in, and called from, AppleKeyPairGenContext
159 void RSAKeyPairGenContext::generate(
160 const Context
&context
,
161 BinaryKey
&pubBinKey
,
162 BinaryKey
&privBinKey
,
166 * These casts throw exceptions if the keys are of the
167 * wrong classes, which would be a major bogon, since we created
168 * the keys in the above generate() function.
170 RSABinaryKey
&rPubBinKey
=
171 dynamic_cast<RSABinaryKey
&>(pubBinKey
);
172 RSABinaryKey
&rPrivBinKey
=
173 dynamic_cast<RSABinaryKey
&>(privBinKey
);
176 * One parameter from context: Key size in bits is required.
177 * FIXME - get public exponent from context?
179 keyBits
= context
.getInt(CSSM_ATTRIBUTE_KEY_LENGTH
,
180 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH
);
182 /* generate the private key */
183 rPrivBinKey
.mRsaKey
= RSA_generate_key(keyBits
,
187 if(rPrivBinKey
.mRsaKey
== NULL
) {
188 rsaKeyDebug("RSA_generate_key returned NULL");
189 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
); // ???
192 /* public key is subset of private key */
193 rPubBinKey
.mRsaKey
= RSA_new();
194 if(rPrivBinKey
.mRsaKey
== NULL
) {
195 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
197 RSA
*pub
= rPubBinKey
.mRsaKey
;
198 RSA
*priv
= rPrivBinKey
.mRsaKey
;
199 pub
->n
= BN_dup(priv
->n
);
200 pub
->e
= BN_dup(priv
->e
);
201 if((pub
->n
== NULL
) || (pub
->e
== NULL
)) {
202 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
208 *** RSA-style CSPKeyInfoProvider.
210 RSAKeyInfoProvider::RSAKeyInfoProvider(
211 const CssmKey
&cssmKey
,
212 AppleCSPSession
&session
) :
213 CSPKeyInfoProvider(cssmKey
, session
)
217 CSPKeyInfoProvider
*RSAKeyInfoProvider::provider(
218 const CssmKey
&cssmKey
,
219 AppleCSPSession
&session
)
221 switch(cssmKey
.algorithm()) {
227 switch(cssmKey
.keyClass()) {
228 case CSSM_KEYCLASS_PUBLIC_KEY
:
229 case CSSM_KEYCLASS_PRIVATE_KEY
:
234 /* OK, we'll handle this one */
235 return new RSAKeyInfoProvider(cssmKey
, session
);
238 /* Given a raw key, cook up a Binary key */
239 void RSAKeyInfoProvider::CssmKeyToBinary(
240 CssmKey
*paramKey
, // ignored
241 CSSM_KEYATTR_FLAGS
&attrFlags
, // IN/OUT, unused here
247 /* first cook up an RSA key, then drop that into a BinaryKey */
248 rsaKey
= rawCssmKeyToRsa(mKey
);
249 RSABinaryKey
*rsaBinKey
= new RSABinaryKey(rsaKey
);
254 * Obtain key size in bits.
256 void RSAKeyInfoProvider::QueryKeySizeInBits(
257 CSSM_KEY_SIZE
&keySize
)
261 if(mKey
.blobType() != CSSM_KEYBLOB_RAW
) {
262 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT
);
264 rsaKey
= rawCssmKeyToRsa(mKey
);
265 keySize
.LogicalKeySizeInBits
= RSA_size(rsaKey
) * 8;
266 keySize
.EffectiveKeySizeInBits
= keySize
.LogicalKeySizeInBits
;
271 * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST
274 bool RSAKeyInfoProvider::getHashableBlob(
275 CssmAllocator
&allocator
,
276 CssmData
&blob
) // blob to hash goes here
279 * The optimized case, a raw key in the "proper" format already.
280 * Only public keys in PKCS1 format fit this bill.
282 assert(mKey
.blobType() == CSSM_KEYBLOB_RAW
);
283 bool useAsIs
= false;
285 switch(mKey
.keyClass()) {
286 case CSSM_KEYCLASS_PUBLIC_KEY
:
287 if(mKey
.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_PKCS1
) {
291 case CSSM_KEYCLASS_PRIVATE_KEY
:
294 /* shouldn't be here */
296 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
);
299 const CssmData
&keyBlob
= CssmData::overlay(mKey
.KeyData
);
300 copyCssmData(keyBlob
, blob
, allocator
);
304 /* caller converts to binary and proceeds */
314 *** DSA-style BinaryKey
317 /* constructor with optional existing DSA key */
318 DSABinaryKey::DSABinaryKey(DSA
*dsaKey
)
323 DSABinaryKey::~DSABinaryKey()
331 void DSABinaryKey::generateKeyBlob(
332 CssmAllocator
&allocator
,
334 CSSM_KEYBLOB_FORMAT
&format
,
335 AppleCSPSession
&session
,
336 const CssmKey
*paramKey
, /* optional */
337 CSSM_KEYATTR_FLAGS
&attrFlags
) /* IN/OUT */
343 * Here, the incoming default of CSSM_KEYBLOB_RAW_FORMAT_NONE
344 * is translated to our AppleCSP-custom defaults. App can override.
346 switch(mKeyHeader
.KeyClass
) {
347 case CSSM_KEYCLASS_PUBLIC_KEY
:
350 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
351 format
= DSA_PUB_KEY_FORMAT
; // default
353 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186
:
354 case CSSM_KEYBLOB_RAW_FORMAT_X509
:
355 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST
:
358 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT
);
361 case CSSM_KEYCLASS_PRIVATE_KEY
:
364 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
365 format
= DSA_PRIV_KEY_FORMAT
; // default
367 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST
:
369 * This is calculated on the public key, which
370 * is not part of a DSA private key's encoding...
371 * so first calculate the public key.
373 dsaKeyPrivToPub(mDsaKey
);
376 case CSSM_KEYBLOB_RAW_FORMAT_FIPS186
:
377 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
378 case CSSM_KEYBLOB_RAW_FORMAT_OPENSSL
:
381 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT
);
385 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
388 /* possible conversion from partial binary key to fully
390 DSA
*dsaToEncode
= mDsaKey
;
391 DSA
*dsaUpgrade
= NULL
;
393 (mDsaKey
->p
== NULL
) &&
394 (paramKey
!= NULL
)) {
396 * Don't modify BinaryKey; make a copy.
398 dsaUpgrade
= DSA_new();
399 if(dsaUpgrade
== NULL
) {
400 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
402 dsaUpgrade
->pub_key
= BN_dup(mDsaKey
->pub_key
);
403 crtn
= dsaGetParamsFromKey(dsaUpgrade
, *paramKey
, session
);
405 DSA_free(dsaUpgrade
);
406 CssmError::throwMe(crtn
);
409 /* success - switch keys and inform caller of attr change */
410 dsaToEncode
= dsaUpgrade
;
411 attrFlags
&= ~CSSM_KEYATTR_PARTIAL
;
414 CssmAutoData
encodedKey(allocator
);
416 crtn
= DSAPublicKeyEncode(dsaToEncode
, format
, encodedKey
);
419 crtn
= DSAPrivateKeyEncode(dsaToEncode
, format
, encodedKey
);
421 if(dsaUpgrade
!= NULL
) {
422 /* temp key, get rid of it */
423 DSA_free(dsaUpgrade
);
426 CssmError::throwMe(crtn
);
428 blob
= encodedKey
.release();
432 *** DSA-style AppleKeyPairGenContext
436 * This one is specified in, and called from, CSPFullPluginSession. Our
437 * only job is to prepare two subclass-specific BinaryKeys and call up to
438 * AppleKeyPairGenContext.
440 void DSAKeyPairGenContext::generate(
441 const Context
&context
,
445 DSABinaryKey
*pubBinKey
= new DSABinaryKey();
446 DSABinaryKey
*privBinKey
= new DSABinaryKey();
449 AppleKeyPairGenContext::generate(context
,
465 * This one is specified in, and called from, AppleKeyPairGenContext
467 void DSAKeyPairGenContext::generate(
468 const Context
&context
,
469 BinaryKey
&pubBinKey
,
470 BinaryKey
&privBinKey
,
474 * These casts throw exceptions if the keys are of the
475 * wrong classes, which would be a major bogon, since we created
476 * the keys in the above generate() function.
478 DSABinaryKey
&rPubBinKey
=
479 dynamic_cast<DSABinaryKey
&>(pubBinKey
);
480 DSABinaryKey
&rPrivBinKey
=
481 dynamic_cast<DSABinaryKey
&>(privBinKey
);
484 * Parameters from context:
485 * Key size in bits, required;
486 * {p,q,g} from generateParams, optional
488 keyBits
= context
.getInt(CSSM_ATTRIBUTE_KEY_LENGTH
,
489 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH
);
490 CssmData
*paramData
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_ALG_PARAMS
);
492 NSS_DSAAlgParams algParams
;
493 SecNssCoder coder
; // generated algParams mallocd from here
494 if(paramData
!= NULL
) {
495 /* this contains the DER encoding of a NSS_DSAAlgParams */
496 CSSM_RETURN crtn
= DSADecodeAlgParams(algParams
, paramData
->Data
,
497 paramData
->Length
, coder
);
499 CssmError::throwMe(crtn
);
503 /* no alg params specified; generate them now using null (random) seed */
504 dsaGenParams(keyBits
, NULL
, 0, algParams
, coder
);
507 /* create key, stuff params into it */
508 rPrivBinKey
.mDsaKey
= DSA_new();
509 if(rPrivBinKey
.mDsaKey
== NULL
) {
510 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
512 DSA
*dsaKey
= rPrivBinKey
.mDsaKey
;
513 dsaKey
->p
= cssmDataToBn(algParams
.p
);
514 dsaKey
->q
= cssmDataToBn(algParams
.q
);
515 dsaKey
->g
= cssmDataToBn(algParams
.g
);
517 /* generate the key (both public and private capabilities) */
518 int irtn
= DSA_generate_key(dsaKey
);
520 throwRsaDsa("DSA_generate_key");
523 /* public key is subset of private key */
524 rPubBinKey
.mDsaKey
= DSA_new();
525 if(rPrivBinKey
.mDsaKey
== NULL
) {
526 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
528 DSA
*pub
= rPubBinKey
.mDsaKey
;
529 DSA
*priv
= rPrivBinKey
.mDsaKey
;
530 pub
->p
= BN_dup(priv
->p
);
531 pub
->q
= BN_dup(priv
->q
);
532 pub
->g
= BN_dup(priv
->g
);
533 pub
->pub_key
= BN_dup(priv
->pub_key
);
534 if((pub
->p
== NULL
) || (pub
->q
== NULL
) || (pub
->g
== NULL
) ||
535 (pub
->pub_key
== NULL
)) {
536 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
541 * Generate keygen parameters, stash them in a context attr array for later use
542 * when actually generating the keys.
544 void DSAKeyPairGenContext::generate(
545 const Context
&context
,
549 Context::Attr
* &attrs
)
552 unsigned seedLen
= 0;
554 /* optional seed from context */
555 CssmData
*seedData
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_SEED
);
557 seed
= seedData
->data();
558 seedLen
= seedData
->length();
561 /* generate the params, temp alloc from SecNssCoder */
562 NSS_DSAAlgParams algParams
;
564 dsaGenParams(bitSize
, seed
, seedLen
, algParams
, coder
);
567 * Here comes the fun part.
568 * We "return" the DER encoding of these generated params in two ways:
569 * 1. Copy out to app via the params argument, mallocing if Data ptr is NULL.
570 * The app must free this.
571 * 2. Cook up a 1-element Context::attr array containing one ALG_PARAM attr,
572 * a CSSM_DATA_PTR containing the DER encoding. We have to save a ptr to
573 * this attr array and free it, the CSSM_DATA it points to, and the DER
574 * encoding *that* points to, in our destructor.
578 CssmAutoData
aDerData(session());
579 DSAEncodeAlgParams(algParams
, aDerData
);
581 /* copy/release that into a mallocd CSSM_DATA. */
582 CSSM_DATA_PTR derData
= (CSSM_DATA_PTR
)session().malloc(sizeof(CSSM_DATA
));
583 *derData
= aDerData
.release();
585 /* stuff that into a one-element Attr array which we keep after returning */
587 mGenAttrs
= (Context::Attr
*)session().malloc(sizeof(Context::Attr
));
588 mGenAttrs
->AttributeType
= CSSM_ATTRIBUTE_ALG_PARAMS
;
589 mGenAttrs
->AttributeLength
= sizeof(CSSM_DATA
);
590 mGenAttrs
->Attribute
.Data
= derData
;
592 /* and "return" this stuff */
593 copyCssmData(CssmData::overlay(*derData
), params
, session());
598 /* free mGenAttrs and its referents if present */
599 void DSAKeyPairGenContext::freeGenAttrs()
601 if(mGenAttrs
== NULL
) {
604 if(mGenAttrs
->Attribute
.Data
) {
605 if(mGenAttrs
->Attribute
.Data
->Data
) {
606 session().free(mGenAttrs
->Attribute
.Data
->Data
);
608 session().free(mGenAttrs
->Attribute
.Data
);
610 session().free(mGenAttrs
);
614 * Generate DSA algorithm parameters from optional seed input, returning result
615 * into NSS_DSAAlgParamss.[pqg]. This is called from both GenerateParameters and from
616 * KeyPairGenerate (if no GenerateParameters has yet been called).
618 void DSAKeyPairGenContext::dsaGenParams(
619 uint32 keySizeInBits
,
620 const void *inSeed
, // optional
622 NSS_DSAAlgParams
&algParams
,
623 SecNssCoder
&coder
) // contents of algParams mallocd from here
625 unsigned char seedBuf
[SHA1_DIGEST_SIZE
];
628 /* validate key size */
629 if((keySizeInBits
< DSA_MIN_KEY_SIZE
) ||
630 (keySizeInBits
> DSA_MAX_KEY_SIZE
) ||
631 (keySizeInBits
& DSA_KEY_BITS_MASK
)) {
632 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH
);
635 /* seed from one of three sources */
637 /* 20 random seed bytes */
638 session().getRandomBytes(SHA1_DIGEST_SIZE
, seedBuf
);
641 else if(inSeedLen
== SHA1_DIGEST_SIZE
) {
643 seedPtr
= (void *)inSeed
;
646 /* hash caller's seed */
647 cspGenSha1Hash(inSeed
, inSeedLen
, seedBuf
);
651 DSA
*dsaKey
= DSA_generate_parameters(keySizeInBits
,
652 (unsigned char *)seedPtr
,
659 throwRsaDsa("DSA_generate_parameters");
662 /* stuff dsaKey->[pqg] into a caller's NSS_DSAAlgParams */
663 bnToCssmData(dsaKey
->p
, algParams
.p
, coder
);
664 bnToCssmData(dsaKey
->q
, algParams
.q
, coder
);
665 bnToCssmData(dsaKey
->g
, algParams
.g
, coder
);
671 *** DSA-style CSPKeyInfoProvider.
673 DSAKeyInfoProvider::DSAKeyInfoProvider(
674 const CssmKey
&cssmKey
,
675 AppleCSPSession
&session
) :
676 CSPKeyInfoProvider(cssmKey
, session
)
680 CSPKeyInfoProvider
*DSAKeyInfoProvider::provider(
681 const CssmKey
&cssmKey
,
682 AppleCSPSession
&session
)
684 switch(cssmKey
.algorithm()) {
690 switch(cssmKey
.keyClass()) {
691 case CSSM_KEYCLASS_PUBLIC_KEY
:
692 case CSSM_KEYCLASS_PRIVATE_KEY
:
697 /* OK, we'll handle this one */
698 return new DSAKeyInfoProvider(cssmKey
, session
);
701 /* Given a raw key, cook up a Binary key */
702 void DSAKeyInfoProvider::CssmKeyToBinary(
703 CssmKey
*paramKey
, // optional
704 CSSM_KEYATTR_FLAGS
&attrFlags
, // IN/OUT
710 /* first cook up an DSA key, then drop that into a BinaryKey */
711 dsaKey
= rawCssmKeyToDsa(mKey
, mSession
, paramKey
);
712 if(dsaKey
->p
== NULL
) {
713 attrFlags
|= CSSM_KEYATTR_PARTIAL
;
716 attrFlags
&= ~CSSM_KEYATTR_PARTIAL
;
718 DSABinaryKey
*dsaBinKey
= new DSABinaryKey(dsaKey
);
723 * Obtain key size in bits.
725 void DSAKeyInfoProvider::QueryKeySizeInBits(
726 CSSM_KEY_SIZE
&keySize
)
730 if(mKey
.blobType() != CSSM_KEYBLOB_RAW
) {
731 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT
);
733 dsaKey
= rawCssmKeyToDsa(mKey
,
735 NULL
); // no param key allowed here
736 if(dsaKey
->p
!= NULL
) {
737 /* normal fully-formed key */
738 keySize
.LogicalKeySizeInBits
= BN_num_bits(dsaKey
->p
);
739 keySize
.EffectiveKeySizeInBits
= keySize
.LogicalKeySizeInBits
;
743 /* partial key, get an approximation from pub_key */
744 keySize
.LogicalKeySizeInBits
= BN_num_bits(dsaKey
->pub_key
);
746 /* and indicate this anomaly like so */
747 CssmError::throwMe(CSSMERR_CSP_APPLE_PUBLIC_KEY_INCOMPLETE
);
752 * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST
755 bool DSAKeyInfoProvider::getHashableBlob(
756 CssmAllocator
&allocator
,
757 CssmData
&blob
) // blob to hash goes here
759 /* No optimized case for DSA keys */