]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 A |
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; | |
6b200bc3 | 238 | CSSM_KEYBLOB_FORMAT rawFormat = CSSM_KEYBLOB_RAW_FORMAT_NONE; |
b1ab9ed8 A |
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 = | |
427c49bc | 701 | (unsigned)(bytesDecrypted * 8); |
b1ab9ed8 A |
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 |