]> git.saurik.com Git - apple/security.git/blob - AppleCSP/DiffieHellman/DH_keys.cpp
Security-54.1.tar.gz
[apple/security.git] / AppleCSP / DiffieHellman / DH_keys.cpp
1 /*
2 * Copyright (c) 2000-2002 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 * DH_keys.cpp - Diffie-Hellman key pair support.
21 */
22
23 #include "DH_keys.h"
24 #include "DH_utils.h"
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>
30 #include <assert.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>
37
38 #define dhKeyDebug(args...) debug("dhKey", ## args)
39
40 /*
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.
46 */
47 #define DH_GENERATOR_DEFAULT DH_GENERATOR_2
48
49
50 /***
51 *** Diffie-Hellman-style BinaryKey
52 ***/
53
54 /* constructor with optional existing RSA key */
55 DHBinaryKey::DHBinaryKey(DH *dhKey)
56 : mDhKey(dhKey)
57 {
58 mPubKey.Data = NULL;
59 mPubKey.Length = 0;
60 }
61
62 DHBinaryKey::DHBinaryKey(const CSSM_DATA *pubBlob)
63 : mDhKey(NULL)
64 {
65 setPubBlob(pubBlob);
66 }
67
68 DHBinaryKey::~DHBinaryKey()
69 {
70 if(mDhKey) {
71 assert(mPubKey.Data == NULL);
72 DH_free(mDhKey);
73 mDhKey = NULL;
74 }
75 if(mPubKey.Data) {
76 assert(mDhKey == NULL);
77 DH_Factory::privAllocator->free(mPubKey.Data);
78 mPubKey.Data = NULL;
79 mPubKey.Length = 0;
80 }
81 }
82
83 void DHBinaryKey::generateKeyBlob(
84 CssmAllocator &allocator,
85 CssmData &blob,
86 CSSM_KEYBLOB_FORMAT &format)
87 {
88 switch(mKeyHeader.KeyClass) {
89 case CSSM_KEYCLASS_PUBLIC_KEY:
90 {
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);
96 break;
97 }
98 case CSSM_KEYCLASS_PRIVATE_KEY:
99 {
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);
105 if(crtn) {
106 CssmError::throwMe(crtn);
107 }
108 blob = encodedKey.release();
109 break;
110 }
111 default:
112 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
113 }
114 }
115
116 /* for importing.... */
117 void DHBinaryKey::setPubBlob(const CSSM_DATA *pubBlob)
118 {
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);
123 }
124
125 /* for creating from a full DH private key... */
126 void DHBinaryKey::setPubBlob(DH *privKey)
127 {
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);
133 }
134
135 /***
136 *** Diffie-Hellman style AppleKeyPairGenContext
137 ***/
138
139 /*
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.
143 */
144 void DHKeyPairGenContext::generate(
145 const Context &context,
146 CssmKey &pubKey,
147 CssmKey &privKey)
148 {
149 DHBinaryKey *pubBinKey = new DHBinaryKey();
150 DHBinaryKey *privBinKey = new DHBinaryKey();
151
152 try {
153 AppleKeyPairGenContext::generate(context,
154 session(),
155 pubKey,
156 pubBinKey,
157 privKey,
158 privBinKey);
159 }
160 catch (...) {
161 delete pubBinKey;
162 delete privBinKey;
163 throw;
164 }
165 }
166
167 /*
168 * obtain a 32-bit integer from a BigIntegerStr.
169 */
170 static uint32 bigIntStrToInt(
171 const BigIntegerStr &bint,
172 CSSM_RETURN toThrow) // throws this if out of range
173 {
174 size_t bytes = bint.Len();
175 if(bytes > 4) {
176 dhKeyDebug("DH integer overflow");
177 if(toThrow) {
178 CssmError::throwMe(toThrow);
179 }
180 else {
181 return 0;
182 }
183 }
184 uint32 rtn = 0;
185 const unsigned char *uo = (const unsigned char *)bint.Octs();
186 for(size_t i=0; i<bytes; i++) {
187 rtn <<= 8;
188 rtn |= uo[i];
189 }
190 return rtn;
191 }
192 /*
193 * This one is specified in, and called from, AppleKeyPairGenContext
194 */
195 void DHKeyPairGenContext::generate(
196 const Context &context,
197 BinaryKey &pubBinKey,
198 BinaryKey &privBinKey,
199 uint32 &keyBits)
200 {
201 /*
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.
205 */
206 DHBinaryKey &rPubBinKey =
207 dynamic_cast<DHBinaryKey &>(pubBinKey);
208 DHBinaryKey &rPrivBinKey =
209 dynamic_cast<DHBinaryKey &>(privBinKey);
210
211 /*
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.
217 */
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);
221
222 DHParameterBlock algParamBlock;
223 DHParameter *algParams = NULL;
224 uint32 privValueLen = 0; // only nonzero from externally generated
225 // params
226
227 if(paramData != NULL) {
228 /* this contains the DER encoding of a DHParameterBlock */
229 try {
230 SC_decodeAsnObj(*paramData, algParamBlock);
231 }
232 catch(...) {
233 /*
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
239 * of these.
240 */
241 if(algParamBlock.params) {
242 delete algParamBlock.params;
243 algParamBlock.params = NULL;
244 }
245 algParamBlock.params = new DHParameter;
246 try {
247 SC_decodeAsnObj(*paramData, *algParamBlock.params);
248 dhKeyDebug("Trying openssl-style DH param decoding");
249 }
250 catch(...) {
251 dhKeyDebug("openssl-style DH param decoding FAILED");
252 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
253 }
254 }
255
256 algParams = algParamBlock.params;
257 if(algParams == NULL) {
258 dhKeyDebug("Bad DH param decoding");
259 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
260 }
261
262 /* snag the optional private key length field */
263 if(algParams->privateValueLength) {
264 privValueLen = bigIntStrToInt(*algParams->privateValueLength,
265 CSSMERR_CSP_INVALID_ATTR_ALG_PARAMS);
266 }
267
268 /* ensure caller's key size matches the incoming params */
269 uint32 paramKeyBytes;
270 if(privValueLen) {
271 paramKeyBytes = (privValueLen + 7) / 8;
272 }
273 else {
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();
278 if(*uo == 0) {
279 paramKeyBytes--;
280 }
281 }
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);
287 }
288 }
289 else {
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);
295 }
296
297 /* create key, stuff params into it */
298 rPrivBinKey.mDhKey = DH_new();
299 if(rPrivBinKey.mDhKey == NULL) {
300 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
301 }
302 DH *dhKey = rPrivBinKey.mDhKey;
303 dhKey->p = bigIntStrToBn(algParams->prime);
304 dhKey->g = bigIntStrToBn(algParams->base);
305 dhKey->length = privValueLen;
306
307 /* generate the key (both public and private capabilities) */
308 int irtn = DH_generate_key(dhKey);
309 if(!irtn) {
310 throwRsaDsa("DH_generate_key");
311 }
312
313 /* public key just a blob */
314 rPubBinKey.setPubBlob(dhKey);
315 }
316
317
318
319 /***
320 *** Diffie-Hellman CSPKeyInfoProvider.
321 ***/
322 DHKeyInfoProvider::DHKeyInfoProvider(
323 const CssmKey &cssmKey) :
324 CSPKeyInfoProvider(cssmKey)
325 {
326 switch(cssmKey.algorithm()) {
327 case CSSM_ALGID_DH:
328 break;
329 default:
330 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
331 }
332 switch(cssmKey.keyClass()) {
333 case CSSM_KEYCLASS_PUBLIC_KEY:
334 case CSSM_KEYCLASS_PRIVATE_KEY:
335 break;
336 default:
337 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
338 }
339 /* OK, we'll handle this one */
340 return;
341 }
342
343 /* Given a raw key, cook up a Binary key */
344 void DHKeyInfoProvider::CssmKeyToBinary(
345 BinaryKey **binKey)
346 {
347 *binKey = NULL;
348
349 assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
350 switch(mKey.keyClass()) {
351 case CSSM_KEYCLASS_PUBLIC_KEY:
352 {
353 /* trivial case - no DH * */
354 DHBinaryKey *dhKey = new DHBinaryKey(&mKey.KeyData);
355 *binKey = dhKey;
356 break;
357 }
358 case CSSM_KEYCLASS_PRIVATE_KEY:
359 {
360 /* first cook up an DH key, then drop that into a BinaryKey */
361 DH *dhKey = rawCssmKeyToDh(mKey);
362 DHBinaryKey *dhBinKey = new DHBinaryKey(dhKey);
363 *binKey = dhBinKey;
364 break;
365 }
366 default:
367 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
368 }
369 }
370
371 /*
372 * Obtain key size in bits.
373 * FIXME - I doubt that this is, or can be, exactly accurate.....
374 */
375 void DHKeyInfoProvider::QueryKeySizeInBits(
376 CSSM_KEY_SIZE &keySize)
377 {
378 uint32 numBits = 0;
379
380 if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
381 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
382 }
383 switch(mKey.keyClass()) {
384 case CSSM_KEYCLASS_PUBLIC_KEY:
385 /* trivial case */
386 numBits = mKey.KeyData.Length * 8;
387 break;
388 case CSSM_KEYCLASS_PRIVATE_KEY:
389 {
390 DH *dhKey = rawCssmKeyToDh(mKey);
391 numBits = DH_size(dhKey) * 8;
392 DH_free(dhKey);
393 break;
394 }
395 default:
396 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
397 }
398 keySize.LogicalKeySizeInBits = numBits;
399 keySize.EffectiveKeySizeInBits = numBits;
400 }
401
402
403 /*
404 * Generate keygen parameters, stash them in a context attr array for later use
405 * when actually generating the keys.
406 */
407
408 void DHKeyPairGenContext::generate(
409 const Context &context,
410 uint32 bitSize,
411 CssmData &params, // RETURNED here,
412 uint32 &attrCount, // here,
413 Context::Attr * &attrs) // and here
414 {
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);
420
421 /* drop in the required OID */
422 algParamBlock.oid.Set(pkcs_3_arc);
423
424 /*
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.
433 *
434 * First, DER encode.
435 */
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);
441 }
442 CssmAutoData aDerData(session());
443 SC_encodeAsnObj(algParamBlock, aDerData, maxSize);
444
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();
448
449 /* stuff that into a one-element Attr array which we keep after returning */
450 freeGenAttrs();
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;
455
456 /* and "return" this stuff */
457 copyCssmData(CssmData::overlay(*derData), params, session());
458 attrCount = 1;
459 attrs = mGenAttrs;
460 }
461
462 /* free mGenAttrs and its referents if present */
463 void DHKeyPairGenContext::freeGenAttrs()
464 {
465 if(mGenAttrs == NULL) {
466 return;
467 }
468 if(mGenAttrs->Attribute.Data) {
469 if(mGenAttrs->Attribute.Data->Data) {
470 session().free(mGenAttrs->Attribute.Data->Data);
471 }
472 session().free(mGenAttrs->Attribute.Data);
473 }
474 session().free(mGenAttrs);
475 }
476
477 /*
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).
482 *
483 * FIXME - privateValueLength not implemented in openssl, not here
484 * either for now.
485 */
486
487 void DHKeyPairGenContext::dhGenParams(
488 uint32 keySizeInBits,
489 unsigned g, // probably should be BIGNUM
490 int privValueLength, // optional
491 DHParameter &algParams)
492 {
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);
497 }
498
499 /* create an openssl-style DH key with minimal setup */
500 DH *dhKey = DH_generate_parameters(keySizeInBits, g, NULL, NULL);
501 if(dhKey == NULL) {
502 throwRsaDsa("DSA_generate_parameters");
503 }
504
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);
511 }
512 DH_free(dhKey);
513 }
514