]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_apple_csp/lib/wrapKey.cpp
Security-59306.120.7.tar.gz
[apple/security.git] / OSX / libsecurity_apple_csp / lib / wrapKey.cpp
1 /*
2 * Copyright (c) 2000-2001,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 // wrapKey.cpp - wrap/unwrap key functions for AppleCSPSession
21 //
22
23 /*
24 * Currently the Security Server wraps public keys when they're stored, so we have
25 * to allow this. We might not want to do this in the real world.
26 */
27 #define ALLOW_PUB_KEY_WRAP 1
28
29 #include "AppleCSPSession.h"
30 #include "AppleCSPUtils.h"
31 #include "AppleCSPKeys.h"
32 #include "pkcs8.h"
33 #include "cspdebugging.h"
34
35 /*
36 * Wrap key function. Used for two things:
37 *
38 * -- Encrypt and encode a private or session key for export to
39 * a foreign system or program. Any type of keys may be used
40 * for the unwrapped key and the wrapping (encrypting) key,
41 * as long as this CSP understands those keys. The context
42 * must be of class ALGCLASS_SYMMETRIC or ALGCLASS_ASYMMETRIC,
43 * matching the wrapping key.
44 *
45 * In the absence of an explicit CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
46 * attribute, private keys will be PKCS8 wrapped; session keys will be
47 * PKCS7 wrapped. Both input keys may be in raw or reference
48 * format. Wrapped key will have BlobType CSSM_KEYBLOB_WRAPPED.
49 *
50 * -- Convert a reference key to a RAW key (with no encrypting).
51 * This is called a NULL wrap; no wrapping key need be present in
52 * the context, but the context must be of class
53 * ALGCLASS_SYMMETRIC and algorithm ALGID_NONE.
54 *
55 * There are serious inconsistencies in the specification of wrap
56 * algorithms to be used in the various CDSA specs (c914.pdf,
57 * CSP Behavior spec) and between those specs and the PKCS standards
58 * PKCS7, PKCS8, RFC2630). Here is what this module implements:
59 *
60 * On a wrap key op, the caller can add a CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
61 * attribute to the context to specify the wrapping algorithm to be used.
62 * If it's there, that's what we use if appropriate for the incoming key
63 * types. Otherwise we figure out a reasonable default from the incoming
64 * key types. The wrapped key always has the appropriate KeyHeader.Format
65 * field set indicating how it was wrapped. Defaults are shows below.
66 *
67 * The format CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM is used to indicate
68 * a modified CMS-style wrapping which is similar to that specified in
69 * RFC2630, with some modification.
70 *
71 * Default wrapping if none specified based on ther unwrapped key as
72 * follows:
73 *
74 * UnwrappedKey Wrap format
75 * ------------ -----------
76 * Symmetric PKCS7
77 * Public APPLE_CUSTOM
78 * FEE private APPLE_CUSTOM
79 * Other private PKCS8
80 */
81
82 void AppleCSPSession::WrapKey(
83 CSSM_CC_HANDLE CCHandle,
84 const Context &Context,
85 const AccessCredentials &AccessCred,
86 const CssmKey &UnwrappedKey,
87 const CssmData *DescriptiveData,
88 CssmKey &WrappedKey,
89 CSSM_PRIVILEGE Privilege)
90 {
91 CssmKey::Header &wrappedHdr = WrappedKey.header();
92 bool isNullWrap = false;
93 CssmKey *wrappingKey = NULL;
94 CSSM_KEYBLOB_FORMAT wrapFormat;
95
96 switch(UnwrappedKey.keyClass()) {
97 case CSSM_KEYCLASS_PUBLIC_KEY:
98 case CSSM_KEYCLASS_PRIVATE_KEY:
99 case CSSM_KEYCLASS_SESSION_KEY:
100 break;
101 default:
102 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
103 }
104
105 /* wrapping key only required for non-NULL wrap */
106 wrappingKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
107 if(wrappingKey == NULL) {
108 if((Context.algorithm() == CSSM_ALGID_NONE) &&
109 (Context.type() == CSSM_ALGCLASS_SYMMETRIC)) {
110 // NULL wrap, OK
111 isNullWrap = true;
112 }
113 else {
114 errorLog0("WrapKey: missing wrapping key\n");
115 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY);
116 }
117 }
118
119 /*
120 * Validate misc. params as best we can
121 */
122 if(isNullWrap) {
123 wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_NONE;
124 }
125 else {
126 /*
127 * Can only wrap session and private keys.
128 */
129 #if !ALLOW_PUB_KEY_WRAP
130 if(UnwrappedKey.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY) {
131 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
132 }
133 #endif /* ALLOW_PUB_KEY_WRAP */
134 cspValidateIntendedKeyUsage(&wrappingKey->KeyHeader, CSSM_KEYUSE_WRAP);
135 cspVerifyKeyTimes(wrappingKey->KeyHeader);
136
137 /*
138 * make sure wrapping key type matches context
139 */
140 CSSM_CONTEXT_TYPE wrapType;
141 switch(wrappingKey->KeyHeader.KeyClass) {
142 case CSSM_KEYCLASS_PUBLIC_KEY:
143 case CSSM_KEYCLASS_PRIVATE_KEY:
144 wrapType = CSSM_ALGCLASS_ASYMMETRIC;
145 break;
146 case CSSM_KEYCLASS_SESSION_KEY:
147 wrapType = CSSM_ALGCLASS_SYMMETRIC;
148 break;
149 default:
150 errorLog0("WrapKey: bad class of wrappingKey\n");
151 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY);
152 }
153 if(wrapType != Context.type()) {
154 errorLog0("WrapKey: mismatch wrappingKey/contextType\n");
155 CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
156 }
157 if(Context.algorithm() == CSSM_ALGID_NONE) {
158 errorLog0("WrapKey: null wrap alg, non-null key\n");
159 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
160 }
161
162 /*
163 * Get optional wrap format, set default per incoming keys
164 * Note: no such atrribute ==> 0 ==> FORMAT_NONE, which we
165 * take to mean "use the default".
166 */
167 wrapFormat = Context.getInt(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT);
168 if(wrapFormat == CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) {
169 /* figure out a default based on unwrapped key */
170 switch(UnwrappedKey.keyClass()) {
171 case CSSM_KEYCLASS_SESSION_KEY:
172 wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
173 break;
174 case CSSM_KEYCLASS_PUBLIC_KEY:
175 wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM;
176 break;
177 case CSSM_KEYCLASS_PRIVATE_KEY:
178 switch(UnwrappedKey.algorithm()) {
179 case CSSM_ALGID_FEE:
180 wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM;
181 break;
182 default:
183 wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8;
184 break;
185 }
186 break;
187 default:
188 /* NOT REACHED - checked above */
189 break;
190 }
191 } /* no format present or FORMAT_NONE */
192 }
193
194 /* make sure we have a valid format here */
195 switch(wrapFormat) {
196 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
197 if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
198 /* this wrapping style only for symmetric keys */
199 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
200 }
201 break;
202 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
203 case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
204 if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
205 /* these wrapping styles only for private keys */
206 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
207 }
208 break;
209 case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
210 /* no restrictions (well AES can't be the wrap alg but that will
211 * be caught later */
212 break;
213 case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
214 /* RSA private key, reference format, only */
215 if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
216 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
217 }
218 if(UnwrappedKey.algorithm() != CSSM_ALGID_RSA) {
219 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
220 }
221 if(UnwrappedKey.blobType() != CSSM_KEYBLOB_REFERENCE) {
222 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
223 }
224 break;
225 case CSSM_KEYBLOB_WRAPPED_FORMAT_NONE:
226 if(isNullWrap) {
227 /* only time this is OK */
228 break;
229 }
230 /* else fall thru */
231 default:
232 dprintf1("KeyWrap: invalid wrapFormat (%d)\n", (int)wrapFormat);
233 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT);
234 }
235 /* get the blob to be wrappped */
236 CssmData rawBlob;
237 bool allocdRawBlob = false;
238 CSSM_KEYBLOB_FORMAT rawFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE;
239
240 /*
241 * Outgoing same as incoming unless a partial key is completed during
242 * generateKeyBlob()
243 */
244 const CssmKey::Header &unwrappedHdr = UnwrappedKey.header();
245 CSSM_KEYATTR_FLAGS unwrappedKeyAttrFlags = unwrappedHdr.KeyAttr;
246
247 switch(UnwrappedKey.blobType()) {
248 case CSSM_KEYBLOB_RAW:
249 /*
250 * Trivial case - we already have the blob.
251 * This op - wrapping a raw key - is not supported for the
252 * CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1 format since that doesn't
253 * operate on a key blob.
254 */
255 rawBlob = CssmData::overlay(UnwrappedKey.KeyData);
256 rawFormat = UnwrappedKey.blobFormat();
257 break;
258 case CSSM_KEYBLOB_REFERENCE:
259 /* get binary key, then get blob from it */
260 {
261 BinaryKey &binKey = lookupRefKey(UnwrappedKey);
262
263 /*
264 * Subsequent tests for extractability: don't trust the
265 * caller's header; use the one in the BinaryKey.
266 */
267 CSSM_KEYATTR_FLAGS keyAttr = binKey.mKeyHeader.KeyAttr;
268 if(!(keyAttr & CSSM_KEYATTR_EXTRACTABLE)) {
269 /* this key not extractable in any form */
270 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
271 }
272
273 /*
274 * CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1: we're ready to roll;
275 * all we need is the reference key.
276 */
277 if(wrapFormat == CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1) {
278 break;
279 }
280
281 /*
282 * Null wrap - prevent caller from obtaining
283 * clear bits if CSSM_KEYATTR_SENSITIVE
284 */
285 if(isNullWrap && (keyAttr & CSSM_KEYATTR_SENSITIVE)) {
286 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
287 }
288
289 /*
290 * Special case for PKCS8 and openssl: need to get blob of a specific
291 * algorithm-dependent format. Caller can override our
292 * preference with a
293 * CSSM_ATTRIBUTE_{PRIVATE,PUBLIC,SESSION}_KEY_FORMAT
294 * context attribute.
295 */
296 rawFormat = requestedKeyFormat(Context, UnwrappedKey);
297 if(rawFormat == CSSM_KEYBLOB_RAW_FORMAT_NONE) {
298 CSSM_ALGORITHMS keyAlg = binKey.mKeyHeader.AlgorithmId;
299 switch(wrapFormat) {
300 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
301 rawFormat = pkcs8RawKeyFormat(keyAlg);
302 break;
303 case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
304 rawFormat = opensslRawKeyFormat(keyAlg);
305 break;
306 default:
307 /* punt and take default for key type */
308 break;
309 }
310 }
311
312 /*
313 * DescriptiveData for encoding, currently only used for
314 * SSH1 keys.
315 */
316 if((DescriptiveData != NULL) && (DescriptiveData->Length != 0)) {
317 binKey.descData(*DescriptiveData);
318 }
319
320 /* optional parameter-bearing key */
321 CssmKey *paramKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
322 binKey.generateKeyBlob(privAllocator,
323 rawBlob,
324 rawFormat,
325 *this,
326 paramKey,
327 unwrappedKeyAttrFlags);
328 }
329 allocdRawBlob = true; // remember - we need to free
330 break;
331
332 default:
333 errorLog0("WrapKey: bad unwrappedKey BlobType\n");
334 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
335 }
336
337 /*
338 * Prepare outgoing header.
339 */
340 setKeyHeader(wrappedHdr,
341 plugin.myGuid(),
342 unwrappedHdr.algorithm(), // same as incoming
343 unwrappedHdr.keyClass(), // same as incoming
344 unwrappedKeyAttrFlags,
345 unwrappedHdr.KeyUsage);
346 wrappedHdr.LogicalKeySizeInBits = unwrappedHdr.LogicalKeySizeInBits;
347 wrappedHdr.WrapAlgorithmId = Context.algorithm(); // true for null
348 // and non-Null
349 wrappedHdr.StartDate = unwrappedHdr.StartDate;
350 wrappedHdr.EndDate = unwrappedHdr.EndDate;
351 wrappedHdr.Format = wrapFormat;
352 if(isNullWrap) {
353 wrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
354 }
355 else {
356 wrappedHdr.BlobType = CSSM_KEYBLOB_WRAPPED;
357 }
358
359 /*
360 * special cases - break out here for Apple Custom and OpenSSHv1
361 */
362 if(!isNullWrap) {
363 switch(wrapFormat) {
364 case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
365 try {
366 WrapKeyCms(CCHandle,
367 Context,
368 AccessCred,
369 UnwrappedKey,
370 rawBlob,
371 allocdRawBlob,
372 DescriptiveData,
373 WrappedKey,
374 Privilege);
375 }
376 catch(...) {
377 if(allocdRawBlob) {
378 freeCssmData(rawBlob, privAllocator);
379 }
380 throw;
381 }
382 if(allocdRawBlob) {
383 freeCssmData(rawBlob, privAllocator);
384 }
385 return;
386 case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
387 {
388 /*
389 * 1. We don't have to worry about allocdRawBlob since this
390 * operation only works on reference keys and we did not
391 * obtain the raw blob from the BinaryKey.
392 * 2. This is a redundant lookupRefKey, I know, but since
393 * that returns a reference, it would just be too messy to have
394 * the previous call be in the same scope as this.
395 */
396 BinaryKey &binKey = lookupRefKey(UnwrappedKey);
397 WrapKeyOpenSSH1(CCHandle,
398 Context,
399 AccessCred,
400 binKey,
401 rawBlob,
402 allocdRawBlob,
403 DescriptiveData,
404 WrappedKey,
405 Privilege);
406 return;
407 }
408 default:
409 /* proceed to encrypt blob */
410 break;
411 }
412 } /* !isNullWrap */
413
414
415 /*
416 * Generate wrapped blob. Careful, we need to conditionally free
417 * rawBlob on error.
418 */
419 CssmData encryptedBlob;
420 CssmData remData;
421 WrappedKey.KeyData.Data = NULL; // ignore possible incoming KeyData
422 WrappedKey.KeyData.Length = 0;
423
424 try {
425 if(isNullWrap) {
426 /* copy raw blob to caller's wrappedKey */
427 copyCssmData(rawBlob,
428 CssmData::overlay(WrappedKey.KeyData),
429 normAllocator);
430 wrappedHdr.Format = rawFormat;
431 }
432 else {
433 /* encrypt rawBlob using caller's context, then encode to
434 * WrappedKey.KeyData */
435 CSSM_SIZE bytesEncrypted;
436 EncryptData(CCHandle,
437 Context,
438 &rawBlob, // ClearBufs[]
439 1, // ClearBufCount
440 &encryptedBlob, // CipherBufs[],
441 1, // CipherBufCount,
442 bytesEncrypted,
443 remData,
444 Privilege);
445
446 // I'm not 100% sure about this....
447 assert(remData.Length == 0);
448 encryptedBlob.Length = bytesEncrypted;
449 WrappedKey.KeyData = encryptedBlob;
450 wrappedHdr.BlobType = CSSM_KEYBLOB_WRAPPED;
451 // OK to be zero or not present
452 wrappedHdr.WrapMode = Context.getInt(
453 CSSM_ATTRIBUTE_MODE);
454 }
455 }
456 catch (...) {
457 errorLog0("WrapKey: EncryptData() threw exception\n");
458 if(allocdRawBlob) {
459 freeCssmData(rawBlob, privAllocator);
460 }
461 freeCssmData(remData,normAllocator);
462 throw;
463 }
464 if(allocdRawBlob) {
465 freeCssmData(rawBlob, privAllocator);
466 }
467 freeCssmData(remData, normAllocator);
468 }
469
470 /*
471 * Unwrap key function. Used for:
472 *
473 * -- Given key of BlobType CSSM_KEYBLOB_WRAPPED, decode and decrypt
474 * it, yielding a key in either raw or reference format. Unwrapping
475 * key may be either raw or reference. The context must match
476 * the unwrapping key (ALGCLASS_SYMMETRIC or ALGCLASS_ASYMMETRIC).
477 *
478 * Private keys are assumed to be PKCS8 encoded; session keys
479 * are assumed to be PKCS7 encoded.
480 *
481 * -- Convert a Raw key to a reference key (with no decrypting).
482 * This is called a NULL unwrap; no unwrapping key need be present in
483 * the context, but the context must be of class
484 * ALGCLASS_SYMMETRIC and algorithm ALGID_NONE.
485 */
486 void AppleCSPSession::UnwrapKey(
487 CSSM_CC_HANDLE CCHandle,
488 const Context &Context,
489 const CssmKey *PublicKey,
490 const CssmKey &WrappedKey,
491 uint32 KeyUsage,
492 uint32 KeyAttr,
493 const CssmData *KeyLabel,
494 const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry,
495 CssmKey &UnwrappedKey,
496 CssmData &DescriptiveData,
497 CSSM_PRIVILEGE Privilege)
498 {
499 bool isNullUnwrap = false;
500 CssmKey *unwrappingKey = NULL;
501 cspKeyType keyType; // CKT_Public, etc.
502 CSSM_KEYBLOB_FORMAT wrapFormat = WrappedKey.blobFormat();
503
504 /* obtain unwrapping key if present */
505 unwrappingKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_KEY);
506 if(unwrappingKey == NULL) {
507 if((Context.algorithm() == CSSM_ALGID_NONE) &&
508 (Context.type() == CSSM_ALGCLASS_SYMMETRIC)) {
509 // NULL unwrap, OK
510 isNullUnwrap = true;
511 }
512 else {
513 errorLog0("UnwrapKey: missing wrapping key\n");
514 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY);
515 }
516 }
517
518 /*
519 * validate unwrappingKey
520 */
521 if(!isNullUnwrap) {
522 /* make sure unwrapping key type matches context */
523 CSSM_CONTEXT_TYPE unwrapType;
524 switch(unwrappingKey->KeyHeader.KeyClass) {
525 case CSSM_KEYCLASS_PUBLIC_KEY:
526 case CSSM_KEYCLASS_PRIVATE_KEY:
527 unwrapType = CSSM_ALGCLASS_ASYMMETRIC;
528 break;
529 case CSSM_KEYCLASS_SESSION_KEY:
530 unwrapType = CSSM_ALGCLASS_SYMMETRIC;
531 break;
532 default:
533 errorLog0("UnwrapKey: bad class of wrappingKey\n");
534 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY);
535 }
536 if(unwrapType != Context.type()) {
537 errorLog0("UnwrapKey: mismatch unwrappingKey/contextType\n");
538 CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT);
539 }
540 if(Context.algorithm() == CSSM_ALGID_NONE) {
541 errorLog0("UnwrapKey: null wrap alg, non-null key\n");
542 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
543 }
544 cspValidateIntendedKeyUsage(&unwrappingKey->KeyHeader, CSSM_KEYUSE_UNWRAP);
545 cspVerifyKeyTimes(unwrappingKey->KeyHeader);
546 }
547
548 /* validate WrappedKey */
549 switch(WrappedKey.keyClass()) {
550 case CSSM_KEYCLASS_PUBLIC_KEY:
551 #if !ALLOW_PUB_KEY_WRAP
552 if(!isNullUnwrap) {
553 errorLog0("UnwrapKey: unwrap of public key illegal\n");
554 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
555 }
556 #endif /* ALLOW_PUB_KEY_WRAP */
557 keyType = CKT_Public;
558 break;
559 case CSSM_KEYCLASS_PRIVATE_KEY:
560 keyType = CKT_Private;
561 break;
562 case CSSM_KEYCLASS_SESSION_KEY:
563 keyType = CKT_Session;
564 break;
565 default:
566 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
567 }
568 if(isNullUnwrap) {
569 if(WrappedKey.blobType() != CSSM_KEYBLOB_RAW) {
570 errorLog0("UnwrapKey: expected raw blobType\n");
571 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
572 }
573 }
574 else {
575 if(WrappedKey.blobType() != CSSM_KEYBLOB_WRAPPED) {
576 errorLog0("UnwrapKey: expected wrapped blobType\n");
577 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
578 }
579 }
580
581 /* validate requested storage and usage */
582 cspKeyStorage keyStorage = cspParseKeyAttr(keyType, KeyAttr);
583 switch(keyStorage) {
584 case CKS_Ref:
585 case CKS_Data:
586 break; // OK
587 default:
588 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK);
589 }
590 cspValidateKeyUsageBits(keyType, KeyUsage);
591
592 /* prepare outgoing header */
593 CssmKey::Header &unwrappedHdr = UnwrappedKey.header();
594 const CssmKey::Header &wrappedHdr = WrappedKey.header();
595 setKeyHeader(unwrappedHdr,
596 plugin.myGuid(),
597 wrappedHdr.algorithm(), // same as incoming
598 wrappedHdr.keyClass(), // same as incoming
599 KeyAttr & ~KEY_ATTR_RETURN_MASK,
600 KeyUsage);
601 unwrappedHdr.LogicalKeySizeInBits = wrappedHdr.LogicalKeySizeInBits;
602 unwrappedHdr.StartDate = wrappedHdr.StartDate;
603 unwrappedHdr.EndDate = wrappedHdr.EndDate;
604 UnwrappedKey.KeyData.Data = NULL; // ignore possible incoming KeyData
605 UnwrappedKey.KeyData.Length = 0;
606
607 /* validate wrappedKey format */
608 if(!isNullUnwrap) {
609 switch(wrapFormat) {
610 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
611 if(WrappedKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) {
612 /* this unwrapping style only for symmetric keys */
613 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
614 }
615 break;
616 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
617 case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
618 if(WrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
619 /* these unwrapping styles only for private keys */
620 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
621 }
622 break;
623 case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM:
624 UnwrapKeyCms(CCHandle,
625 Context,
626 WrappedKey,
627 CredAndAclEntry,
628 UnwrappedKey,
629 DescriptiveData,
630 Privilege,
631 keyStorage);
632 return;
633 case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1:
634 /* RSA private key, unwrap to ref key only */
635 if(WrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) {
636 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
637 }
638 if(WrappedKey.algorithm() != CSSM_ALGID_RSA) {
639 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
640 }
641 if(keyStorage != CKS_Ref) {
642 errorLog0("UNwrapKey: OPENSSH1 only wraps to reference key\n");
643 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT);
644 }
645 UnwrapKeyOpenSSH1(CCHandle,
646 Context,
647 WrappedKey,
648 CredAndAclEntry,
649 UnwrappedKey,
650 DescriptiveData,
651 Privilege,
652 keyStorage);
653 return;
654 default:
655 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT);
656 }
657 }
658
659 /* Get key blob, decoding and decrypting if necessary */
660 CssmData decodedBlob;
661 CssmData remData;
662 try {
663 if(isNullUnwrap) {
664 /* simple copy of raw blob */
665 copyData(WrappedKey.KeyData,
666 UnwrappedKey.KeyData,
667 normAllocator);
668 unwrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
669 unwrappedHdr.Format = wrapFormat;
670 }
671 else {
672 decodedBlob = CssmData::overlay(WrappedKey.KeyData);
673 CSSM_SIZE bytesDecrypted;
674 CssmData *unwrapData =
675 CssmData::overlay(&UnwrappedKey.KeyData);
676
677 DecryptData(CCHandle,
678 Context,
679 &decodedBlob, // CipherBufs[],
680 1, // CipherBufCount,
681 unwrapData, // ClearBufs[]
682 1, // ClearBufCount
683 bytesDecrypted,
684 remData,
685 Privilege);
686
687 // I'm not 100% sure about this....
688 assert(remData.Length == 0);
689 UnwrappedKey.KeyData.Length = bytesDecrypted;
690 unwrappedHdr.BlobType = CSSM_KEYBLOB_RAW;
691
692 /*
693 * Figure out various header fields from resulting blob
694 */
695 switch(wrapFormat) {
696 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7:
697 unwrappedHdr.Format =
698 CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
699 if(unwrappedHdr.LogicalKeySizeInBits == 0) {
700 unwrappedHdr.LogicalKeySizeInBits =
701 (unsigned)(bytesDecrypted * 8);
702 }
703 /* app has to infer/know algorithm */
704 break;
705 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8:
706 pkcs8InferKeyHeader(UnwrappedKey);
707 break;
708 case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL:
709 /*
710 * App told us key algorithm (in WrappedKey).
711 * Infer format and key size.
712 */
713 opensslInferKeyHeader(UnwrappedKey);
714 break;
715 }
716 }
717 }
718 catch (...) {
719 errorLog0("UnwrapKey: DecryptData() threw exception\n");
720 freeCssmData(remData, normAllocator);
721 throw;
722 }
723 freeCssmData(remData, normAllocator);
724
725 /*
726 * One more thing: cook up a BinaryKey if caller wants a
727 * reference key.
728 */
729 if(keyStorage == CKS_Ref) {
730 /*
731 * We have a key in raw format; convert to BinaryKey.
732 */
733 BinaryKey *binKey = NULL;
734 CSPKeyInfoProvider *provider = infoProvider(UnwrappedKey);
735 /* optional parameter-bearing key */
736 CssmKey *paramKey = Context.get<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
737 provider->CssmKeyToBinary(paramKey, UnwrappedKey.KeyHeader.KeyAttr, &binKey);
738 addRefKey(*binKey, UnwrappedKey);
739 delete provider;
740 }
741 }
742