2 * Copyright (c) 2000-2002 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 * DH_keys.cpp - Diffie-Hellman key pair support.
25 #include <opensslUtils/opensslUtils.h>
26 #include <opensslUtils/opensslAsn1.h>
27 #include <security_cdsa_utilities/cssmdata.h>
28 #include <AppleCSPSession.h>
29 #include <AppleCSPUtils.h>
31 #include <security_utilities/debugging.h>
32 #include <Security/oidsalg.h>
33 #include <YarrowConnection.h>
35 #define dhKeyDebug(args...) secdebug("dhKey", ## args)
38 * FIXME - the CDSA Algorithm Guide claims that the incoming params argument
39 * for a GenerateAlgorithmParameters call is ignored for D-H. This means
40 * that there is no way for the caller to specify 'g' (typically 2, 3, or
41 * 5). This seems WAY bogus but we'll code to the spec for now, assuming
42 * a hard-coded default generator.
44 #define DH_GENERATOR_DEFAULT DH_GENERATOR_2
48 *** Diffie-Hellman-style BinaryKey
51 /* constructor with optional existing DSA key */
52 DHBinaryKey::DHBinaryKey(DH
*dhKey
)
57 DHBinaryKey::~DHBinaryKey()
65 void DHBinaryKey::generateKeyBlob(
68 CSSM_KEYBLOB_FORMAT
&format
,
69 AppleCSPSession
&session
,
70 const CssmKey
*paramKey
, /* optional, unused here */
71 CSSM_KEYATTR_FLAGS
&attrFlags
) /* IN/OUT */
74 switch(mKeyHeader
.KeyClass
) {
75 case CSSM_KEYCLASS_PUBLIC_KEY
:
78 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
80 format
= DH_PUB_KEY_FORMAT
;
82 case DH_PUB_KEY_FORMAT
:
83 case CSSM_KEYBLOB_RAW_FORMAT_X509
:
86 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST
:
87 /* use PKCS3 - caller won't care if we change this...right? */
88 format
= DH_PUB_KEY_FORMAT
;
91 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT
);
94 assert(mDhKey
!= NULL
);
95 CssmAutoData
encodedKey(allocator
);
96 CSSM_RETURN crtn
= DHPublicKeyEncode(mDhKey
, format
,
99 CssmError::throwMe(crtn
);
101 blob
= encodedKey
.release();
104 case CSSM_KEYCLASS_PRIVATE_KEY
:
107 case CSSM_KEYBLOB_RAW_FORMAT_NONE
:
109 format
= DH_PRIV_KEY_FORMAT
;
111 case DH_PRIV_KEY_FORMAT
:
112 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8
:
116 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST
:
119 * Use public blob; calculate it if we
120 * don't already have it.
122 assert(mDhKey
!= NULL
);
123 if(mDhKey
->pub_key
== NULL
) {
124 int irtn
= DH_generate_key(mDhKey
);
126 throwRsaDsa("DH_generate_key");
129 assert(mDhKey
->pub_key
!= NULL
);
131 BN_num_bytes(mDhKey
->pub_key
),
132 *DH_Factory::privAllocator
);
133 BN_bn2bin(mDhKey
->pub_key
, blob
);
134 format
= DH_PUB_KEY_FORMAT
;
139 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT
);
141 assert(mDhKey
!= NULL
);
142 CssmAutoData
encodedKey(allocator
);
143 CSSM_RETURN crtn
= DHPrivateKeyEncode(mDhKey
, format
,
146 CssmError::throwMe(crtn
);
148 blob
= encodedKey
.release();
152 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
157 *** Diffie-Hellman style AppleKeyPairGenContext
161 * This one is specified in, and called from, CSPFullPluginSession. Our
162 * only job is to prepare two subclass-specific BinaryKeys and call up to
163 * AppleKeyPairGenContext.
165 void DHKeyPairGenContext::generate(
166 const Context
&context
,
170 DHBinaryKey
*pubBinKey
= new DHBinaryKey();
171 DHBinaryKey
*privBinKey
= new DHBinaryKey();
174 AppleKeyPairGenContext::generate(context
,
189 * This one is specified in, and called from, AppleKeyPairGenContext
191 void DHKeyPairGenContext::generate(
192 const Context
&context
,
193 BinaryKey
&pubBinKey
,
194 BinaryKey
&privBinKey
,
198 * These casts throw exceptions if the keys are of the
199 * wrong classes, which would be a major bogon, since we created
200 * the keys in the above generate() function.
202 DHBinaryKey
&rPubBinKey
=
203 dynamic_cast<DHBinaryKey
&>(pubBinKey
);
204 DHBinaryKey
&rPrivBinKey
=
205 dynamic_cast<DHBinaryKey
&>(privBinKey
);
208 * Parameters from context:
209 * Key size in bits, required;
210 * {p,g,privKeyLength} from generateParams, optional
211 * NOTE: currently the openssl D-H imnplementation ignores the
212 * privKeyLength field.
214 keyBits
= context
.getInt(CSSM_ATTRIBUTE_KEY_LENGTH
,
215 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH
);
216 CssmData
*paramData
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_ALG_PARAMS
);
218 NSS_DHParameterBlock algParamBlock
;
219 NSS_DHParameter
&algParams
= algParamBlock
.params
;
220 uint32 privValueLen
= 0; // only nonzero from externally generated
222 SecNssCoder coder
; // for temp allocs of decoded parameters
224 if(paramData
!= NULL
) {
225 /* this contains the DER encoding of a DHParameterBlock */
227 crtn
= DHParamBlockDecode(*paramData
, algParamBlock
, coder
);
229 CssmError::throwMe(crtn
);
232 /* snag the optional private key length field */
233 if(algParams
.privateValueLength
.Data
) {
234 privValueLen
= cssmDataToInt(algParams
.privateValueLength
);
237 /* ensure caller's key size matches the incoming params */
238 size_t paramKeyBytes
;
240 paramKeyBytes
= (privValueLen
+ 7) / 8;
243 paramKeyBytes
= algParams
.prime
.Length
;
244 /* trim off possible m.s. byte of zero */
245 const unsigned char *uo
=
246 (const unsigned char *)algParams
.prime
.Data
;
251 uint32 reqBytes
= (keyBits
+ 7) / 8;
252 if(paramKeyBytes
!= reqBytes
) {
253 dhKeyDebug("DH key size mismatch (req %d param %d)",
254 (int)reqBytes
, (int)paramKeyBytes
);
255 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE
);
259 /* no alg params specified; generate them now */
260 dhKeyDebug("DH implicit alg param calculation");
261 memset(&algParamBlock
, 0, sizeof(algParamBlock
));
262 dhGenParams(keyBits
, DH_GENERATOR_DEFAULT
, 0, algParams
, coder
);
265 /* create key, stuff params into it */
266 rPrivBinKey
.mDhKey
= DH_new();
267 if(rPrivBinKey
.mDhKey
== NULL
) {
268 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
270 DH
*dhKey
= rPrivBinKey
.mDhKey
;
271 dhKey
->p
= cssmDataToBn(algParams
.prime
);
272 dhKey
->g
= cssmDataToBn(algParams
.base
);
273 dhKey
->length
= privValueLen
;
274 cspDhDebug("private DH binary key dhKey %p", dhKey
);
276 /* generate the key (both public and private capabilities) */
277 int irtn
= DH_generate_key(dhKey
);
279 throwRsaDsa("DH_generate_key");
282 /* public key is a subset */
283 rPubBinKey
.mDhKey
= DH_new();
284 if(rPubBinKey
.mDhKey
== NULL
) {
285 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
287 DH
*pubDhKey
= rPubBinKey
.mDhKey
;
288 pubDhKey
->pub_key
= BN_dup(dhKey
->pub_key
);
289 /* these params used for X509 style key blobs */
290 pubDhKey
->p
= BN_dup(dhKey
->p
);
291 pubDhKey
->g
= BN_dup(dhKey
->g
);
292 cspDhDebug("public DH binary key pubDhKey %p", pubDhKey
);
298 *** Diffie-Hellman CSPKeyInfoProvider.
300 DHKeyInfoProvider::DHKeyInfoProvider(
301 const CssmKey
&cssmKey
,
302 AppleCSPSession
&session
) :
303 CSPKeyInfoProvider(cssmKey
, session
)
307 CSPKeyInfoProvider
*DHKeyInfoProvider::provider(
308 const CssmKey
&cssmKey
,
309 AppleCSPSession
&session
)
311 switch(cssmKey
.algorithm()) {
315 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
317 switch(cssmKey
.keyClass()) {
318 case CSSM_KEYCLASS_PUBLIC_KEY
:
319 case CSSM_KEYCLASS_PRIVATE_KEY
:
322 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
324 /* OK, we'll handle this one */
325 return new DHKeyInfoProvider(cssmKey
, session
);
328 /* Given a raw key, cook up a Binary key */
329 void DHKeyInfoProvider::CssmKeyToBinary(
330 CssmKey
*paramKey
, // optional, ignored here
331 CSSM_KEYATTR_FLAGS
&attrFlags
, // IN/OUT
336 assert(mKey
.blobType() == CSSM_KEYBLOB_RAW
);
337 switch(mKey
.keyClass()) {
338 case CSSM_KEYCLASS_PUBLIC_KEY
:
339 case CSSM_KEYCLASS_PRIVATE_KEY
:
342 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
345 /* first cook up an DH key, then drop that into a BinaryKey */
346 DH
*dhKey
= rawCssmKeyToDh(mKey
);
347 DHBinaryKey
*dhBinKey
= new DHBinaryKey(dhKey
);
349 cspDhDebug("CssmKeyToBinary dhKey %p", dhKey
);
353 * Obtain key size in bits.
354 * FIXME - I doubt that this is, or can be, exactly accurate.....
356 void DHKeyInfoProvider::QueryKeySizeInBits(
357 CSSM_KEY_SIZE
&keySize
)
361 if(mKey
.blobType() != CSSM_KEYBLOB_RAW
) {
362 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT
);
364 DH
*dhKey
= rawCssmKeyToDh(mKey
);
366 /* DH_size requires the p parameter, which some public keys don't have */
367 if(dhKey
->p
!= NULL
) {
368 numBits
= DH_size(dhKey
) * 8;
371 assert(dhKey
->pub_key
!= NULL
);
372 numBits
= BN_num_bytes(dhKey
->pub_key
) * 8;
375 keySize
.LogicalKeySizeInBits
= numBits
;
376 keySize
.EffectiveKeySizeInBits
= numBits
;
380 * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST
383 bool DHKeyInfoProvider::getHashableBlob(
384 Allocator
&allocator
,
385 CssmData
&blob
) // blob to hash goes here
388 * The optimized case, a raw key in the "proper" format already.
390 assert(mKey
.blobType() == CSSM_KEYBLOB_RAW
);
391 bool useAsIs
= false;
393 switch(mKey
.keyClass()) {
394 case CSSM_KEYCLASS_PUBLIC_KEY
:
395 if(mKey
.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_PKCS3
) {
399 case CSSM_KEYCLASS_PRIVATE_KEY
:
402 /* shouldn't be here */
404 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR
);
407 const CssmData
&keyBlob
= CssmData::overlay(mKey
.KeyData
);
408 copyCssmData(keyBlob
, blob
, allocator
);
412 /* caller converts to binary and proceeds */
417 * Generate keygen parameters, stash them in a context attr array for later use
418 * when actually generating the keys.
421 void DHKeyPairGenContext::generate(
422 const Context
&context
,
424 CssmData
¶ms
, // RETURNED here,
425 uint32
&attrCount
, // here,
426 Context::Attr
* &attrs
) // and here
428 /* generate the params */
429 NSS_DHParameterBlock algParamBlock
;
431 NSS_DHParameter
&algParams
= algParamBlock
.params
;
432 dhGenParams(bitSize
, DH_GENERATOR_DEFAULT
, 0, algParams
, coder
);
434 /* drop in the required OID */
435 algParamBlock
.oid
= CSSMOID_PKCS3
;
438 * Here comes the fun part.
439 * We "return" the DER encoding of these generated params in two ways:
440 * 1. Copy out to app via the params argument, mallocing if Data ptr is NULL.
441 * The app must free this.
442 * 2. Cook up a 1-element Context::attr array containing one ALG_PARAM attr,
443 * a CSSM_DATA_PTR containing the DER encoding. We have to save a ptr to
444 * this attr array and free it, the CSSM_DATA it points to, and the DER
445 * encoding *that* points to, in our destructor.
449 CssmAutoData
aDerData(session());
451 perr
= SecNssEncodeItemOdata(&algParamBlock
, kSecAsn1DHParameterBlockTemplate
,
454 /* only known error... */
455 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
458 /* copy/release that into a mallocd CSSM_DATA. */
459 CSSM_DATA_PTR derData
= (CSSM_DATA_PTR
)session().malloc(sizeof(CSSM_DATA
));
460 *derData
= aDerData
.release();
462 /* stuff that into a one-element Attr array which we keep after returning */
464 mGenAttrs
= (Context::Attr
*)session().malloc(sizeof(Context::Attr
));
465 mGenAttrs
->AttributeType
= CSSM_ATTRIBUTE_ALG_PARAMS
;
466 mGenAttrs
->AttributeLength
= sizeof(CSSM_DATA
);
467 mGenAttrs
->Attribute
.Data
= derData
;
469 /* and "return" this stuff */
470 copyCssmData(CssmData::overlay(*derData
), params
, session());
475 /* free mGenAttrs and its referents if present */
476 void DHKeyPairGenContext::freeGenAttrs()
478 if(mGenAttrs
== NULL
) {
481 if(mGenAttrs
->Attribute
.Data
) {
482 if(mGenAttrs
->Attribute
.Data
->Data
) {
483 session().free(mGenAttrs
->Attribute
.Data
->Data
);
485 session().free(mGenAttrs
->Attribute
.Data
);
487 session().free(mGenAttrs
);
491 * Generate DSA algorithm parameters returning result
492 * into DHParameter.{prime,base,privateValueLength].
493 * This is called from both GenerateParameters and from
494 * KeyPairGenerate (if no GenerateParameters has yet been called).
496 * FIXME - privateValueLength not implemented in openssl, not here
500 void DHKeyPairGenContext::dhGenParams(
501 uint32 keySizeInBits
,
502 unsigned g
, // probably should be BIGNUM
503 int privValueLength
, // optional
504 NSS_DHParameter
&algParams
,
505 SecNssCoder
&coder
) // temp contents of algParams
508 /* validate key size */
509 if((keySizeInBits
< DH_MIN_KEY_SIZE
) ||
510 (keySizeInBits
> DH_MAX_KEY_SIZE
)) {
511 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH
);
514 /* create an openssl-style DH key with minimal setup */
515 DH
*dhKey
= DH_generate_parameters(keySizeInBits
, g
, NULL
, NULL
);
517 throwRsaDsa("DSA_generate_parameters");
520 /* stuff dhKey->{p,g,length}] into a caller's NSS_DHParameter */
521 bnToCssmData(dhKey
->p
, algParams
.prime
, coder
);
522 bnToCssmData(dhKey
->g
, algParams
.base
, coder
);
523 CSSM_DATA
&privValData
= algParams
.privateValueLength
;
524 if(privValueLength
) {
525 intToCssmData(privValueLength
, privValData
, coder
);
528 privValData
.Data
= NULL
;
529 privValData
.Length
= 0;