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"
34 #include "cspdebugging.h"
37 * Wrap key function. Used for two things:
39 * -- Encrypt and encode a private or session key for export to
40 * a foreign system or program. Any type of keys may be used
41 * for the unwrapped key and the wrapping (encrypting) key,
42 * as long as this CSP understands those keys. The context
43 * must be of class ALGCLASS_SYMMETRIC or ALGCLASS_ASYMMETRIC,
44 * matching the wrapping key.
46 * Private keys will be PKCS8 encoded; session keys will be
47 * PKCS7 encoded. 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:
73 * UnwrappedKey type WrappingKey type Format
74 * ----------------- ---------------- -------------------------
75 * 3DES 3DES CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
76 * any Other symmetric CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
77 * any Other asymmetric CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
80 void AppleCSPSession::WrapKey(
81 CSSM_CC_HANDLE CCHandle
,
82 const Context
&Context
,
83 const AccessCredentials
&AccessCred
,
84 const CssmKey
&UnwrappedKey
,
85 const CssmData
*DescriptiveData
,
87 CSSM_PRIVILEGE Privilege
)
89 CssmKey::Header
&wrappedHdr
= WrappedKey
.header();
90 bool isNullWrap
= false;
91 CssmKey
*wrappingKey
= NULL
;
92 CSSM_KEYBLOB_FORMAT wrapFormat
;
94 switch(UnwrappedKey
.keyClass()) {
95 case CSSM_KEYCLASS_PUBLIC_KEY
:
96 case CSSM_KEYCLASS_PRIVATE_KEY
:
97 case CSSM_KEYCLASS_SESSION_KEY
:
100 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
103 /* wrapping key only required for non-NULL wrap */
104 wrappingKey
= Context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
);
105 if(wrappingKey
== NULL
) {
106 if((Context
.algorithm() == CSSM_ALGID_NONE
) &&
107 (Context
.type() == CSSM_ALGCLASS_SYMMETRIC
)) {
112 errorLog0("WrapKey: missing wrapping key\n");
113 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY
);
118 * Validate misc. params as best we can
121 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_NONE
;
125 * Can only wrap session and private keys.
127 #if !ALLOW_PUB_KEY_WRAP
128 if(UnwrappedKey
.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY
) {
129 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
131 #endif /* ALLOW_PUB_KEY_WRAP */
132 cspValidateIntendedKeyUsage(&wrappingKey
->KeyHeader
, CSSM_KEYUSE_WRAP
);
135 * make sure wrapping key type matches context
137 CSSM_CONTEXT_TYPE wrapType
;
138 switch(wrappingKey
->KeyHeader
.KeyClass
) {
139 case CSSM_KEYCLASS_PUBLIC_KEY
:
140 case CSSM_KEYCLASS_PRIVATE_KEY
:
141 wrapType
= CSSM_ALGCLASS_ASYMMETRIC
;
143 case CSSM_KEYCLASS_SESSION_KEY
:
144 wrapType
= CSSM_ALGCLASS_SYMMETRIC
;
147 errorLog0("WrapKey: bad class of wrappingKey\n");
148 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY
);
150 if(wrapType
!= Context
.type()) {
151 errorLog0("WrapKey: mismatch wrappingKey/contextType\n");
152 CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT
);
154 if(Context
.algorithm() == CSSM_ALGID_NONE
) {
155 errorLog0("WrapKey: null wrap alg, non-null key\n");
156 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
160 * Get optional wrap format, set default per incoming keys
161 * Note: no such atrribute ==> 0 ==> FORMAT_NONE, which we
162 * take to mean "use the default".
164 wrapFormat
= Context
.getInt(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT
);
165 if(wrapFormat
== CSSM_KEYBLOB_WRAPPED_FORMAT_NONE
) {
166 /* figure out a default */
167 if(wrapType
== CSSM_ALGCLASS_ASYMMETRIC
) {
170 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
;
172 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
;
176 CASSERT(wrapType
== CSSM_ALGCLASS_SYMMETRIC
);
177 if((wrappingKey
->algorithm() == CSSM_ALGID_3DES_3KEY
) &&
178 (UnwrappedKey
.algorithm() == CSSM_ALGID_3DES_3KEY
)) {
179 /* apple custom CMS */
180 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
;
183 /* normal case for symmetric wrapping keys */
185 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
;
187 wrapFormat
= CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
;
190 } /* default for symmetric wrapping key */
191 } /* no format present or FORMAT_NONE */
194 /* make sure we have a valid format here */
197 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
:
198 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
:
200 case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
:
202 case CSSM_KEYBLOB_WRAPPED_FORMAT_NONE
:
204 /* only time this is OK */
209 dprintf1("KeyWrap: invalid wrapFormat (%d)\n", (int)wrapFormat
);
210 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT
);
212 /* get the blob to be wrappped */
214 bool allocdRawBlob
= false;
215 CSSM_KEYBLOB_FORMAT rawFormat
;
217 switch(UnwrappedKey
.blobType()) {
218 case CSSM_KEYBLOB_RAW
:
220 rawBlob
= CssmData::overlay(UnwrappedKey
.KeyData
);
221 rawFormat
= UnwrappedKey
.blobFormat();
223 case CSSM_KEYBLOB_REFERENCE
:
224 /* get binary key, then get blob from it */
226 BinaryKey
&binKey
= lookupRefKey(UnwrappedKey
);
228 * Special case for null wrap - prevent caller from obtaining
229 * clear bits if CSSM_KEYATTR_SENSITIVE or !CSSM_KEYATTR_EXTRACTABLE.
230 * Don't trust the caller's header; use the one in the BinaryKey.
233 CSSM_KEYATTR_FLAGS keyAttr
= binKey
.mKeyHeader
.KeyAttr
;
234 if((keyAttr
& CSSM_KEYATTR_SENSITIVE
) ||
235 !(keyAttr
& CSSM_KEYATTR_EXTRACTABLE
)) {
237 CSSMERR_CSP_INVALID_KEYATTR_MASK
);
240 rawFormat
= requestedKeyFormat(Context
, UnwrappedKey
);
241 binKey
.generateKeyBlob(privAllocator
,
245 allocdRawBlob
= true; // remember - we need to free
249 errorLog0("WrapKey: bad unwrappedKey BlobType\n");
250 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY
);
254 * Prepare outgoing header.
256 const CssmKey::Header
&unwrappedHdr
= UnwrappedKey
.header();
257 setKeyHeader(wrappedHdr
,
259 unwrappedHdr
.algorithm(), // same as incoming
260 unwrappedHdr
.keyClass(), // same as incoming
261 unwrappedHdr
.KeyAttr
,
262 unwrappedHdr
.KeyUsage
);
263 wrappedHdr
.LogicalKeySizeInBits
= unwrappedHdr
.LogicalKeySizeInBits
;
264 wrappedHdr
.WrapAlgorithmId
= Context
.algorithm(); // true for null
266 wrappedHdr
.Format
= wrapFormat
;
268 wrappedHdr
.BlobType
= CSSM_KEYBLOB_RAW
;
271 wrappedHdr
.BlobType
= CSSM_KEYBLOB_WRAPPED
;
275 * special case - break out here for custom Apple CMS
277 if(!isNullWrap
&& (wrapFormat
== CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
)) {
291 freeCssmData(rawBlob
, privAllocator
);
296 freeCssmData(rawBlob
, privAllocator
);
303 * Generate wrapped blob. Careful, we need to conditionally free
306 CssmData encryptedBlob
;
308 WrappedKey
.KeyData
.Data
= NULL
; // ignore possible incoming KeyData
309 WrappedKey
.KeyData
.Length
= 0;
313 /* copy raw blob to caller's wrappedKey */
314 copyCssmData(rawBlob
,
315 CssmData::overlay(WrappedKey
.KeyData
),
317 wrappedHdr
.Format
= rawFormat
;
321 /* encrypt rawBlob using caller's context, then encode to
322 * WrappedKey.KeyData */
323 uint32 bytesEncrypted
;
324 EncryptData(CCHandle
,
326 &rawBlob
, // ClearBufs[]
328 &encryptedBlob
, // CipherBufs[],
329 1, // CipherBufCount,
334 // I'm not 100% sure about this....
335 assert(remData
.Length
== 0);
336 encryptedBlob
.Length
= bytesEncrypted
;
337 if(wrapFormat
== CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
) {
338 cspEncodePkcs7(Context
.algorithm(),
339 Context
.getInt(CSSM_ATTRIBUTE_MODE
),
341 CssmData::overlay(WrappedKey
.KeyData
),
345 CASSERT(wrapFormat
== CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
);
346 cspEncodePkcs8(Context
.algorithm(),
347 Context
.getInt(CSSM_ATTRIBUTE_MODE
),
349 CssmData::overlay(WrappedKey
.KeyData
),
352 wrappedHdr
.BlobType
= CSSM_KEYBLOB_WRAPPED
;
353 // OK to be zero or not present
354 wrappedHdr
.WrapMode
= Context
.getInt(
355 CSSM_ATTRIBUTE_MODE
);
360 errorLog0("WrapKey: EncryptData() threw exception\n");
362 freeCssmData(rawBlob
, privAllocator
);
364 /* mallocd in EncryptData, thus normAllocator */
365 freeCssmData(encryptedBlob
, normAllocator
);
366 freeCssmData(remData
,normAllocator
);
370 freeCssmData(rawBlob
, privAllocator
);
372 freeCssmData(encryptedBlob
, normAllocator
);
373 freeCssmData(remData
, normAllocator
);
377 * Unwrap key function. Used for:
379 * -- Given key of BlobType CSSM_KEYBLOB_WRAPPED, decode and decrypt
380 * it, yielding a key in either raw or reference format. Unwrapping
381 * key may be either raw or reference. The context must match
382 * the unwrapping key (ALGCLASS_SYMMETRIC or ALGCLASS_ASYMMETRIC).
384 * Private keys are assumed to be PKCS8 encoded; session keys
385 * are assumed to be PKCS7 encoded.
387 * -- Convert a Raw key to a reference key (with no decrypting).
388 * This is called a NULL unwrap; no unwrapping key need be present in
389 * the context, but the context must be of class
390 * ALGCLASS_SYMMETRIC and algorithm ALGID_NONE.
392 void AppleCSPSession::UnwrapKey(
393 CSSM_CC_HANDLE CCHandle
,
394 const Context
&Context
,
395 const CssmKey
*PublicKey
,
396 const CssmKey
&WrappedKey
,
399 const CssmData
*KeyLabel
,
400 const CSSM_RESOURCE_CONTROL_CONTEXT
*CredAndAclEntry
,
401 CssmKey
&UnwrappedKey
,
402 CssmData
&DescriptiveData
,
403 CSSM_PRIVILEGE Privilege
)
405 bool isNullUnwrap
= false;
406 CssmKey
*unwrappingKey
= NULL
;
407 cspKeyType keyType
; // CKT_Public, etc.
408 CSSM_KEYBLOB_FORMAT wrapFormat
= WrappedKey
.blobFormat();
410 /* obtain unwrapping key if present */
411 unwrappingKey
= Context
.get
<CssmKey
>(CSSM_ATTRIBUTE_KEY
);
412 if(unwrappingKey
== NULL
) {
413 if((Context
.algorithm() == CSSM_ALGID_NONE
) &&
414 (Context
.type() == CSSM_ALGCLASS_SYMMETRIC
)) {
419 errorLog0("UnwrapKey: missing wrapping key\n");
420 CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY
);
425 * validate unwrappingKey
428 /* make sure unwrapping key type matches context */
429 CSSM_CONTEXT_TYPE unwrapType
;
430 switch(unwrappingKey
->KeyHeader
.KeyClass
) {
431 case CSSM_KEYCLASS_PUBLIC_KEY
:
432 case CSSM_KEYCLASS_PRIVATE_KEY
:
433 unwrapType
= CSSM_ALGCLASS_ASYMMETRIC
;
435 case CSSM_KEYCLASS_SESSION_KEY
:
436 unwrapType
= CSSM_ALGCLASS_SYMMETRIC
;
439 errorLog0("UnwrapKey: bad class of wrappingKey\n");
440 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY
);
442 if(unwrapType
!= Context
.type()) {
443 errorLog0("UnwrapKey: mismatch unwrappingKey/contextType\n");
444 CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT
);
446 if(Context
.algorithm() == CSSM_ALGID_NONE
) {
447 errorLog0("UnwrapKey: null wrap alg, non-null key\n");
448 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM
);
450 cspValidateIntendedKeyUsage(&unwrappingKey
->KeyHeader
, CSSM_KEYUSE_UNWRAP
);
453 /* validate WrappedKey */
454 switch(WrappedKey
.keyClass()) {
455 case CSSM_KEYCLASS_PUBLIC_KEY
:
456 #if !ALLOW_PUB_KEY_WRAP
458 errorLog0("UnwrapKey: unwrap of public key illegal\n");
459 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
461 #endif /* ALLOW_PUB_KEY_WRAP */
462 keyType
= CKT_Public
;
464 case CSSM_KEYCLASS_PRIVATE_KEY
:
465 keyType
= CKT_Private
;
467 case CSSM_KEYCLASS_SESSION_KEY
:
468 keyType
= CKT_Session
;
471 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS
);
474 if(WrappedKey
.blobType() != CSSM_KEYBLOB_RAW
) {
475 errorLog0("UnwrapKey: expected raw blobType\n");
476 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT
);
480 if(WrappedKey
.blobType() != CSSM_KEYBLOB_WRAPPED
) {
481 errorLog0("UnwrapKey: expected wrapped blobType\n");
482 CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT
);
486 /* validate requested storage and usage */
487 cspKeyStorage keyStorage
= cspParseKeyAttr(keyType
, KeyAttr
);
493 CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK
);
495 cspValidateKeyUsageBits(keyType
, KeyUsage
);
497 /* prepare outgoing header */
498 CssmKey::Header
&unwrappedHdr
= UnwrappedKey
.header();
499 const CssmKey::Header
&wrappedHdr
= WrappedKey
.header();
500 setKeyHeader(unwrappedHdr
,
502 wrappedHdr
.algorithm(), // same as incoming
503 wrappedHdr
.keyClass(), // same as incoming
504 KeyAttr
& ~KEY_ATTR_RETURN_MASK
,
506 unwrappedHdr
.LogicalKeySizeInBits
= wrappedHdr
.LogicalKeySizeInBits
;
507 unwrappedHdr
.KeyUsage
= wrappedHdr
.KeyUsage
;
508 UnwrappedKey
.KeyData
.Data
= NULL
; // ignore possible incoming KeyData
509 UnwrappedKey
.KeyData
.Length
= 0;
511 /* validate wrappedKey format */
515 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
:
516 case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8
:
519 case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM
:
520 UnwrapKeyCms(CCHandle
,
530 CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT
);
534 /* Get key blob, decoding and decrypting if necessary */
535 CssmData decodedBlob
;
539 /* simple copy of raw blob */
540 copyData(WrappedKey
.KeyData
,
541 UnwrappedKey
.KeyData
,
543 unwrappedHdr
.BlobType
= CSSM_KEYBLOB_RAW
;
544 unwrappedHdr
.Format
= wrapFormat
;
548 /* decode wrapped blob, then decrypt to UnwrappedKey.KeyData
549 * using caller's context */
550 CSSM_KEYBLOB_FORMAT rawFormat
;
551 if(wrapFormat
== CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7
) {
552 cspDecodePkcs7(WrappedKey
,
558 cspDecodePkcs8(WrappedKey
,
563 uint32 bytesDecrypted
;
564 CssmData
*unwrapData
=
565 CssmData::overlay(&UnwrappedKey
.KeyData
);
567 DecryptData(CCHandle
,
569 &decodedBlob
, // CipherBufs[],
570 1, // CipherBufCount,
571 unwrapData
, // ClearBufs[]
577 // I'm not 100% sure about this....
578 assert(remData
.Length
== 0);
579 UnwrappedKey
.KeyData
.Length
= bytesDecrypted
;
580 unwrappedHdr
.BlobType
= CSSM_KEYBLOB_RAW
;
581 unwrappedHdr
.Format
= rawFormat
;
586 errorLog0("UnwrapKey: DecryptData() threw exception\n");
587 freeCssmData(decodedBlob
, normAllocator
);
588 freeCssmData(remData
, normAllocator
);
591 freeCssmData(decodedBlob
, normAllocator
);
592 freeCssmData(remData
, normAllocator
);
595 * One more thing: cook up a BinaryKey if caller wants a
598 if(keyStorage
== CKS_Ref
) {
600 * We have a key in raw format; convert to BinaryKey.
602 BinaryKey
*binKey
= NULL
;
603 CSPKeyInfoProvider
*provider
= infoProvider(UnwrappedKey
);
604 provider
->CssmKeyToBinary(&binKey
);
605 addRefKey(*binKey
, UnwrappedKey
);