]> git.saurik.com Git - apple/security.git/blobdiff - securityd/src/dbcrypto.cpp
Security-57031.1.35.tar.gz
[apple/security.git] / securityd / src / dbcrypto.cpp
diff --git a/securityd/src/dbcrypto.cpp b/securityd/src/dbcrypto.cpp
new file mode 100644 (file)
index 0000000..22180f6
--- /dev/null
@@ -0,0 +1,542 @@
+/*
+ * Copyright (c) 2000-2006,2013 Apple Inc. All Rights Reserved.
+ * 
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The 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.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+//
+// dbcrypto - cryptographic core for database and key blob cryptography
+//
+#include "dbcrypto.h"
+#include <securityd_client/ssblob.h>
+#include "server.h"            // just for Server::csp()
+#include <security_cdsa_client/genkey.h>
+#include <security_cdsa_client/cryptoclient.h>
+#include <security_cdsa_client/keyclient.h>
+#include <security_cdsa_client/macclient.h>
+#include <security_cdsa_client/wrapkey.h>
+#include <security_cdsa_utilities/cssmendian.h>
+
+using namespace CssmClient;
+using LowLevelMemoryUtilities::fieldOffsetOf;
+
+
+//
+// The CryptoCore constructor doesn't do anything interesting.
+// It just initializes us to "empty".
+//
+DatabaseCryptoCore::DatabaseCryptoCore() : mHaveMaster(false), mIsValid(false)
+{
+}
+
+DatabaseCryptoCore::~DatabaseCryptoCore()
+{
+    // key objects take care of themselves
+}
+
+
+//
+// Forget the secrets
+//
+void DatabaseCryptoCore::invalidate()
+{
+       mMasterKey.release();
+       mHaveMaster = false;
+       
+       mEncryptionKey.release();
+       mSigningKey.release();
+       mIsValid = false;
+}
+
+
+//
+// Generate new secrets for this crypto core.
+//
+void DatabaseCryptoCore::generateNewSecrets()
+{
+    // create a random DES3 key
+    GenerateKey desGenerator(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE, 24 * 8);
+    mEncryptionKey = desGenerator(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
+        CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
+    
+    // create a random 20 byte HMAC/SHA1 signing "key"
+    GenerateKey signGenerator(Server::csp(), CSSM_ALGID_SHA1HMAC,
+        sizeof(DbBlob::PrivateBlob::SigningKey) * 8);
+    mSigningKey = signGenerator(KeySpec(CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY,
+        CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
+    
+    // secrets established
+    mIsValid = true;
+}
+
+
+CssmClient::Key DatabaseCryptoCore::masterKey()
+{
+       assert(mHaveMaster);
+       return mMasterKey;
+}
+
+
+//
+// Establish the master secret as derived from a passphrase passed in.
+// If a DbBlob is passed, take the salt from it and remember it.
+// If a NULL DbBlob is passed, generate a new (random) salt.
+// Note that the passphrase is NOT remembered; only the master key.
+//
+void DatabaseCryptoCore::setup(const DbBlob *blob, const CssmData &passphrase)
+{
+       if (blob)
+               memcpy(mSalt, blob->salt, sizeof(mSalt));
+       else
+               Server::active().random(mSalt);
+    mMasterKey = deriveDbMasterKey(passphrase);
+       mHaveMaster = true;
+}
+
+
+//
+// Establish the master secret directly from a master key passed in.
+// We will copy the KeyData (caller still owns its copy).
+// Blob/salt handling as above.
+//
+void DatabaseCryptoCore::setup(const DbBlob *blob, CssmClient::Key master)
+{
+       // pre-screen the key
+       CssmKey::Header header = master.header();
+       if (header.keyClass() != CSSM_KEYCLASS_SESSION_KEY)
+               CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
+       if (header.algorithm() != CSSM_ALGID_3DES_3KEY_EDE)
+               CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
+       
+       // accept it
+       if (blob)
+               memcpy(mSalt, blob->salt, sizeof(mSalt));
+       else
+               Server::active().random(mSalt);
+       mMasterKey = master;
+       mHaveMaster = true;
+}
+
+bool DatabaseCryptoCore::get_encryption_key(CssmOwnedData &data)
+{
+    bool result = false;
+    if (isValid()) {
+        data = mEncryptionKey->keyData();
+        result = true;
+    }
+    return result;
+}
+
+//
+// Given a putative passphrase, determine whether that passphrase
+// properly generates the database's master secret.
+// Return a boolean accordingly. Do not change our state.
+// The database must have a master secret (to compare with).
+// Note that any errors thrown by the cryptography here will actually
+// throw out of validatePassphrase, since they "should not happen" and
+// thus indicate a problem *beyond* (just) a bad passphrase.
+//
+bool DatabaseCryptoCore::validatePassphrase(const CssmData &passphrase)
+{
+       assert(hasMaster());
+       CssmClient::Key master = deriveDbMasterKey(passphrase);
+       
+       // to compare master with mMaster, see if they encrypt alike
+       StringData probe
+               ("Now is the time for all good processes to come to the aid of their kernel.");
+       CssmData noRemainder((void *)1, 0);     // no cipher overflow
+       Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
+       cryptor.mode(CSSM_ALGMODE_CBCPadIV8);
+       cryptor.padding(CSSM_PADDING_PKCS1);
+       uint8 iv[8];    // leave uninitialized; pseudo-random is cool
+       cryptor.initVector(CssmData::wrap(iv));
+       
+       cryptor.key(master);
+       CssmAutoData cipher1(Server::csp().allocator());
+       cryptor.encrypt(probe, cipher1.get(), noRemainder);
+       
+       cryptor.key(mMasterKey);
+       CssmAutoData cipher2(Server::csp().allocator());
+       cryptor.encrypt(probe, cipher2.get(), noRemainder);
+       
+       return cipher1 == cipher2;
+}
+
+
+//
+// Encode a database blob from the core.
+//
+DbBlob *DatabaseCryptoCore::encodeCore(const DbBlob &blobTemplate,
+    const CssmData &publicAcl, const CssmData &privateAcl) const
+{
+    assert(isValid());         // must have secrets to work from
+
+    // make a new IV
+    uint8 iv[8];
+    Server::active().random(iv);
+    
+    // build the encrypted section blob
+    CssmData &encryptionBits = *mEncryptionKey;
+    CssmData &signingBits = *mSigningKey;
+    CssmData incrypt[3];
+    incrypt[0] = encryptionBits;
+    incrypt[1] = signingBits;
+    incrypt[2] = privateAcl;
+    CssmData cryptoBlob, remData;
+    Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
+    cryptor.mode(CSSM_ALGMODE_CBCPadIV8);
+    cryptor.padding(CSSM_PADDING_PKCS1);
+    cryptor.key(mMasterKey);
+    CssmData ivd(iv, sizeof(iv)); cryptor.initVector(ivd);
+    cryptor.encrypt(incrypt, 3, &cryptoBlob, 1, remData);
+    
+    // allocate the final DbBlob, uh, blob
+    size_t length = sizeof(DbBlob) + publicAcl.length() + cryptoBlob.length();
+    DbBlob *blob = Allocator::standard().malloc<DbBlob>(length);
+    
+    // assemble the DbBlob
+    memset(blob, 0x7d, sizeof(DbBlob));        // deterministically fill any alignment gaps
+    blob->initialize();
+    blob->randomSignature = blobTemplate.randomSignature;
+    blob->sequence = blobTemplate.sequence;
+    blob->params = blobTemplate.params;
+       memcpy(blob->salt, mSalt, sizeof(blob->salt));
+    memcpy(blob->iv, iv, sizeof(iv));
+    memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length());
+    blob->startCryptoBlob = sizeof(DbBlob) + publicAcl.length();
+    memcpy(blob->cryptoBlob(), cryptoBlob, cryptoBlob.length());
+    blob->totalLength = blob->startCryptoBlob + cryptoBlob.length();
+    
+    // sign the blob
+    CssmData signChunk[] = {
+               CssmData(blob->data(), fieldOffsetOf(&DbBlob::blobSignature)),
+               CssmData(blob->publicAclBlob(), publicAcl.length() + cryptoBlob.length())
+       };
+    CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
+    GenerateMac signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY);
+    signer.key(mSigningKey);
+    signer.sign(signChunk, 2, signature);
+    assert(signature.length() == sizeof(blob->blobSignature));
+    
+    // all done. Clean up
+    Server::csp()->allocator().free(cryptoBlob);
+    return blob;
+}
+
+
+//
+// Decode a database blob into the core.
+// Throws exceptions if decoding fails.
+// Memory returned in privateAclBlob is allocated and becomes owned by caller.
+//
+void DatabaseCryptoCore::decodeCore(const DbBlob *blob, void **privateAclBlob)
+{
+       assert(mHaveMaster);    // must have master key installed
+    
+    // try to decrypt the cryptoblob section
+    Decrypt decryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
+    decryptor.mode(CSSM_ALGMODE_CBCPadIV8);
+    decryptor.padding(CSSM_PADDING_PKCS1);
+    decryptor.key(mMasterKey);
+    CssmData ivd = CssmData::wrap(blob->iv); decryptor.initVector(ivd);
+    CssmData cryptoBlob = CssmData::wrap(blob->cryptoBlob(), blob->cryptoBlobLength());
+    CssmData decryptedBlob, remData;
+    decryptor.decrypt(cryptoBlob, decryptedBlob, remData);
+    DbBlob::PrivateBlob *privateBlob = decryptedBlob.interpretedAs<DbBlob::PrivateBlob>();
+    
+    // tentatively establish keys
+    mEncryptionKey = makeRawKey(privateBlob->encryptionKey,
+        sizeof(privateBlob->encryptionKey), CSSM_ALGID_3DES_3KEY_EDE,
+        CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP);
+    mSigningKey = makeRawKey(privateBlob->signingKey,
+        sizeof(privateBlob->signingKey), CSSM_ALGID_SHA1HMAC,
+        CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY);
+    
+    // verify signature on the whole blob
+    CssmData signChunk[] = {
+               CssmData::wrap(blob->data(), fieldOffsetOf(&DbBlob::blobSignature)),
+       CssmData::wrap(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
+       };
+    CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC;
+#if defined(COMPAT_OSX_10_0)
+    if (blob->version() == blob->version_MacOS_10_0)
+        verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY;  // BSafe bug compatibility
+#endif
+    VerifyMac verifier(Server::csp(), verifyAlgorithm);
+    verifier.key(mSigningKey);
+    verifier.verify(signChunk, 2, CssmData::wrap(blob->blobSignature));
+    
+    // all checks out; start extracting fields
+    if (privateAclBlob) {
+        // extract private ACL blob as a separately allocated area
+        uint32 blobLength = decryptedBlob.length() - sizeof(DbBlob::PrivateBlob);
+        *privateAclBlob = Allocator::standard().malloc(blobLength);
+        memcpy(*privateAclBlob, privateBlob->privateAclBlob(), blobLength);
+    }
+        
+    // secrets have been established
+    mIsValid = true;
+    Allocator::standard().free(privateBlob);
+}
+
+
+//
+// Make another DatabaseCryptoCore's operational secrets our own.  
+// Intended for keychain synchronization.  
+//
+void DatabaseCryptoCore::importSecrets(const DatabaseCryptoCore &src)
+{
+       assert(src.isValid());  // must have called src.decodeCore() first
+       assert(hasMaster());
+       mEncryptionKey = src.mEncryptionKey;
+       mSigningKey = src.mSigningKey;
+    mIsValid = true;
+}
+
+//
+// Encode a key blob
+//
+KeyBlob *DatabaseCryptoCore::encodeKeyCore(const CssmKey &inKey,
+    const CssmData &publicAcl, const CssmData &privateAcl,
+       bool inTheClear) const
+{
+    CssmKey key = inKey;
+       uint8 iv[8];
+       CssmKey wrappedKey;
+
+       if(inTheClear && (privateAcl.Length != 0)) {
+               /* can't store private ACL component in the clear */
+               CssmError::throwMe(CSSMERR_DL_INVALID_ACCESS_CREDENTIALS);
+       }
+       
+    // extract and hold some header bits the CSP does not want to see
+    uint32 heldAttributes = key.attributes() & managedAttributes;
+    key.clearAttribute(managedAttributes);
+       key.setAttribute(forcedAttributes);
+    
+       if(inTheClear) {
+               /* NULL wrap of public key */
+               WrapKey wrap(Server::csp(), CSSM_ALGID_NONE);
+               wrap(key, wrappedKey, NULL);
+       }
+       else {
+               assert(isValid());              // need our database secrets
+               
+               // create new IV
+               Server::active().random(iv);
+               
+          // use a CMS wrap to encrypt the key
+               WrapKey wrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
+               wrap.key(mEncryptionKey);
+               wrap.mode(CSSM_ALGMODE_CBCPadIV8);
+               wrap.padding(CSSM_PADDING_PKCS1);
+               CssmData ivd(iv, sizeof(iv)); wrap.initVector(ivd);
+               wrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
+                       uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM));
+               wrap(key, wrappedKey, &privateAcl);
+    }
+       
+    // stick the held attribute bits back in
+       key.clearAttribute(forcedAttributes);
+    key.setAttribute(heldAttributes);
+    
+    // allocate the final KeyBlob, uh, blob
+    size_t length = sizeof(KeyBlob) + publicAcl.length() + wrappedKey.length();
+    KeyBlob *blob = Allocator::standard().malloc<KeyBlob>(length);
+    
+    // assemble the KeyBlob
+    memset(blob, 0, sizeof(KeyBlob));  // fill alignment gaps
+    blob->initialize();
+       if(!inTheClear) {
+               memcpy(blob->iv, iv, sizeof(iv));
+       }
+    blob->header = key.header();
+       h2ni(blob->header);     // endian-correct the header
+    blob->wrappedHeader.blobType = wrappedKey.blobType();
+    blob->wrappedHeader.blobFormat = wrappedKey.blobFormat();
+    blob->wrappedHeader.wrapAlgorithm = wrappedKey.wrapAlgorithm();
+    blob->wrappedHeader.wrapMode = wrappedKey.wrapMode();
+    memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length());
+    blob->startCryptoBlob = sizeof(KeyBlob) + publicAcl.length();
+    memcpy(blob->cryptoBlob(), wrappedKey.data(), wrappedKey.length());
+    blob->totalLength = blob->startCryptoBlob + wrappedKey.length();
+    
+       if(inTheClear) {
+               /* indicate that this is cleartext for decoding */
+               blob->setClearTextSignature();
+       }
+       else {
+               // sign the blob
+               CssmData signChunk[] = {
+                       CssmData(blob->data(), fieldOffsetOf(&KeyBlob::blobSignature)),
+                       CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
+               };
+               CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
+               GenerateMac signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY);  //@@@!!! CRUD
+               signer.key(mSigningKey);
+               signer.sign(signChunk, 2, signature);
+               assert(signature.length() == sizeof(blob->blobSignature));
+    }
+       
+    // all done. Clean up
+    Server::csp()->allocator().free(wrappedKey);
+    return blob;
+}
+
+
+//
+// Decode a key blob
+//
+void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob,
+    CssmKey &key, void * &pubAcl, void * &privAcl) const
+{    
+    // Assemble the encrypted blob as a CSSM "wrapped key"
+    CssmKey wrappedKey;
+    wrappedKey.KeyHeader = blob->header;
+       h2ni(wrappedKey.KeyHeader);
+    wrappedKey.blobType(blob->wrappedHeader.blobType);
+    wrappedKey.blobFormat(blob->wrappedHeader.blobFormat);
+    wrappedKey.wrapAlgorithm(blob->wrappedHeader.wrapAlgorithm);
+    wrappedKey.wrapMode(blob->wrappedHeader.wrapMode);
+    wrappedKey.KeyData = CssmData(blob->cryptoBlob(), blob->cryptoBlobLength());
+       
+       bool inTheClear = blob->isClearText();
+       if(!inTheClear) {
+               // verify signature (check against corruption)
+               assert(isValid());              // need our database secrets
+               CssmData signChunk[] = {
+                       CssmData::wrap(blob, fieldOffsetOf(&KeyBlob::blobSignature)),
+                       CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
+               };
+               CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC;
+       #if defined(COMPAT_OSX_10_0)
+               if (blob->version() == blob->version_MacOS_10_0)
+                       verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY;   // BSafe bug compatibility
+       #endif
+               VerifyMac verifier(Server::csp(), verifyAlgorithm);
+               verifier.key(mSigningKey);
+               CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
+               verifier.verify(signChunk, 2, signature);
+    }
+       /* else signature indicates cleartext */
+       
+    // extract and hold some header bits the CSP does not want to see
+    uint32 heldAttributes = n2h(blob->header.attributes()) & managedAttributes;
+   
+       CssmData privAclData;
+       if(inTheClear) {
+               /* NULL unwrap */
+               UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);
+               wrappedKey.clearAttribute(managedAttributes);    //@@@ shouldn't be needed(?)
+               unwrap(wrappedKey,
+                       KeySpec(n2h(blob->header.usage()),
+                               (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes),
+                       key, &privAclData);
+       }
+       else {
+               // decrypt the key using an unwrapping operation
+               UnwrapKey unwrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
+               unwrap.key(mEncryptionKey);
+               unwrap.mode(CSSM_ALGMODE_CBCPadIV8);
+               unwrap.padding(CSSM_PADDING_PKCS1);
+               CssmData ivd(blob->iv, sizeof(blob->iv)); unwrap.initVector(ivd);
+               unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
+                       uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM));
+               wrappedKey.clearAttribute(managedAttributes);    //@@@ shouldn't be needed(?)
+               unwrap(wrappedKey,
+                       KeySpec(n2h(blob->header.usage()),
+                               (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes),
+                       key, &privAclData);
+    }
+       
+    // compare retrieved key headers with blob headers (sanity check)
+    // @@@ this should probably be checked over carefully
+    CssmKey::Header &real = key.header();
+    CssmKey::Header &incoming = blob->header;
+       n2hi(incoming);
+
+    if (real.HeaderVersion != incoming.HeaderVersion ||
+        real.cspGuid() != incoming.cspGuid())
+        CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
+    if (real.algorithm() != incoming.algorithm())
+        CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
+        
+    // re-insert held bits
+    key.header().KeyAttr |= heldAttributes;
+    
+       if(inTheClear && (real.keyClass() != CSSM_KEYCLASS_PUBLIC_KEY)) {
+               /* Spoof - cleartext KeyBlob passed off as private key */
+        CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
+       }
+       
+    // got a valid key: return the pieces
+    pubAcl = blob->publicAclBlob();            // points into blob (shared)
+    privAcl = privAclData;                             // was allocated by CSP decrypt, else NULL for
+                                                                               // cleatext keys
+    // key was set by unwrap operation
+}
+
+
+//
+// Derive the blob-specific database blob encryption key from the passphrase and the salt.
+//
+CssmClient::Key DatabaseCryptoCore::deriveDbMasterKey(const CssmData &passphrase) const
+{
+    // derive an encryption key and IV from passphrase and salt
+    CssmClient::DeriveKey makeKey(Server::csp(),
+        CSSM_ALGID_PKCS5_PBKDF2, CSSM_ALGID_3DES_3KEY_EDE, 24 * 8);
+    makeKey.iterationCount(1000);
+       CssmData salt = CssmData::wrap(mSalt);
+    makeKey.salt(salt);
+    CSSM_PKCS5_PBKDF2_PARAMS params;
+    params.Passphrase = passphrase;
+    params.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
+       CssmData paramData = CssmData::wrap(params);
+    return makeKey(&paramData, KeySpec(CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
+        CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
+}
+
+
+//
+// Turn raw keybits into a symmetric key in the CSP
+//
+CssmClient::Key DatabaseCryptoCore::makeRawKey(void *data, size_t length,
+    CSSM_ALGORITHMS algid, CSSM_KEYUSE usage)
+{
+    // build a fake key
+    CssmKey key;
+    key.header().BlobType = CSSM_KEYBLOB_RAW;
+    key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
+    key.header().AlgorithmId = algid;
+    key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY;
+    key.header().KeyUsage = usage;
+    key.header().KeyAttr = 0;
+    key.KeyData = CssmData(data, length);
+    
+    // unwrap it into the CSP (but keep it raw)
+    UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);
+    CssmKey unwrappedKey;
+    CssmData descriptiveData;
+    unwrap(key,
+        KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE),
+        unwrappedKey, &descriptiveData, NULL);
+    return CssmClient::Key(Server::csp(), unwrappedKey);
+}