X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/OSX/libsecurity_apple_csp/lib/wrapKey.cpp diff --git a/OSX/libsecurity_apple_csp/lib/wrapKey.cpp b/OSX/libsecurity_apple_csp/lib/wrapKey.cpp new file mode 100644 index 00000000..00ab865e --- /dev/null +++ b/OSX/libsecurity_apple_csp/lib/wrapKey.cpp @@ -0,0 +1,742 @@ +/* + * Copyright (c) 2000-2001,2011-2012,2014 Apple Inc. All Rights Reserved. + * + * The contents of this file constitute Original Code as defined in and are + * subject to the Apple Public Source License Version 1.2 (the 'License'). + * You may not use this file except in compliance with the License. Please obtain + * a copy of the License at http://www.apple.com/publicsource and read it before + * using this file. + * + * This Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESS + * OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, INCLUDING WITHOUT + * LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR + * PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. Please see the License for the + * specific language governing rights and limitations under the License. + */ + + +// +// wrapKey.cpp - wrap/unwrap key functions for AppleCSPSession +// + +/* + * Currently the Security Server wraps public keys when they're stored, so we have + * to allow this. We might not want to do this in the real world. + */ +#define ALLOW_PUB_KEY_WRAP 1 + +#include "AppleCSPSession.h" +#include "AppleCSPUtils.h" +#include "AppleCSPKeys.h" +#include "pkcs8.h" +#include "cspdebugging.h" + +/* + * Wrap key function. Used for two things: + * + * -- Encrypt and encode a private or session key for export to + * a foreign system or program. Any type of keys may be used + * for the unwrapped key and the wrapping (encrypting) key, + * as long as this CSP understands those keys. The context + * must be of class ALGCLASS_SYMMETRIC or ALGCLASS_ASYMMETRIC, + * matching the wrapping key. + * + * In the absence of an explicit CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT + * attribute, private keys will be PKCS8 wrapped; session keys will be + * PKCS7 wrapped. Both input keys may be in raw or reference + * format. Wrapped key will have BlobType CSSM_KEYBLOB_WRAPPED. + * + * -- Convert a reference key to a RAW key (with no encrypting). + * This is called a NULL wrap; no wrapping key need be present in + * the context, but the context must be of class + * ALGCLASS_SYMMETRIC and algorithm ALGID_NONE. + * + * There are serious inconsistencies in the specification of wrap + * algorithms to be used in the various CDSA specs (c914.pdf, + * CSP Behavior spec) and between those specs and the PKCS standards + * PKCS7, PKCS8, RFC2630). Here is what this module implements: + * + * On a wrap key op, the caller can add a CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT + * attribute to the context to specify the wrapping algorithm to be used. + * If it's there, that's what we use if appropriate for the incoming key + * types. Otherwise we figure out a reasonable default from the incoming + * key types. The wrapped key always has the appropriate KeyHeader.Format + * field set indicating how it was wrapped. Defaults are shows below. + * + * The format CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM is used to indicate + * a modified CMS-style wrapping which is similar to that specified in + * RFC2630, with some modification. + * + * Default wrapping if none specified based on ther unwrapped key as + * follows: + * + * UnwrappedKey Wrap format + * ------------ ----------- + * Symmetric PKCS7 + * Public APPLE_CUSTOM + * FEE private APPLE_CUSTOM + * Other private PKCS8 + */ + +void AppleCSPSession::WrapKey( + CSSM_CC_HANDLE CCHandle, + const Context &Context, + const AccessCredentials &AccessCred, + const CssmKey &UnwrappedKey, + const CssmData *DescriptiveData, + CssmKey &WrappedKey, + CSSM_PRIVILEGE Privilege) +{ + CssmKey::Header &wrappedHdr = WrappedKey.header(); + bool isNullWrap = false; + CssmKey *wrappingKey = NULL; + CSSM_KEYBLOB_FORMAT wrapFormat; + + switch(UnwrappedKey.keyClass()) { + case CSSM_KEYCLASS_PUBLIC_KEY: + case CSSM_KEYCLASS_PRIVATE_KEY: + case CSSM_KEYCLASS_SESSION_KEY: + break; + default: + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + + /* wrapping key only required for non-NULL wrap */ + wrappingKey = Context.get(CSSM_ATTRIBUTE_KEY); + if(wrappingKey == NULL) { + if((Context.algorithm() == CSSM_ALGID_NONE) && + (Context.type() == CSSM_ALGCLASS_SYMMETRIC)) { + // NULL wrap, OK + isNullWrap = true; + } + else { + errorLog0("WrapKey: missing wrapping key\n"); + CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY); + } + } + + /* + * Validate misc. params as best we can + */ + if(isNullWrap) { + wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_NONE; + } + else { + /* + * Can only wrap session and private keys. + */ + #if !ALLOW_PUB_KEY_WRAP + if(UnwrappedKey.keyClass() == CSSM_KEYCLASS_PUBLIC_KEY) { + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + #endif /* ALLOW_PUB_KEY_WRAP */ + cspValidateIntendedKeyUsage(&wrappingKey->KeyHeader, CSSM_KEYUSE_WRAP); + cspVerifyKeyTimes(wrappingKey->KeyHeader); + + /* + * make sure wrapping key type matches context + */ + CSSM_CONTEXT_TYPE wrapType; + switch(wrappingKey->KeyHeader.KeyClass) { + case CSSM_KEYCLASS_PUBLIC_KEY: + case CSSM_KEYCLASS_PRIVATE_KEY: + wrapType = CSSM_ALGCLASS_ASYMMETRIC; + break; + case CSSM_KEYCLASS_SESSION_KEY: + wrapType = CSSM_ALGCLASS_SYMMETRIC; + break; + default: + errorLog0("WrapKey: bad class of wrappingKey\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY); + } + if(wrapType != Context.type()) { + errorLog0("WrapKey: mismatch wrappingKey/contextType\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT); + } + if(Context.algorithm() == CSSM_ALGID_NONE) { + errorLog0("WrapKey: null wrap alg, non-null key\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); + } + + /* + * Get optional wrap format, set default per incoming keys + * Note: no such atrribute ==> 0 ==> FORMAT_NONE, which we + * take to mean "use the default". + */ + wrapFormat = Context.getInt(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT); + if(wrapFormat == CSSM_KEYBLOB_WRAPPED_FORMAT_NONE) { + /* figure out a default based on unwrapped key */ + switch(UnwrappedKey.keyClass()) { + case CSSM_KEYCLASS_SESSION_KEY: + wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7; + break; + case CSSM_KEYCLASS_PUBLIC_KEY: + wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM; + break; + case CSSM_KEYCLASS_PRIVATE_KEY: + switch(UnwrappedKey.algorithm()) { + case CSSM_ALGID_FEE: + wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM; + break; + default: + wrapFormat = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8; + break; + } + break; + default: + /* NOT REACHED - checked above */ + break; + } + } /* no format present or FORMAT_NONE */ + } + + /* make sure we have a valid format here */ + switch(wrapFormat) { + case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7: + if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) { + /* this wrapping style only for symmetric keys */ + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8: + case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL: + if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) { + /* these wrapping styles only for private keys */ + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM: + /* no restrictions (well AES can't be the wrap alg but that will + * be caught later */ + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1: + /* RSA private key, reference format, only */ + if(UnwrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) { + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + if(UnwrappedKey.algorithm() != CSSM_ALGID_RSA) { + CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); + } + if(UnwrappedKey.blobType() != CSSM_KEYBLOB_REFERENCE) { + CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT); + } + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_NONE: + if(isNullWrap) { + /* only time this is OK */ + break; + } + /* else fall thru */ + default: + dprintf1("KeyWrap: invalid wrapFormat (%d)\n", (int)wrapFormat); + CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT); + } + /* get the blob to be wrappped */ + CssmData rawBlob; + bool allocdRawBlob = false; + CSSM_KEYBLOB_FORMAT rawFormat; + + /* + * Outgoing same as incoming unless a partial key is completed during + * generateKeyBlob() + */ + const CssmKey::Header &unwrappedHdr = UnwrappedKey.header(); + CSSM_KEYATTR_FLAGS unwrappedKeyAttrFlags = unwrappedHdr.KeyAttr; + + switch(UnwrappedKey.blobType()) { + case CSSM_KEYBLOB_RAW: + /* + * Trivial case - we already have the blob. + * This op - wrapping a raw key - is not supported for the + * CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1 format since that doesn't + * operate on a key blob. + */ + rawBlob = CssmData::overlay(UnwrappedKey.KeyData); + rawFormat = UnwrappedKey.blobFormat(); + break; + case CSSM_KEYBLOB_REFERENCE: + /* get binary key, then get blob from it */ + { + BinaryKey &binKey = lookupRefKey(UnwrappedKey); + + /* + * Subsequent tests for extractability: don't trust the + * caller's header; use the one in the BinaryKey. + */ + CSSM_KEYATTR_FLAGS keyAttr = binKey.mKeyHeader.KeyAttr; + if(!(keyAttr & CSSM_KEYATTR_EXTRACTABLE)) { + /* this key not extractable in any form */ + CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK); + } + + /* + * CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1: we're ready to roll; + * all we need is the reference key. + */ + if(wrapFormat == CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1) { + break; + } + + /* + * Null wrap - prevent caller from obtaining + * clear bits if CSSM_KEYATTR_SENSITIVE + */ + if(isNullWrap && (keyAttr & CSSM_KEYATTR_SENSITIVE)) { + CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK); + } + + /* + * Special case for PKCS8 and openssl: need to get blob of a specific + * algorithm-dependent format. Caller can override our + * preference with a + * CSSM_ATTRIBUTE_{PRIVATE,PUBLIC,SESSION}_KEY_FORMAT + * context attribute. + */ + rawFormat = requestedKeyFormat(Context, UnwrappedKey); + if(rawFormat == CSSM_KEYBLOB_RAW_FORMAT_NONE) { + CSSM_ALGORITHMS keyAlg = binKey.mKeyHeader.AlgorithmId; + switch(wrapFormat) { + case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8: + rawFormat = pkcs8RawKeyFormat(keyAlg); + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL: + rawFormat = opensslRawKeyFormat(keyAlg); + break; + default: + /* punt and take default for key type */ + break; + } + } + + /* + * DescriptiveData for encoding, currently only used for + * SSH1 keys. + */ + if((DescriptiveData != NULL) && (DescriptiveData->Length != 0)) { + binKey.descData(*DescriptiveData); + } + + /* optional parameter-bearing key */ + CssmKey *paramKey = Context.get(CSSM_ATTRIBUTE_PARAM_KEY); + binKey.generateKeyBlob(privAllocator, + rawBlob, + rawFormat, + *this, + paramKey, + unwrappedKeyAttrFlags); + } + allocdRawBlob = true; // remember - we need to free + break; + + default: + errorLog0("WrapKey: bad unwrappedKey BlobType\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY); + } + + /* + * Prepare outgoing header. + */ + setKeyHeader(wrappedHdr, + plugin.myGuid(), + unwrappedHdr.algorithm(), // same as incoming + unwrappedHdr.keyClass(), // same as incoming + unwrappedKeyAttrFlags, + unwrappedHdr.KeyUsage); + wrappedHdr.LogicalKeySizeInBits = unwrappedHdr.LogicalKeySizeInBits; + wrappedHdr.WrapAlgorithmId = Context.algorithm(); // true for null + // and non-Null + wrappedHdr.StartDate = unwrappedHdr.StartDate; + wrappedHdr.EndDate = unwrappedHdr.EndDate; + wrappedHdr.Format = wrapFormat; + if(isNullWrap) { + wrappedHdr.BlobType = CSSM_KEYBLOB_RAW; + } + else { + wrappedHdr.BlobType = CSSM_KEYBLOB_WRAPPED; + } + + /* + * special cases - break out here for Apple Custom and OpenSSHv1 + */ + if(!isNullWrap) { + switch(wrapFormat) { + case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM: + try { + WrapKeyCms(CCHandle, + Context, + AccessCred, + UnwrappedKey, + rawBlob, + allocdRawBlob, + DescriptiveData, + WrappedKey, + Privilege); + } + catch(...) { + if(allocdRawBlob) { + freeCssmData(rawBlob, privAllocator); + } + throw; + } + if(allocdRawBlob) { + freeCssmData(rawBlob, privAllocator); + } + return; + case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1: + { + /* + * 1. We don't have to worry about allocdRawBlob since this + * operation only works on reference keys and we did not + * obtain the raw blob from the BinaryKey. + * 2. This is a redundant lookupRefKey, I know, but since + * that returns a reference, it would just be too messy to have + * the previous call be in the same scope as this. + */ + BinaryKey &binKey = lookupRefKey(UnwrappedKey); + WrapKeyOpenSSH1(CCHandle, + Context, + AccessCred, + binKey, + rawBlob, + allocdRawBlob, + DescriptiveData, + WrappedKey, + Privilege); + return; + } + default: + /* proceed to encrypt blob */ + break; + } + } /* !isNullWrap */ + + + /* + * Generate wrapped blob. Careful, we need to conditionally free + * rawBlob on error. + */ + CssmData encryptedBlob; + CssmData remData; + WrappedKey.KeyData.Data = NULL; // ignore possible incoming KeyData + WrappedKey.KeyData.Length = 0; + + try { + if(isNullWrap) { + /* copy raw blob to caller's wrappedKey */ + copyCssmData(rawBlob, + CssmData::overlay(WrappedKey.KeyData), + normAllocator); + wrappedHdr.Format = rawFormat; + } + else { + /* encrypt rawBlob using caller's context, then encode to + * WrappedKey.KeyData */ + CSSM_SIZE bytesEncrypted; + EncryptData(CCHandle, + Context, + &rawBlob, // ClearBufs[] + 1, // ClearBufCount + &encryptedBlob, // CipherBufs[], + 1, // CipherBufCount, + bytesEncrypted, + remData, + Privilege); + + // I'm not 100% sure about this.... + assert(remData.Length == 0); + encryptedBlob.Length = bytesEncrypted; + WrappedKey.KeyData = encryptedBlob; + wrappedHdr.BlobType = CSSM_KEYBLOB_WRAPPED; + // OK to be zero or not present + wrappedHdr.WrapMode = Context.getInt( + CSSM_ATTRIBUTE_MODE); + } + } + catch (...) { + errorLog0("WrapKey: EncryptData() threw exception\n"); + if(allocdRawBlob) { + freeCssmData(rawBlob, privAllocator); + } + freeCssmData(remData,normAllocator); + throw; + } + if(allocdRawBlob) { + freeCssmData(rawBlob, privAllocator); + } + freeCssmData(remData, normAllocator); +} + +/* + * Unwrap key function. Used for: + * + * -- Given key of BlobType CSSM_KEYBLOB_WRAPPED, decode and decrypt + * it, yielding a key in either raw or reference format. Unwrapping + * key may be either raw or reference. The context must match + * the unwrapping key (ALGCLASS_SYMMETRIC or ALGCLASS_ASYMMETRIC). + * + * Private keys are assumed to be PKCS8 encoded; session keys + * are assumed to be PKCS7 encoded. + * + * -- Convert a Raw key to a reference key (with no decrypting). + * This is called a NULL unwrap; no unwrapping key need be present in + * the context, but the context must be of class + * ALGCLASS_SYMMETRIC and algorithm ALGID_NONE. + */ +void AppleCSPSession::UnwrapKey( + CSSM_CC_HANDLE CCHandle, + const Context &Context, + const CssmKey *PublicKey, + const CssmKey &WrappedKey, + uint32 KeyUsage, + uint32 KeyAttr, + const CssmData *KeyLabel, + const CSSM_RESOURCE_CONTROL_CONTEXT *CredAndAclEntry, + CssmKey &UnwrappedKey, + CssmData &DescriptiveData, + CSSM_PRIVILEGE Privilege) +{ + bool isNullUnwrap = false; + CssmKey *unwrappingKey = NULL; + cspKeyType keyType; // CKT_Public, etc. + CSSM_KEYBLOB_FORMAT wrapFormat = WrappedKey.blobFormat(); + + /* obtain unwrapping key if present */ + unwrappingKey = Context.get(CSSM_ATTRIBUTE_KEY); + if(unwrappingKey == NULL) { + if((Context.algorithm() == CSSM_ALGID_NONE) && + (Context.type() == CSSM_ALGCLASS_SYMMETRIC)) { + // NULL unwrap, OK + isNullUnwrap = true; + } + else { + errorLog0("UnwrapKey: missing wrapping key\n"); + CssmError::throwMe(CSSMERR_CSP_MISSING_ATTR_KEY); + } + } + + /* + * validate unwrappingKey + */ + if(!isNullUnwrap) { + /* make sure unwrapping key type matches context */ + CSSM_CONTEXT_TYPE unwrapType; + switch(unwrappingKey->KeyHeader.KeyClass) { + case CSSM_KEYCLASS_PUBLIC_KEY: + case CSSM_KEYCLASS_PRIVATE_KEY: + unwrapType = CSSM_ALGCLASS_ASYMMETRIC; + break; + case CSSM_KEYCLASS_SESSION_KEY: + unwrapType = CSSM_ALGCLASS_SYMMETRIC; + break; + default: + errorLog0("UnwrapKey: bad class of wrappingKey\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_KEY); + } + if(unwrapType != Context.type()) { + errorLog0("UnwrapKey: mismatch unwrappingKey/contextType\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_CONTEXT); + } + if(Context.algorithm() == CSSM_ALGID_NONE) { + errorLog0("UnwrapKey: null wrap alg, non-null key\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); + } + cspValidateIntendedKeyUsage(&unwrappingKey->KeyHeader, CSSM_KEYUSE_UNWRAP); + cspVerifyKeyTimes(unwrappingKey->KeyHeader); + } + + /* validate WrappedKey */ + switch(WrappedKey.keyClass()) { + case CSSM_KEYCLASS_PUBLIC_KEY: + #if !ALLOW_PUB_KEY_WRAP + if(!isNullUnwrap) { + errorLog0("UnwrapKey: unwrap of public key illegal\n"); + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + #endif /* ALLOW_PUB_KEY_WRAP */ + keyType = CKT_Public; + break; + case CSSM_KEYCLASS_PRIVATE_KEY: + keyType = CKT_Private; + break; + case CSSM_KEYCLASS_SESSION_KEY: + keyType = CKT_Session; + break; + default: + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + if(isNullUnwrap) { + if(WrappedKey.blobType() != CSSM_KEYBLOB_RAW) { + errorLog0("UnwrapKey: expected raw blobType\n"); + CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT); + } + } + else { + if(WrappedKey.blobType() != CSSM_KEYBLOB_WRAPPED) { + errorLog0("UnwrapKey: expected wrapped blobType\n"); + CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT); + } + } + + /* validate requested storage and usage */ + cspKeyStorage keyStorage = cspParseKeyAttr(keyType, KeyAttr); + switch(keyStorage) { + case CKS_Ref: + case CKS_Data: + break; // OK + default: + CssmError::throwMe(CSSMERR_CSP_INVALID_KEYATTR_MASK); + } + cspValidateKeyUsageBits(keyType, KeyUsage); + + /* prepare outgoing header */ + CssmKey::Header &unwrappedHdr = UnwrappedKey.header(); + const CssmKey::Header &wrappedHdr = WrappedKey.header(); + setKeyHeader(unwrappedHdr, + plugin.myGuid(), + wrappedHdr.algorithm(), // same as incoming + wrappedHdr.keyClass(), // same as incoming + KeyAttr & ~KEY_ATTR_RETURN_MASK, + KeyUsage); + unwrappedHdr.LogicalKeySizeInBits = wrappedHdr.LogicalKeySizeInBits; + unwrappedHdr.StartDate = wrappedHdr.StartDate; + unwrappedHdr.EndDate = wrappedHdr.EndDate; + UnwrappedKey.KeyData.Data = NULL; // ignore possible incoming KeyData + UnwrappedKey.KeyData.Length = 0; + + /* validate wrappedKey format */ + if(!isNullUnwrap) { + switch(wrapFormat) { + case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7: + if(WrappedKey.keyClass() != CSSM_KEYCLASS_SESSION_KEY) { + /* this unwrapping style only for symmetric keys */ + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8: + case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL: + if(WrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) { + /* these unwrapping styles only for private keys */ + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM: + UnwrapKeyCms(CCHandle, + Context, + WrappedKey, + CredAndAclEntry, + UnwrappedKey, + DescriptiveData, + Privilege, + keyStorage); + return; + case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSH1: + /* RSA private key, unwrap to ref key only */ + if(WrappedKey.keyClass() != CSSM_KEYCLASS_PRIVATE_KEY) { + CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS); + } + if(WrappedKey.algorithm() != CSSM_ALGID_RSA) { + CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM); + } + if(keyStorage != CKS_Ref) { + errorLog0("UNwrapKey: OPENSSH1 only wraps to reference key\n"); + CssmError::throwMe(CSSMERR_CSP_KEY_BLOB_TYPE_INCORRECT); + } + UnwrapKeyOpenSSH1(CCHandle, + Context, + WrappedKey, + CredAndAclEntry, + UnwrappedKey, + DescriptiveData, + Privilege, + keyStorage); + return; + default: + CssmError::throwMe(CSSMERR_CSP_INVALID_ATTR_WRAPPED_KEY_FORMAT); + } + } + + /* Get key blob, decoding and decrypting if necessary */ + CssmData decodedBlob; + CssmData remData; + try { + if(isNullUnwrap) { + /* simple copy of raw blob */ + copyData(WrappedKey.KeyData, + UnwrappedKey.KeyData, + normAllocator); + unwrappedHdr.BlobType = CSSM_KEYBLOB_RAW; + unwrappedHdr.Format = wrapFormat; + } + else { + decodedBlob = CssmData::overlay(WrappedKey.KeyData); + CSSM_SIZE bytesDecrypted; + CssmData *unwrapData = + CssmData::overlay(&UnwrappedKey.KeyData); + + DecryptData(CCHandle, + Context, + &decodedBlob, // CipherBufs[], + 1, // CipherBufCount, + unwrapData, // ClearBufs[] + 1, // ClearBufCount + bytesDecrypted, + remData, + Privilege); + + // I'm not 100% sure about this.... + assert(remData.Length == 0); + UnwrappedKey.KeyData.Length = bytesDecrypted; + unwrappedHdr.BlobType = CSSM_KEYBLOB_RAW; + + /* + * Figure out various header fields from resulting blob + */ + switch(wrapFormat) { + case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7: + unwrappedHdr.Format = + CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING; + if(unwrappedHdr.LogicalKeySizeInBits == 0) { + unwrappedHdr.LogicalKeySizeInBits = + (unsigned)(bytesDecrypted * 8); + } + /* app has to infer/know algorithm */ + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS8: + pkcs8InferKeyHeader(UnwrappedKey); + break; + case CSSM_KEYBLOB_WRAPPED_FORMAT_OPENSSL: + /* + * App told us key algorithm (in WrappedKey). + * Infer format and key size. + */ + opensslInferKeyHeader(UnwrappedKey); + break; + } + } + } + catch (...) { + errorLog0("UnwrapKey: DecryptData() threw exception\n"); + freeCssmData(remData, normAllocator); + throw; + } + freeCssmData(remData, normAllocator); + + /* + * One more thing: cook up a BinaryKey if caller wants a + * reference key. + */ + if(keyStorage == CKS_Ref) { + /* + * We have a key in raw format; convert to BinaryKey. + */ + BinaryKey *binKey = NULL; + CSPKeyInfoProvider *provider = infoProvider(UnwrappedKey); + /* optional parameter-bearing key */ + CssmKey *paramKey = Context.get(CSSM_ATTRIBUTE_PARAM_KEY); + provider->CssmKeyToBinary(paramKey, UnwrappedKey.KeyHeader.KeyAttr, &binKey); + addRefKey(*binKey, UnwrappedKey); + delete provider; + } +} +