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/openRsaSnacc.h>
27 #include <Security/cssmdata.h>
28 #include <AppleCSP/AppleCSPSession.h>
29 #include <AppleCSP/AppleCSPUtils.h>
31 #include <Security/debugging.h>
32 #include <AppleCSP/YarrowConnection.h>
33 #include <Security/appleoids.h>
34 #include <Security/cdsaUtils.h>
35 #include <Security/asn-octs.h>
36 #include <Security/sm_vdatypes.h>
38 #define dhKeyDebug(args...) debug("dhKey", ## args)
41 * FIXME - the CDSA Algorithm Guide claims that the incoming params argument
42 * for a GenerateAlgorithmParameters call is ignored for D-H. This means
43 * that there is no way for the caller to specify 'g' (typically 2, 3, or
44 * 5). This seems WAY bogus but we'll code to the spec for now, assuming
45 * a hard-coded default generator.
47 #define DH_GENERATOR_DEFAULT DH_GENERATOR_2
51 *** Diffie-Hellman-style BinaryKey
54 /* constructor with optional existing RSA key */
55 DHBinaryKey::DHBinaryKey(DH
*dhKey
)
62 DHBinaryKey::DHBinaryKey(const CSSM_DATA
*pubBlob
)
68 DHBinaryKey::~DHBinaryKey()
71 assert(mPubKey
.Data
== NULL
);
76 assert(mDhKey
== NULL
);
77 DH_Factory::privAllocator
->free(mPubKey
.Data
);
83 void DHBinaryKey::generateKeyBlob(
84 CssmAllocator
&allocator
,
86 CSSM_KEYBLOB_FORMAT
&format
)
88 switch(mKeyHeader
.KeyClass
) {
89 case CSSM_KEYCLASS_PUBLIC_KEY
:
91 /* trivial case, just copy the public blob */
92 assert(mDhKey
== NULL
);
93 assert(mPubKey
.Data
!= NULL
);
94 format
= DH_PUB_KEY_FORMAT
;
95 copyCssmData(CssmData::overlay(mPubKey
), blob
, allocator
);
98 case CSSM_KEYCLASS_PRIVATE_KEY
:
100 assert(mDhKey
!= NULL
);
101 assert(mPubKey
.Data
== NULL
);
102 format
= DH_PRIV_KEY_FORMAT
;
103 CssmAutoData
encodedKey(allocator
);
104 CSSM_RETURN crtn
= DHPrivateKeyEncode(mDhKey
, encodedKey
);
106 CssmError::throwMe(crtn
);
108 blob
= encodedKey
.release();
112 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
116 /* for importing.... */
117 void DHBinaryKey::setPubBlob(const CSSM_DATA
*pubBlob
)
119 assert(mDhKey
== NULL
);
120 assert(mPubKey
.Data
== NULL
);
121 setUpData(mPubKey
, pubBlob
->Length
, *DH_Factory::privAllocator
);
122 memmove(mPubKey
.Data
, pubBlob
->Data
, pubBlob
->Length
);
125 /* for creating from a full DH private key... */
126 void DHBinaryKey::setPubBlob(DH
*privKey
)
128 assert(mDhKey
== NULL
);
129 assert(mPubKey
.Data
== NULL
);
130 setUpData(mPubKey
, BN_num_bytes(privKey
->pub_key
),
131 *DH_Factory::privAllocator
);
132 BN_bn2bin(privKey
->pub_key
, mPubKey
.Data
);
136 *** Diffie-Hellman style AppleKeyPairGenContext
140 * This one is specified in, and called from, CSPFullPluginSession. Our
141 * only job is to prepare two subclass-specific BinaryKeys and call up to
142 * AppleKeyPairGenContext.
144 void DHKeyPairGenContext::generate(
145 const Context
&context
,
149 DHBinaryKey
*pubBinKey
= new DHBinaryKey();
150 DHBinaryKey
*privBinKey
= new DHBinaryKey();
153 AppleKeyPairGenContext::generate(context
,
168 * obtain a 32-bit integer from a BigIntegerStr.
170 static uint32
bigIntStrToInt(
171 const BigIntegerStr
&bint
,
172 CSSM_RETURN toThrow
) // throws this if out of range
174 size_t bytes
= bint
.Len();
176 dhKeyDebug("DH integer overflow");
178 CssmError::throwMe(toThrow
);
185 const unsigned char *uo
= (const unsigned char *)bint
.Octs();
186 for(size_t i
=0; i
<bytes
; i
++) {
193 * This one is specified in, and called from, AppleKeyPairGenContext
195 void DHKeyPairGenContext::generate(
196 const Context
&context
,
197 BinaryKey
&pubBinKey
,
198 BinaryKey
&privBinKey
,
202 * These casts throw exceptions if the keys are of the
203 * wrong classes, which would be a major bogon, since we created
204 * the keys in the above generate() function.
206 DHBinaryKey
&rPubBinKey
=
207 dynamic_cast<DHBinaryKey
&>(pubBinKey
);
208 DHBinaryKey
&rPrivBinKey
=
209 dynamic_cast<DHBinaryKey
&>(privBinKey
);
212 * Parameters from context:
213 * Key size in bits, required;
214 * {p,g,privKeyLength} from generateParams, optional
215 * NOTE: currently the openssl D-H imnplementation ignores the
216 * privKeyLength field.
218 keyBits
= context
.getInt(CSSM_ATTRIBUTE_KEY_LENGTH
,
219 CSSMERR_CSP_MISSING_ATTR_KEY_LENGTH
);
220 CssmData
*paramData
= context
.get
<CssmData
>(CSSM_ATTRIBUTE_ALG_PARAMS
);
222 DHParameterBlock algParamBlock
;
223 DHParameter
*algParams
= NULL
;
224 uint32 privValueLen
= 0; // only nonzero from externally generated
227 if(paramData
!= NULL
) {
228 /* this contains the DER encoding of a DHParameterBlock */
230 SC_decodeAsnObj(*paramData
, algParamBlock
);
234 * CDSA Extension: the CDSA Algorithm Guide says that the D-H
235 * parameter block is supposed to be wrapped with its accompanying
236 * OID. However Openssl does not do this; it just exports
237 * an encoded DHParameter rather than a DHParameterBlock.
238 * For compatibility we'll try decoding the parameters as one
241 if(algParamBlock
.params
) {
242 delete algParamBlock
.params
;
243 algParamBlock
.params
= NULL
;
245 algParamBlock
.params
= new DHParameter
;
247 SC_decodeAsnObj(*paramData
, *algParamBlock
.params
);
248 dhKeyDebug("Trying openssl-style DH param decoding");
251 dhKeyDebug("openssl-style DH param decoding FAILED");
252 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS
);
256 algParams
= algParamBlock
.params
;
257 if(algParams
== NULL
) {
258 dhKeyDebug("Bad DH param decoding");
259 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS
);
262 /* snag the optional private key length field */
263 if(algParams
->privateValueLength
) {
264 privValueLen
= bigIntStrToInt(*algParams
->privateValueLength
,
265 CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS
);
268 /* ensure caller's key size matches the incoming params */
269 uint32 paramKeyBytes
;
271 paramKeyBytes
= (privValueLen
+ 7) / 8;
274 paramKeyBytes
= algParams
->prime
.Len();
275 /* trim off possible m.s. byte of zero */
276 const unsigned char *uo
=
277 (const unsigned char *)algParams
->prime
.Octs();
282 uint32 reqBytes
= (keyBits
+ 7) / 8;
283 if(paramKeyBytes
!= reqBytes
) {
284 dhKeyDebug("DH key size mismatch (req %d param %d)",
285 (int)reqBytes
, (int)paramKeyBytes
);
286 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_SIZE
);
290 /* no alg params specified; generate them now */
291 dhKeyDebug("DH implicit alg param calculation");
292 algParamBlock
.params
= new DHParameter
;
293 algParams
= algParamBlock
.params
;
294 dhGenParams(keyBits
, DH_GENERATOR_DEFAULT
, 0, *algParams
);
297 /* create key, stuff params into it */
298 rPrivBinKey
.mDhKey
= DH_new();
299 if(rPrivBinKey
.mDhKey
== NULL
) {
300 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR
);
302 DH
*dhKey
= rPrivBinKey
.mDhKey
;
303 dhKey
->p
= bigIntStrToBn(algParams
->prime
);
304 dhKey
->g
= bigIntStrToBn(algParams
->base
);
305 dhKey
->length
= privValueLen
;
307 /* generate the key (both public and private capabilities) */
308 int irtn
= DH_generate_key(dhKey
);
310 throwRsaDsa("DH_generate_key");
313 /* public key just a blob */
314 rPubBinKey
.setPubBlob(dhKey
);
320 *** Diffie-Hellman CSPKeyInfoProvider.
322 DHKeyInfoProvider::DHKeyInfoProvider(
323 const CssmKey
&cssmKey
) :
324 CSPKeyInfoProvider(cssmKey
)
326 switch(cssmKey
.algorithm()) {
330 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
332 switch(cssmKey
.keyClass()) {
333 case CSSM_KEYCLASS_PUBLIC_KEY
:
334 case CSSM_KEYCLASS_PRIVATE_KEY
:
337 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
339 /* OK, we'll handle this one */
343 /* Given a raw key, cook up a Binary key */
344 void DHKeyInfoProvider::CssmKeyToBinary(
349 assert(mKey
.blobType() == CSSM_KEYBLOB_RAW
);
350 switch(mKey
.keyClass()) {
351 case CSSM_KEYCLASS_PUBLIC_KEY
:
353 /* trivial case - no DH * */
354 DHBinaryKey
*dhKey
= new DHBinaryKey(&mKey
.KeyData
);
358 case CSSM_KEYCLASS_PRIVATE_KEY
:
360 /* first cook up an DH key, then drop that into a BinaryKey */
361 DH
*dhKey
= rawCssmKeyToDh(mKey
);
362 DHBinaryKey
*dhBinKey
= new DHBinaryKey(dhKey
);
367 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
372 * Obtain key size in bits.
373 * FIXME - I doubt that this is, or can be, exactly accurate.....
375 void DHKeyInfoProvider::QueryKeySizeInBits(
376 CSSM_KEY_SIZE
&keySize
)
380 if(mKey
.blobType() != CSSM_KEYBLOB_RAW
) {
381 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT
);
383 switch(mKey
.keyClass()) {
384 case CSSM_KEYCLASS_PUBLIC_KEY
:
386 numBits
= mKey
.KeyData
.Length
* 8;
388 case CSSM_KEYCLASS_PRIVATE_KEY
:
390 DH
*dhKey
= rawCssmKeyToDh(mKey
);
391 numBits
= DH_size(dhKey
) * 8;
396 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
398 keySize
.LogicalKeySizeInBits
= numBits
;
399 keySize
.EffectiveKeySizeInBits
= numBits
;
404 * Generate keygen parameters, stash them in a context attr array for later use
405 * when actually generating the keys.
408 void DHKeyPairGenContext::generate(
409 const Context
&context
,
411 CssmData
¶ms
, // RETURNED here,
412 uint32
&attrCount
, // here,
413 Context::Attr
* &attrs
) // and here
415 /* generate the params */
416 DHParameterBlock algParamBlock
;
417 algParamBlock
.params
= new DHParameter
;
418 DHParameter
*algParams
= algParamBlock
.params
;
419 dhGenParams(bitSize
, DH_GENERATOR_DEFAULT
, 0, *algParams
);
421 /* drop in the required OID */
422 algParamBlock
.oid
.Set(pkcs_3_arc
);
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.
436 size_t maxSize
= sizeofBigInt(algParams
->prime
) +
437 sizeofBigInt(algParams
->base
)
438 + 30; // includes oid, tag, length
439 if(algParams
->privateValueLength
) {
440 maxSize
+= sizeofBigInt(*algParams
->privateValueLength
);
442 CssmAutoData
aDerData(session());
443 SC_encodeAsnObj(algParamBlock
, aDerData
, maxSize
);
445 /* copy/release that into a mallocd CSSM_DATA. */
446 CSSM_DATA_PTR derData
= (CSSM_DATA_PTR
)session().malloc(sizeof(CSSM_DATA
));
447 *derData
= aDerData
.release();
449 /* stuff that into a one-element Attr array which we keep after returning */
451 mGenAttrs
= (Context::Attr
*)session().malloc(sizeof(Context::Attr
));
452 mGenAttrs
->AttributeType
= CSSM_ATTRIBUTE_ALG_PARAMS
;
453 mGenAttrs
->AttributeLength
= sizeof(CSSM_DATA
);
454 mGenAttrs
->Attribute
.Data
= derData
;
456 /* and "return" this stuff */
457 copyCssmData(CssmData::overlay(*derData
), params
, session());
462 /* free mGenAttrs and its referents if present */
463 void DHKeyPairGenContext::freeGenAttrs()
465 if(mGenAttrs
== NULL
) {
468 if(mGenAttrs
->Attribute
.Data
) {
469 if(mGenAttrs
->Attribute
.Data
->Data
) {
470 session().free(mGenAttrs
->Attribute
.Data
->Data
);
472 session().free(mGenAttrs
->Attribute
.Data
);
474 session().free(mGenAttrs
);
478 * Generate DSA algorithm parameters returning result
479 * into DHParameter.{prime,base,privateValueLength].
480 * This is called from both GenerateParameters and from
481 * KeyPairGenerate (if no GenerateParameters has yet been called).
483 * FIXME - privateValueLength not implemented in openssl, not here
487 void DHKeyPairGenContext::dhGenParams(
488 uint32 keySizeInBits
,
489 unsigned g
, // probably should be BIGNUM
490 int privValueLength
, // optional
491 DHParameter
&algParams
)
493 /* validate key size */
494 if((keySizeInBits
< DH_MIN_KEY_SIZE
) ||
495 (keySizeInBits
> DH_MAX_KEY_SIZE
)) {
496 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY_LENGTH
);
499 /* create an openssl-style DH key with minimal setup */
500 DH
*dhKey
= DH_generate_parameters(keySizeInBits
, g
, NULL
, NULL
);
502 throwRsaDsa("DSA_generate_parameters");
505 /* stuff dhKey->{p,g,length}] into a caller's DSAAlgParams */
506 bnToBigIntStr(dhKey
->p
, algParams
.prime
);
507 bnToBigIntStr(dhKey
->g
, algParams
.base
);
508 if(privValueLength
) {
509 algParams
.privateValueLength
= new BigIntegerStr();
510 snaccIntToBigIntegerStr(g
, *algParams
.privateValueLength
);