2 * Copyright (c) 2000-2001 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 // wrapKey.cpp - wrap/unwrap key functions for AppleCSPSession
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.
27 #define ALLOW_PUB_KEY_WRAP 1
29 #include "AppleCSPSession.h"
30 #include "AppleCSPUtils.h"
31 #include "AppleCSPKeys.h"
33 #include "cspdebugging.h"
36 * Wrap key function. Used for two things:
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.
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.
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.
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:
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.
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.
71 * Default wrapping if none specified based on ther unwrapped key as
74 * UnwrappedKey Wrap format
75 * ------------ -----------
78 * FEE private APPLE_CUSTOM
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
,
89 CSSM_PRIVILEGE Privilege
)
91 CssmKey::Header
&wrappedHdr
= WrappedKey
.header();
92 bool isNullWrap
= false;
93 CssmKey
*wrappingKey
= NULL
;
94 CSSM_KEYBLOB_FORMAT wrapFormat
;
96 switch(UnwrappedKey
.keyClass()) {
97 case CSSM_KEYCLASS_PUBLIC_KEY
:
98 case CSSM_KEYCLASS_PRIVATE_KEY
:
99 case CSSM_KEYCLASS_SESSION_KEY
:
102 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
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
)) {
114 errorLog0("WrapKey: missing wrapping key\n");
115 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY
);
120 * Validate misc. params as best we can
123 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_NONE
;
127 * Can only wrap session and private keys.
129 #if !ALLOW_PUB_KEY_WRAP
130 if(UnwrappedKey
.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY
) {
131 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
133 #endif /* ALLOW_PUB_KEY_WRAP */
134 cspValidateIntendedKeyUsage(&wrappingKey
->KeyHeader
, CSSM_KEYUSE_WRAP
);
135 cspVerifyKeyTimes(wrappingKey
->KeyHeader
);
138 * make sure wrapping key type matches context
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
;
146 case CSSM_KEYCLASS_SESSION_KEY
:
147 wrapType
= CSSM_ALGCLASS_SYMMETRIC
;
150 errorLog0("WrapKey: bad class of wrappingKey\n");
151 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY
);
153 if(wrapType
!= Context
.type()) {
154 errorLog0("WrapKey: mismatch wrappingKey/contextType\n");
155 CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT
);
157 if(Context
.algorithm() == CSSM_ALGID_NONE
) {
158 errorLog0("WrapKey: null wrap alg, non-null key\n");
159 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
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".
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
;
174 case CSSM_KEYCLASS_PUBLIC_KEY
:
175 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
;
177 case CSSM_KEYCLASS_PRIVATE_KEY
:
178 switch(UnwrappedKey
.algorithm()) {
180 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
;
183 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
;
188 /* NOT REACHED - checked above */
191 } /* no format present or FORMAT_NONE */
194 /* make sure we have a valid format here */
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
);
202 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
:
203 if(UnwrappedKey
.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY
) {
204 /* this wrapping style only for private keys */
205 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
208 case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
:
209 /* no restrictions (well AES can't be the wrap alg but that will
212 case CSSM_KEYBLOB_WRAPPED_FORMAT_NONE
:
214 /* only time this is OK */
219 dprintf1("KeyWrap: invalid wrapFormat (%d)\n", (int)wrapFormat
);
220 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT
);
222 /* get the blob to be wrappped */
224 bool allocdRawBlob
= false;
225 CSSM_KEYBLOB_FORMAT rawFormat
;
228 * Outgoing same as incoming unless a partial key is completed during
231 const CssmKey::Header
&unwrappedHdr
= UnwrappedKey
.header();
232 CSSM_KEYATTR_FLAGS unwrappedKeyAttrFlags
= unwrappedHdr
.KeyAttr
;
234 switch(UnwrappedKey
.blobType()) {
235 case CSSM_KEYBLOB_RAW
:
237 rawBlob
= CssmData::overlay(UnwrappedKey
.KeyData
);
238 rawFormat
= UnwrappedKey
.blobFormat();
240 case CSSM_KEYBLOB_REFERENCE
:
241 /* get binary key, then get blob from it */
243 BinaryKey
&binKey
= lookupRefKey(UnwrappedKey
);
246 * Subsequent tests for extractability: don't trust the
247 * caller's header; use the one in the BinaryKey.
249 CSSM_KEYATTR_FLAGS keyAttr
= binKey
.mKeyHeader
.KeyAttr
;
250 if(!(keyAttr
& CSSM_KEYATTR_EXTRACTABLE
)) {
251 /* this key not extractable in any form */
252 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK
);
256 * Null wrap - prevent caller from obtaining
257 * clear bits if CSSM_KEYATTR_SENSITIVE
259 if(isNullWrap
&& (keyAttr
& CSSM_KEYATTR_SENSITIVE
)) {
260 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK
);
264 * Special case for PKCS8: need to get blob of a specific
265 * algorithm-dependent format.
267 if(wrapFormat
== CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
) {
268 rawFormat
= pkcs8RawKeyFormat(binKey
.mKeyHeader
.Format
);
271 rawFormat
= requestedKeyFormat(Context
, UnwrappedKey
);
273 /* optional parameter-bearing key */
274 CssmKey
*paramKey
= Context
.get
<CssmKey
>(CSSM_ATTRIBUTE_PARAM_KEY
);
275 binKey
.generateKeyBlob(privAllocator
,
280 unwrappedKeyAttrFlags
);
282 allocdRawBlob
= true; // remember - we need to free
286 errorLog0("WrapKey: bad unwrappedKey BlobType\n");
287 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
291 * Prepare outgoing header.
293 setKeyHeader(wrappedHdr
,
295 unwrappedHdr
.algorithm(), // same as incoming
296 unwrappedHdr
.keyClass(), // same as incoming
297 unwrappedKeyAttrFlags
,
298 unwrappedHdr
.KeyUsage
);
299 wrappedHdr
.LogicalKeySizeInBits
= unwrappedHdr
.LogicalKeySizeInBits
;
300 wrappedHdr
.WrapAlgorithmId
= Context
.algorithm(); // true for null
302 wrappedHdr
.StartDate
= unwrappedHdr
.StartDate
;
303 wrappedHdr
.EndDate
= unwrappedHdr
.EndDate
;
304 wrappedHdr
.Format
= wrapFormat
;
306 wrappedHdr
.BlobType
= CSSM_KEYBLOB_RAW
;
309 wrappedHdr
.BlobType
= CSSM_KEYBLOB_WRAPPED
;
313 * special case - break out here for custom Apple CMS
315 if(!isNullWrap
&& (wrapFormat
== CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
)) {
329 freeCssmData(rawBlob
, privAllocator
);
334 freeCssmData(rawBlob
, privAllocator
);
341 * Generate wrapped blob. Careful, we need to conditionally free
344 CssmData encryptedBlob
;
346 WrappedKey
.KeyData
.Data
= NULL
; // ignore possible incoming KeyData
347 WrappedKey
.KeyData
.Length
= 0;
351 /* copy raw blob to caller's wrappedKey */
352 copyCssmData(rawBlob
,
353 CssmData::overlay(WrappedKey
.KeyData
),
355 wrappedHdr
.Format
= rawFormat
;
358 /* encrypt rawBlob using caller's context, then encode to
359 * WrappedKey.KeyData */
360 uint32 bytesEncrypted
;
361 EncryptData(CCHandle
,
363 &rawBlob
, // ClearBufs[]
365 &encryptedBlob
, // CipherBufs[],
366 1, // CipherBufCount,
371 // I'm not 100% sure about this....
372 assert(remData
.Length
== 0);
373 encryptedBlob
.Length
= bytesEncrypted
;
374 WrappedKey
.KeyData
= encryptedBlob
;
375 wrappedHdr
.BlobType
= CSSM_KEYBLOB_WRAPPED
;
376 // OK to be zero or not present
377 wrappedHdr
.WrapMode
= Context
.getInt(
378 CSSM_ATTRIBUTE_MODE
);
382 errorLog0("WrapKey: EncryptData() threw exception\n");
384 freeCssmData(rawBlob
, privAllocator
);
386 freeCssmData(remData
,normAllocator
);
390 freeCssmData(rawBlob
, privAllocator
);
392 freeCssmData(remData
, normAllocator
);
396 * Unwrap key function. Used for:
398 * -- Given key of BlobType CSSM_KEYBLOB_WRAPPED, decode and decrypt
399 * it, yielding a key in either raw or reference format. Unwrapping
400 * key may be either raw or reference. The context must match
401 * the unwrapping key (ALGCLASS_SYMMETRIC or ALGCLASS_ASYMMETRIC).
403 * Private keys are assumed to be PKCS8 encoded; session keys
404 * are assumed to be PKCS7 encoded.
406 * -- Convert a Raw key to a reference key (with no decrypting).
407 * This is called a NULL unwrap; no unwrapping key need be present in
408 * the context, but the context must be of class
409 * ALGCLASS_SYMMETRIC and algorithm ALGID_NONE.
411 void AppleCSPSession::UnwrapKey(
412 CSSM_CC_HANDLE CCHandle
,
413 const Context
&Context
,
414 const CssmKey
*PublicKey
,
415 const CssmKey
&WrappedKey
,
418 const CssmData
*KeyLabel
,
419 const CSSM_RESOURCE_CONTROL_CONTEXT
*CredAndAclEntry
,
420 CssmKey
&UnwrappedKey
,
421 CssmData
&DescriptiveData
,
422 CSSM_PRIVILEGE Privilege
)
424 bool isNullUnwrap
= false;
425 CssmKey
*unwrappingKey
= NULL
;
426 cspKeyType keyType
; // CKT_Public, etc.
427 CSSM_KEYBLOB_FORMAT wrapFormat
= WrappedKey
.blobFormat();
429 /* obtain unwrapping key if present */
430 unwrappingKey
= Context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
);
431 if(unwrappingKey
== NULL
) {
432 if((Context
.algorithm() == CSSM_ALGID_NONE
) &&
433 (Context
.type() == CSSM_ALGCLASS_SYMMETRIC
)) {
438 errorLog0("UnwrapKey: missing wrapping key\n");
439 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY
);
444 * validate unwrappingKey
447 /* make sure unwrapping key type matches context */
448 CSSM_CONTEXT_TYPE unwrapType
;
449 switch(unwrappingKey
->KeyHeader
.KeyClass
) {
450 case CSSM_KEYCLASS_PUBLIC_KEY
:
451 case CSSM_KEYCLASS_PRIVATE_KEY
:
452 unwrapType
= CSSM_ALGCLASS_ASYMMETRIC
;
454 case CSSM_KEYCLASS_SESSION_KEY
:
455 unwrapType
= CSSM_ALGCLASS_SYMMETRIC
;
458 errorLog0("UnwrapKey: bad class of wrappingKey\n");
459 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY
);
461 if(unwrapType
!= Context
.type()) {
462 errorLog0("UnwrapKey: mismatch unwrappingKey/contextType\n");
463 CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT
);
465 if(Context
.algorithm() == CSSM_ALGID_NONE
) {
466 errorLog0("UnwrapKey: null wrap alg, non-null key\n");
467 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
469 cspValidateIntendedKeyUsage(&unwrappingKey
->KeyHeader
, CSSM_KEYUSE_UNWRAP
);
470 cspVerifyKeyTimes(unwrappingKey
->KeyHeader
);
473 /* validate WrappedKey */
474 switch(WrappedKey
.keyClass()) {
475 case CSSM_KEYCLASS_PUBLIC_KEY
:
476 #if !ALLOW_PUB_KEY_WRAP
478 errorLog0("UnwrapKey: unwrap of public key illegal\n");
479 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
481 #endif /* ALLOW_PUB_KEY_WRAP */
482 keyType
= CKT_Public
;
484 case CSSM_KEYCLASS_PRIVATE_KEY
:
485 keyType
= CKT_Private
;
487 case CSSM_KEYCLASS_SESSION_KEY
:
488 keyType
= CKT_Session
;
491 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
494 if(WrappedKey
.blobType() != CSSM_KEYBLOB_RAW
) {
495 errorLog0("UnwrapKey: expected raw blobType\n");
496 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT
);
500 if(WrappedKey
.blobType() != CSSM_KEYBLOB_WRAPPED
) {
501 errorLog0("UnwrapKey: expected wrapped blobType\n");
502 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT
);
506 /* validate requested storage and usage */
507 cspKeyStorage keyStorage
= cspParseKeyAttr(keyType
, KeyAttr
);
513 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK
);
515 cspValidateKeyUsageBits(keyType
, KeyUsage
);
517 /* prepare outgoing header */
518 CssmKey::Header
&unwrappedHdr
= UnwrappedKey
.header();
519 const CssmKey::Header
&wrappedHdr
= WrappedKey
.header();
520 setKeyHeader(unwrappedHdr
,
522 wrappedHdr
.algorithm(), // same as incoming
523 wrappedHdr
.keyClass(), // same as incoming
524 KeyAttr
& ~KEY_ATTR_RETURN_MASK
,
526 unwrappedHdr
.LogicalKeySizeInBits
= wrappedHdr
.LogicalKeySizeInBits
;
527 unwrappedHdr
.StartDate
= wrappedHdr
.StartDate
;
528 unwrappedHdr
.EndDate
= wrappedHdr
.EndDate
;
529 UnwrappedKey
.KeyData
.Data
= NULL
; // ignore possible incoming KeyData
530 UnwrappedKey
.KeyData
.Length
= 0;
532 /* validate wrappedKey format */
535 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
:
536 if(WrappedKey
.keyClass() != CSSM_KEYCLASS_SESSION_KEY
) {
537 /* this unwrapping style only for symmetric keys */
538 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
541 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
:
542 if(WrappedKey
.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY
) {
543 /* this unwrapping style only for private keys */
544 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
547 case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
:
548 UnwrapKeyCms(CCHandle
,
558 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT
);
562 /* Get key blob, decoding and decrypting if necessary */
563 CssmData decodedBlob
;
567 /* simple copy of raw blob */
568 copyData(WrappedKey
.KeyData
,
569 UnwrappedKey
.KeyData
,
571 unwrappedHdr
.BlobType
= CSSM_KEYBLOB_RAW
;
572 unwrappedHdr
.Format
= wrapFormat
;
575 decodedBlob
= CssmData::overlay(WrappedKey
.KeyData
);
576 uint32 bytesDecrypted
;
577 CssmData
*unwrapData
=
578 CssmData::overlay(&UnwrappedKey
.KeyData
);
580 DecryptData(CCHandle
,
582 &decodedBlob
, // CipherBufs[],
583 1, // CipherBufCount,
584 unwrapData
, // ClearBufs[]
590 // I'm not 100% sure about this....
591 assert(remData
.Length
== 0);
592 UnwrappedKey
.KeyData
.Length
= bytesDecrypted
;
593 unwrappedHdr
.BlobType
= CSSM_KEYBLOB_RAW
;
596 * Figure out various header fields from resulting blob
599 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
:
600 unwrappedHdr
.Format
=
601 CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING
;
602 if(unwrappedHdr
.LogicalKeySizeInBits
== 0) {
603 unwrappedHdr
.LogicalKeySizeInBits
=
606 /* app has to infer/know algorithm */
608 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
:
609 pkcs8InferKeyHeader(UnwrappedKey
);
615 errorLog0("UnwrapKey: DecryptData() threw exception\n");
616 freeCssmData(remData
, normAllocator
);
619 freeCssmData(remData
, normAllocator
);
622 * One more thing: cook up a BinaryKey if caller wants a
625 if(keyStorage
== CKS_Ref
) {
627 * We have a key in raw format; convert to BinaryKey.
629 BinaryKey
*binKey
= NULL
;
630 CSPKeyInfoProvider
*provider
= infoProvider(UnwrappedKey
);
631 /* optional parameter-bearing key */
632 CssmKey
*paramKey
= Context
.get
<CssmKey
>(CSSM_ATTRIBUTE_PARAM_KEY
);
633 provider
->CssmKeyToBinary(paramKey
, UnwrappedKey
.KeyHeader
.KeyAttr
, &binKey
);
634 addRefKey(*binKey
, UnwrappedKey
);