]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_csp/lib/DH_keys.cpp
Security-59306.120.7.tar.gz
[apple/security.git] / OSX / libsecurity_apple_csp / lib / DH_keys.cpp
1 /*
2 * Copyright (c) 2000-2002,2011-2012,2014 Apple 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/opensslAsn1.h>
27 #include <security_cdsa_utilities/cssmdata.h>
28 #include <AppleCSPSession.h>
29 #include <AppleCSPUtils.h>
30 #include <assert.h>
31 #include <security_utilities/debugging.h>
32 #include <Security/oidsalg.h>
33 #include <YarrowConnection.h>
34
35 #define dhKeyDebug(args...) secinfo("dhKey", ## args)
36
37 /*
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.
43 */
44 #define DH_GENERATOR_DEFAULT DH_GENERATOR_2
45
46
47 /***
48 *** Diffie-Hellman-style BinaryKey
49 ***/
50
51 /* constructor with optional existing DSA key */
52 DHBinaryKey::DHBinaryKey(DH *dhKey)
53 : mDhKey(dhKey)
54 {
55 }
56
57 DHBinaryKey::~DHBinaryKey()
58 {
59 if(mDhKey) {
60 DH_free(mDhKey);
61 mDhKey = NULL;
62 }
63 }
64
65 void DHBinaryKey::generateKeyBlob(
66 Allocator &allocator,
67 CssmData &blob,
68 CSSM_KEYBLOB_FORMAT &format,
69 AppleCSPSession &session,
70 const CssmKey *paramKey, /* optional, unused here */
71 CSSM_KEYATTR_FLAGS &attrFlags) /* IN/OUT */
72 {
73
74 switch(mKeyHeader.KeyClass) {
75 case CSSM_KEYCLASS_PUBLIC_KEY:
76 {
77 switch(format) {
78 case CSSM_KEYBLOB_RAW_FORMAT_NONE:
79 // take default
80 format = DH_PUB_KEY_FORMAT;
81 break;
82 case DH_PUB_KEY_FORMAT:
83 case CSSM_KEYBLOB_RAW_FORMAT_X509:
84 // proceed
85 break;
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;
89 break;
90 default:
91 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
92 }
93
94 assert(mDhKey != NULL);
95 CssmAutoData encodedKey(allocator);
96 CSSM_RETURN crtn = DHPublicKeyEncode(mDhKey, format,
97 encodedKey);
98 if(crtn) {
99 CssmError::throwMe(crtn);
100 }
101 blob = encodedKey.release();
102 break;
103 }
104 case CSSM_KEYCLASS_PRIVATE_KEY:
105 {
106 switch(format) {
107 case CSSM_KEYBLOB_RAW_FORMAT_NONE:
108 // i.e., use default
109 format = DH_PRIV_KEY_FORMAT;
110 break;
111 case DH_PRIV_KEY_FORMAT:
112 case CSSM_KEYBLOB_RAW_FORMAT_PKCS8:
113 // proceed
114 break;
115
116 case CSSM_KEYBLOB_RAW_FORMAT_DIGEST:
117 {
118 /*
119 * Use public blob; calculate it if we
120 * don't already have it.
121 */
122 assert(mDhKey != NULL);
123 if(mDhKey->pub_key == NULL) {
124 int irtn = DH_generate_key(mDhKey);
125 if(!irtn) {
126 throwRsaDsa("DH_generate_key");
127 }
128 }
129 assert(mDhKey->pub_key != NULL);
130 setUpData(blob,
131 BN_num_bytes(mDhKey->pub_key),
132 *DH_Factory::privAllocator);
133 BN_bn2bin(mDhKey->pub_key, blob);
134 format = DH_PUB_KEY_FORMAT;
135 return;
136 }
137
138 default:
139 CssmError::throwMe(CSSMERR_CSP_UNSUPPORTED_KEY_FORMAT);
140 }
141 assert(mDhKey != NULL);
142 CssmAutoData encodedKey(allocator);
143 CSSM_RETURN crtn = DHPrivateKeyEncode(mDhKey, format,
144 encodedKey);
145 if(crtn) {
146 CssmError::throwMe(crtn);
147 }
148 blob = encodedKey.release();
149 break;
150 }
151 default:
152 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
153 }
154 }
155
156 /***
157 *** Diffie-Hellman style AppleKeyPairGenContext
158 ***/
159
160 /*
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.
164 */
165 void DHKeyPairGenContext::generate(
166 const Context &context,
167 CssmKey &pubKey,
168 CssmKey &privKey)
169 {
170 DHBinaryKey *pubBinKey = new DHBinaryKey();
171 DHBinaryKey *privBinKey = new DHBinaryKey();
172
173 try {
174 AppleKeyPairGenContext::generate(context,
175 session(),
176 pubKey,
177 pubBinKey,
178 privKey,
179 privBinKey);
180 }
181 catch (...) {
182 delete pubBinKey;
183 delete privBinKey;
184 throw;
185 }
186 }
187
188 /*
189 * This one is specified in, and called from, AppleKeyPairGenContext
190 */
191 void DHKeyPairGenContext::generate(
192 const Context &context,
193 BinaryKey &pubBinKey,
194 BinaryKey &privBinKey,
195 uint32 &keyBits)
196 {
197 /*
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.
201 */
202 DHBinaryKey &rPubBinKey =
203 dynamic_cast<DHBinaryKey &>(pubBinKey);
204 DHBinaryKey &rPrivBinKey =
205 dynamic_cast<DHBinaryKey &>(privBinKey);
206
207 /*
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.
213 */
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);
217
218 NSS_DHParameterBlock algParamBlock;
219 NSS_DHParameter &algParams = algParamBlock.params;
220 uint32 privValueLen = 0; // only nonzero from externally generated
221 // params
222 SecNssCoder coder; // for temp allocs of decoded parameters
223
224 if(paramData != NULL) {
225 /* this contains the DER encoding of a DHParameterBlock */
226 CSSM_RETURN crtn;
227 crtn = DHParamBlockDecode(*paramData, algParamBlock, coder);
228 if(crtn) {
229 CssmError::throwMe(crtn);
230 }
231
232 /* snag the optional private key length field */
233 if(algParams.privateValueLength.Data) {
234 privValueLen = cssmDataToInt(algParams.privateValueLength);
235 }
236
237 /* ensure caller's key size matches the incoming params */
238 size_t paramKeyBytes;
239 if(privValueLen) {
240 paramKeyBytes = (privValueLen + 7) / 8;
241 }
242 else {
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;
247 if(*uo == 0) {
248 paramKeyBytes--;
249 }
250 }
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);
256 }
257 }
258 else {
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);
263 }
264
265 /* create key, stuff params into it */
266 rPrivBinKey.mDhKey = DH_new();
267 if(rPrivBinKey.mDhKey == NULL) {
268 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
269 }
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);
275
276 /* generate the key (both public and private capabilities) */
277 int irtn = DH_generate_key(dhKey);
278 if(!irtn) {
279 throwRsaDsa("DH_generate_key");
280 }
281
282 /* public key is a subset */
283 rPubBinKey.mDhKey = DH_new();
284 if(rPubBinKey.mDhKey == NULL) {
285 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
286 }
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);
293 }
294
295
296
297 /***
298 *** Diffie-Hellman CSPKeyInfoProvider.
299 ***/
300 DHKeyInfoProvider::DHKeyInfoProvider(
301 const CssmKey &cssmKey,
302 AppleCSPSession &session) :
303 CSPKeyInfoProvider(cssmKey, session)
304 {
305 }
306
307 CSPKeyInfoProvider *DHKeyInfoProvider::provider(
308 const CssmKey &cssmKey,
309 AppleCSPSession &session)
310 {
311 switch(cssmKey.algorithm()) {
312 case CSSM_ALGID_DH:
313 break;
314 default:
315 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
316 }
317 switch(cssmKey.keyClass()) {
318 case CSSM_KEYCLASS_PUBLIC_KEY:
319 case CSSM_KEYCLASS_PRIVATE_KEY:
320 break;
321 default:
322 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
323 }
324 /* OK, we'll handle this one */
325 return new DHKeyInfoProvider(cssmKey, session);
326 }
327
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
332 BinaryKey **binKey)
333 {
334 *binKey = NULL;
335
336 assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
337 switch(mKey.keyClass()) {
338 case CSSM_KEYCLASS_PUBLIC_KEY:
339 case CSSM_KEYCLASS_PRIVATE_KEY:
340 break;
341 default:
342 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
343 }
344
345 /* first cook up an DH key, then drop that into a BinaryKey */
346 DH *dhKey = rawCssmKeyToDh(mKey);
347 DHBinaryKey *dhBinKey = new DHBinaryKey(dhKey);
348 *binKey = dhBinKey;
349 cspDhDebug("CssmKeyToBinary dhKey %p", dhKey);
350 }
351
352 /*
353 * Obtain key size in bits.
354 * FIXME - I doubt that this is, or can be, exactly accurate.....
355 */
356 void DHKeyInfoProvider::QueryKeySizeInBits(
357 CSSM_KEY_SIZE &keySize)
358 {
359 uint32 numBits = 0;
360
361 if(mKey.blobType() != CSSM_KEYBLOB_RAW) {
362 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_FORMAT);
363 }
364 DH *dhKey = rawCssmKeyToDh(mKey);
365
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;
369 }
370 else {
371 assert(dhKey->pub_key != NULL);
372 numBits = BN_num_bytes(dhKey->pub_key) * 8;
373 }
374 DH_free(dhKey);
375 keySize.LogicalKeySizeInBits = numBits;
376 keySize.EffectiveKeySizeInBits = numBits;
377 }
378
379 /*
380 * Obtain blob suitable for hashing in CSSM_APPLECSP_KEYDIGEST
381 * passthrough.
382 */
383 bool DHKeyInfoProvider::getHashableBlob(
384 Allocator &allocator,
385 CssmData &blob) // blob to hash goes here
386 {
387 /*
388 * The optimized case, a raw key in the "proper" format already.
389 */
390 assert(mKey.blobType() == CSSM_KEYBLOB_RAW);
391 bool useAsIs = false;
392
393 switch(mKey.keyClass()) {
394 case CSSM_KEYCLASS_PUBLIC_KEY:
395 if(mKey.blobFormat() == CSSM_KEYBLOB_RAW_FORMAT_PKCS3) {
396 useAsIs = true;
397 }
398 break;
399 case CSSM_KEYCLASS_PRIVATE_KEY:
400 break;
401 default:
402 /* shouldn't be here */
403 assert(0);
404 CssmError::throwMe(CSSMERR_CSP_INTERNAL_ERROR);
405 }
406 if(useAsIs) {
407 const CssmData &keyBlob = CssmData::overlay(mKey.KeyData);
408 copyCssmData(keyBlob, blob, allocator);
409 return true;
410 }
411
412 /* caller converts to binary and proceeds */
413 return false;
414 }
415
416 /*
417 * Generate keygen parameters, stash them in a context attr array for later use
418 * when actually generating the keys.
419 */
420
421 void DHKeyPairGenContext::generate(
422 const Context &context,
423 uint32 bitSize,
424 CssmData &params, // RETURNED here,
425 uint32 &attrCount, // here,
426 Context::Attr * &attrs) // and here
427 {
428 /* generate the params */
429 NSS_DHParameterBlock algParamBlock;
430 SecNssCoder coder;
431 NSS_DHParameter &algParams = algParamBlock.params;
432 dhGenParams(bitSize, DH_GENERATOR_DEFAULT, 0, algParams, coder);
433
434 /* drop in the required OID */
435 algParamBlock.oid = CSSMOID_PKCS3;
436
437 /*
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.
446 *
447 * First, DER encode.
448 */
449 CssmAutoData aDerData(session());
450 PRErrorCode perr;
451 perr = SecNssEncodeItemOdata(&algParamBlock, kSecAsn1DHParameterBlockTemplate,
452 aDerData);
453 if(perr) {
454 /* only known error... */
455 CssmError::throwMe(CSSMERR_CSP_MEMORY_ERROR);
456 }
457
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();
461
462 /* stuff that into a one-element Attr array which we keep after returning */
463 freeGenAttrs();
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;
468
469 /* and "return" this stuff */
470 copyCssmData(CssmData::overlay(*derData), params, session());
471 attrCount = 1;
472 attrs = mGenAttrs;
473 }
474
475 /* free mGenAttrs and its referents if present */
476 void DHKeyPairGenContext::freeGenAttrs()
477 {
478 if(mGenAttrs == NULL) {
479 return;
480 }
481 if(mGenAttrs->Attribute.Data) {
482 if(mGenAttrs->Attribute.Data->Data) {
483 session().free(mGenAttrs->Attribute.Data->Data);
484 }
485 session().free(mGenAttrs->Attribute.Data);
486 }
487 session().free(mGenAttrs);
488 }
489
490 /*
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).
495 *
496 * FIXME - privateValueLength not implemented in openssl, not here
497 * either for now.
498 */
499
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
506 // mallocd here
507 {
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);
512 }
513
514 /* create an openssl-style DH key with minimal setup */
515 DH *dhKey = DH_generate_parameters(keySizeInBits, g, NULL, NULL);
516 if(dhKey == NULL) {
517 throwRsaDsa("DSA_generate_parameters");
518 }
519
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);
526 }
527 else {
528 privValData.Data = NULL;
529 privValData.Length = 0;
530 }
531 DH_free(dhKey);
532 }
533