--- /dev/null
+/*
+ * 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<CssmKey>(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<CssmKey>(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<CssmKey>(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<CssmKey>(CSSM_ATTRIBUTE_PARAM_KEY);
+ provider->CssmKeyToBinary(paramKey, UnwrappedKey.KeyHeader.KeyAttr, &binKey);
+ addRefKey(*binKey, UnwrappedKey);
+ delete provider;
+ }
+}
+