]> git.saurik.com Git - apple/security.git/blob - securityd/src/dbcrypto.cpp
Security-57031.40.6.tar.gz
[apple/security.git] / securityd / src / dbcrypto.cpp
1 /*
2 * Copyright (c) 2000-2006,2013 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25 //
26 // dbcrypto - cryptographic core for database and key blob cryptography
27 //
28 #include "dbcrypto.h"
29 #include <securityd_client/ssblob.h>
30 #include "server.h" // just for Server::csp()
31 #include <security_cdsa_client/genkey.h>
32 #include <security_cdsa_client/cryptoclient.h>
33 #include <security_cdsa_client/keyclient.h>
34 #include <security_cdsa_client/macclient.h>
35 #include <security_cdsa_client/wrapkey.h>
36 #include <security_cdsa_utilities/cssmendian.h>
37
38 using namespace CssmClient;
39 using LowLevelMemoryUtilities::fieldOffsetOf;
40
41
42 //
43 // The CryptoCore constructor doesn't do anything interesting.
44 // It just initializes us to "empty".
45 //
46 DatabaseCryptoCore::DatabaseCryptoCore() : mHaveMaster(false), mIsValid(false)
47 {
48 }
49
50 DatabaseCryptoCore::~DatabaseCryptoCore()
51 {
52 // key objects take care of themselves
53 }
54
55
56 //
57 // Forget the secrets
58 //
59 void DatabaseCryptoCore::invalidate()
60 {
61 mMasterKey.release();
62 mHaveMaster = false;
63
64 mEncryptionKey.release();
65 mSigningKey.release();
66 mIsValid = false;
67 }
68
69
70 //
71 // Generate new secrets for this crypto core.
72 //
73 void DatabaseCryptoCore::generateNewSecrets()
74 {
75 // create a random DES3 key
76 GenerateKey desGenerator(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE, 24 * 8);
77 mEncryptionKey = desGenerator(KeySpec(CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP,
78 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
79
80 // create a random 20 byte HMAC/SHA1 signing "key"
81 GenerateKey signGenerator(Server::csp(), CSSM_ALGID_SHA1HMAC,
82 sizeof(DbBlob::PrivateBlob::SigningKey) * 8);
83 mSigningKey = signGenerator(KeySpec(CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY,
84 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
85
86 // secrets established
87 mIsValid = true;
88 }
89
90
91 CssmClient::Key DatabaseCryptoCore::masterKey()
92 {
93 assert(mHaveMaster);
94 return mMasterKey;
95 }
96
97
98 //
99 // Establish the master secret as derived from a passphrase passed in.
100 // If a DbBlob is passed, take the salt from it and remember it.
101 // If a NULL DbBlob is passed, generate a new (random) salt.
102 // Note that the passphrase is NOT remembered; only the master key.
103 //
104 void DatabaseCryptoCore::setup(const DbBlob *blob, const CssmData &passphrase)
105 {
106 if (blob)
107 memcpy(mSalt, blob->salt, sizeof(mSalt));
108 else
109 Server::active().random(mSalt);
110 mMasterKey = deriveDbMasterKey(passphrase);
111 mHaveMaster = true;
112 }
113
114
115 //
116 // Establish the master secret directly from a master key passed in.
117 // We will copy the KeyData (caller still owns its copy).
118 // Blob/salt handling as above.
119 //
120 void DatabaseCryptoCore::setup(const DbBlob *blob, CssmClient::Key master)
121 {
122 // pre-screen the key
123 CssmKey::Header header = master.header();
124 if (header.keyClass() != CSSM_KEYCLASS_SESSION_KEY)
125 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY_CLASS);
126 if (header.algorithm() != CSSM_ALGID_3DES_3KEY_EDE)
127 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
128
129 // accept it
130 if (blob)
131 memcpy(mSalt, blob->salt, sizeof(mSalt));
132 else
133 Server::active().random(mSalt);
134 mMasterKey = master;
135 mHaveMaster = true;
136 }
137
138 bool DatabaseCryptoCore::get_encryption_key(CssmOwnedData &data)
139 {
140 bool result = false;
141 if (isValid()) {
142 data = mEncryptionKey->keyData();
143 result = true;
144 }
145 return result;
146 }
147
148 //
149 // Given a putative passphrase, determine whether that passphrase
150 // properly generates the database's master secret.
151 // Return a boolean accordingly. Do not change our state.
152 // The database must have a master secret (to compare with).
153 // Note that any errors thrown by the cryptography here will actually
154 // throw out of validatePassphrase, since they "should not happen" and
155 // thus indicate a problem *beyond* (just) a bad passphrase.
156 //
157 bool DatabaseCryptoCore::validatePassphrase(const CssmData &passphrase)
158 {
159 assert(hasMaster());
160 CssmClient::Key master = deriveDbMasterKey(passphrase);
161
162 // to compare master with mMaster, see if they encrypt alike
163 StringData probe
164 ("Now is the time for all good processes to come to the aid of their kernel.");
165 CssmData noRemainder((void *)1, 0); // no cipher overflow
166 Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
167 cryptor.mode(CSSM_ALGMODE_CBCPadIV8);
168 cryptor.padding(CSSM_PADDING_PKCS1);
169 uint8 iv[8]; // leave uninitialized; pseudo-random is cool
170 cryptor.initVector(CssmData::wrap(iv));
171
172 cryptor.key(master);
173 CssmAutoData cipher1(Server::csp().allocator());
174 cryptor.encrypt(probe, cipher1.get(), noRemainder);
175
176 cryptor.key(mMasterKey);
177 CssmAutoData cipher2(Server::csp().allocator());
178 cryptor.encrypt(probe, cipher2.get(), noRemainder);
179
180 return cipher1 == cipher2;
181 }
182
183
184 //
185 // Encode a database blob from the core.
186 //
187 DbBlob *DatabaseCryptoCore::encodeCore(const DbBlob &blobTemplate,
188 const CssmData &publicAcl, const CssmData &privateAcl) const
189 {
190 assert(isValid()); // must have secrets to work from
191
192 // make a new IV
193 uint8 iv[8];
194 Server::active().random(iv);
195
196 // build the encrypted section blob
197 CssmData &encryptionBits = *mEncryptionKey;
198 CssmData &signingBits = *mSigningKey;
199 CssmData incrypt[3];
200 incrypt[0] = encryptionBits;
201 incrypt[1] = signingBits;
202 incrypt[2] = privateAcl;
203 CssmData cryptoBlob, remData;
204 Encrypt cryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
205 cryptor.mode(CSSM_ALGMODE_CBCPadIV8);
206 cryptor.padding(CSSM_PADDING_PKCS1);
207 cryptor.key(mMasterKey);
208 CssmData ivd(iv, sizeof(iv)); cryptor.initVector(ivd);
209 cryptor.encrypt(incrypt, 3, &cryptoBlob, 1, remData);
210
211 // allocate the final DbBlob, uh, blob
212 size_t length = sizeof(DbBlob) + publicAcl.length() + cryptoBlob.length();
213 DbBlob *blob = Allocator::standard().malloc<DbBlob>(length);
214
215 // assemble the DbBlob
216 memset(blob, 0x7d, sizeof(DbBlob)); // deterministically fill any alignment gaps
217 blob->initialize();
218 blob->randomSignature = blobTemplate.randomSignature;
219 blob->sequence = blobTemplate.sequence;
220 blob->params = blobTemplate.params;
221 memcpy(blob->salt, mSalt, sizeof(blob->salt));
222 memcpy(blob->iv, iv, sizeof(iv));
223 memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length());
224 blob->startCryptoBlob = sizeof(DbBlob) + publicAcl.length();
225 memcpy(blob->cryptoBlob(), cryptoBlob, cryptoBlob.length());
226 blob->totalLength = blob->startCryptoBlob + cryptoBlob.length();
227
228 // sign the blob
229 CssmData signChunk[] = {
230 CssmData(blob->data(), fieldOffsetOf(&DbBlob::blobSignature)),
231 CssmData(blob->publicAclBlob(), publicAcl.length() + cryptoBlob.length())
232 };
233 CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
234 GenerateMac signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY);
235 signer.key(mSigningKey);
236 signer.sign(signChunk, 2, signature);
237 assert(signature.length() == sizeof(blob->blobSignature));
238
239 // all done. Clean up
240 Server::csp()->allocator().free(cryptoBlob);
241 return blob;
242 }
243
244
245 //
246 // Decode a database blob into the core.
247 // Throws exceptions if decoding fails.
248 // Memory returned in privateAclBlob is allocated and becomes owned by caller.
249 //
250 void DatabaseCryptoCore::decodeCore(const DbBlob *blob, void **privateAclBlob)
251 {
252 assert(mHaveMaster); // must have master key installed
253
254 // try to decrypt the cryptoblob section
255 Decrypt decryptor(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
256 decryptor.mode(CSSM_ALGMODE_CBCPadIV8);
257 decryptor.padding(CSSM_PADDING_PKCS1);
258 decryptor.key(mMasterKey);
259 CssmData ivd = CssmData::wrap(blob->iv); decryptor.initVector(ivd);
260 CssmData cryptoBlob = CssmData::wrap(blob->cryptoBlob(), blob->cryptoBlobLength());
261 CssmData decryptedBlob, remData;
262 decryptor.decrypt(cryptoBlob, decryptedBlob, remData);
263 DbBlob::PrivateBlob *privateBlob = decryptedBlob.interpretedAs<DbBlob::PrivateBlob>();
264
265 // tentatively establish keys
266 mEncryptionKey = makeRawKey(privateBlob->encryptionKey,
267 sizeof(privateBlob->encryptionKey), CSSM_ALGID_3DES_3KEY_EDE,
268 CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP);
269 mSigningKey = makeRawKey(privateBlob->signingKey,
270 sizeof(privateBlob->signingKey), CSSM_ALGID_SHA1HMAC,
271 CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY);
272
273 // verify signature on the whole blob
274 CssmData signChunk[] = {
275 CssmData::wrap(blob->data(), fieldOffsetOf(&DbBlob::blobSignature)),
276 CssmData::wrap(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
277 };
278 CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC;
279 #if defined(COMPAT_OSX_10_0)
280 if (blob->version() == blob->version_MacOS_10_0)
281 verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; // BSafe bug compatibility
282 #endif
283 VerifyMac verifier(Server::csp(), verifyAlgorithm);
284 verifier.key(mSigningKey);
285 verifier.verify(signChunk, 2, CssmData::wrap(blob->blobSignature));
286
287 // all checks out; start extracting fields
288 if (privateAclBlob) {
289 // extract private ACL blob as a separately allocated area
290 uint32 blobLength = decryptedBlob.length() - sizeof(DbBlob::PrivateBlob);
291 *privateAclBlob = Allocator::standard().malloc(blobLength);
292 memcpy(*privateAclBlob, privateBlob->privateAclBlob(), blobLength);
293 }
294
295 // secrets have been established
296 mIsValid = true;
297 Allocator::standard().free(privateBlob);
298 }
299
300
301 //
302 // Make another DatabaseCryptoCore's operational secrets our own.
303 // Intended for keychain synchronization.
304 //
305 void DatabaseCryptoCore::importSecrets(const DatabaseCryptoCore &src)
306 {
307 assert(src.isValid()); // must have called src.decodeCore() first
308 assert(hasMaster());
309 mEncryptionKey = src.mEncryptionKey;
310 mSigningKey = src.mSigningKey;
311 mIsValid = true;
312 }
313
314 //
315 // Encode a key blob
316 //
317 KeyBlob *DatabaseCryptoCore::encodeKeyCore(const CssmKey &inKey,
318 const CssmData &publicAcl, const CssmData &privateAcl,
319 bool inTheClear) const
320 {
321 CssmKey key = inKey;
322 uint8 iv[8];
323 CssmKey wrappedKey;
324
325 if(inTheClear && (privateAcl.Length != 0)) {
326 /* can't store private ACL component in the clear */
327 CssmError::throwMe(CSSMERR_DL_INVALID_ACCESS_CREDENTIALS);
328 }
329
330 // extract and hold some header bits the CSP does not want to see
331 uint32 heldAttributes = key.attributes() & managedAttributes;
332 key.clearAttribute(managedAttributes);
333 key.setAttribute(forcedAttributes);
334
335 if(inTheClear) {
336 /* NULL wrap of public key */
337 WrapKey wrap(Server::csp(), CSSM_ALGID_NONE);
338 wrap(key, wrappedKey, NULL);
339 }
340 else {
341 assert(isValid()); // need our database secrets
342
343 // create new IV
344 Server::active().random(iv);
345
346 // use a CMS wrap to encrypt the key
347 WrapKey wrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
348 wrap.key(mEncryptionKey);
349 wrap.mode(CSSM_ALGMODE_CBCPadIV8);
350 wrap.padding(CSSM_PADDING_PKCS1);
351 CssmData ivd(iv, sizeof(iv)); wrap.initVector(ivd);
352 wrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
353 uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM));
354 wrap(key, wrappedKey, &privateAcl);
355 }
356
357 // stick the held attribute bits back in
358 key.clearAttribute(forcedAttributes);
359 key.setAttribute(heldAttributes);
360
361 // allocate the final KeyBlob, uh, blob
362 size_t length = sizeof(KeyBlob) + publicAcl.length() + wrappedKey.length();
363 KeyBlob *blob = Allocator::standard().malloc<KeyBlob>(length);
364
365 // assemble the KeyBlob
366 memset(blob, 0, sizeof(KeyBlob)); // fill alignment gaps
367 blob->initialize();
368 if(!inTheClear) {
369 memcpy(blob->iv, iv, sizeof(iv));
370 }
371 blob->header = key.header();
372 h2ni(blob->header); // endian-correct the header
373 blob->wrappedHeader.blobType = wrappedKey.blobType();
374 blob->wrappedHeader.blobFormat = wrappedKey.blobFormat();
375 blob->wrappedHeader.wrapAlgorithm = wrappedKey.wrapAlgorithm();
376 blob->wrappedHeader.wrapMode = wrappedKey.wrapMode();
377 memcpy(blob->publicAclBlob(), publicAcl, publicAcl.length());
378 blob->startCryptoBlob = sizeof(KeyBlob) + publicAcl.length();
379 memcpy(blob->cryptoBlob(), wrappedKey.data(), wrappedKey.length());
380 blob->totalLength = blob->startCryptoBlob + wrappedKey.length();
381
382 if(inTheClear) {
383 /* indicate that this is cleartext for decoding */
384 blob->setClearTextSignature();
385 }
386 else {
387 // sign the blob
388 CssmData signChunk[] = {
389 CssmData(blob->data(), fieldOffsetOf(&KeyBlob::blobSignature)),
390 CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
391 };
392 CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
393 GenerateMac signer(Server::csp(), CSSM_ALGID_SHA1HMAC_LEGACY); //@@@!!! CRUD
394 signer.key(mSigningKey);
395 signer.sign(signChunk, 2, signature);
396 assert(signature.length() == sizeof(blob->blobSignature));
397 }
398
399 // all done. Clean up
400 Server::csp()->allocator().free(wrappedKey);
401 return blob;
402 }
403
404
405 //
406 // Decode a key blob
407 //
408 void DatabaseCryptoCore::decodeKeyCore(KeyBlob *blob,
409 CssmKey &key, void * &pubAcl, void * &privAcl) const
410 {
411 // Assemble the encrypted blob as a CSSM "wrapped key"
412 CssmKey wrappedKey;
413 wrappedKey.KeyHeader = blob->header;
414 h2ni(wrappedKey.KeyHeader);
415 wrappedKey.blobType(blob->wrappedHeader.blobType);
416 wrappedKey.blobFormat(blob->wrappedHeader.blobFormat);
417 wrappedKey.wrapAlgorithm(blob->wrappedHeader.wrapAlgorithm);
418 wrappedKey.wrapMode(blob->wrappedHeader.wrapMode);
419 wrappedKey.KeyData = CssmData(blob->cryptoBlob(), blob->cryptoBlobLength());
420
421 bool inTheClear = blob->isClearText();
422 if(!inTheClear) {
423 // verify signature (check against corruption)
424 assert(isValid()); // need our database secrets
425 CssmData signChunk[] = {
426 CssmData::wrap(blob, fieldOffsetOf(&KeyBlob::blobSignature)),
427 CssmData(blob->publicAclBlob(), blob->publicAclBlobLength() + blob->cryptoBlobLength())
428 };
429 CSSM_ALGORITHMS verifyAlgorithm = CSSM_ALGID_SHA1HMAC;
430 #if defined(COMPAT_OSX_10_0)
431 if (blob->version() == blob->version_MacOS_10_0)
432 verifyAlgorithm = CSSM_ALGID_SHA1HMAC_LEGACY; // BSafe bug compatibility
433 #endif
434 VerifyMac verifier(Server::csp(), verifyAlgorithm);
435 verifier.key(mSigningKey);
436 CssmData signature(blob->blobSignature, sizeof(blob->blobSignature));
437 verifier.verify(signChunk, 2, signature);
438 }
439 /* else signature indicates cleartext */
440
441 // extract and hold some header bits the CSP does not want to see
442 uint32 heldAttributes = n2h(blob->header.attributes()) & managedAttributes;
443
444 CssmData privAclData;
445 if(inTheClear) {
446 /* NULL unwrap */
447 UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);
448 wrappedKey.clearAttribute(managedAttributes); //@@@ shouldn't be needed(?)
449 unwrap(wrappedKey,
450 KeySpec(n2h(blob->header.usage()),
451 (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes),
452 key, &privAclData);
453 }
454 else {
455 // decrypt the key using an unwrapping operation
456 UnwrapKey unwrap(Server::csp(), CSSM_ALGID_3DES_3KEY_EDE);
457 unwrap.key(mEncryptionKey);
458 unwrap.mode(CSSM_ALGMODE_CBCPadIV8);
459 unwrap.padding(CSSM_PADDING_PKCS1);
460 CssmData ivd(blob->iv, sizeof(blob->iv)); unwrap.initVector(ivd);
461 unwrap.add(CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT,
462 uint32(CSSM_KEYBLOB_WRAPPED_FORMAT_APPLE_CUSTOM));
463 wrappedKey.clearAttribute(managedAttributes); //@@@ shouldn't be needed(?)
464 unwrap(wrappedKey,
465 KeySpec(n2h(blob->header.usage()),
466 (n2h(blob->header.attributes()) & ~managedAttributes) | forcedAttributes),
467 key, &privAclData);
468 }
469
470 // compare retrieved key headers with blob headers (sanity check)
471 // @@@ this should probably be checked over carefully
472 CssmKey::Header &real = key.header();
473 CssmKey::Header &incoming = blob->header;
474 n2hi(incoming);
475
476 if (real.HeaderVersion != incoming.HeaderVersion ||
477 real.cspGuid() != incoming.cspGuid())
478 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
479 if (real.algorithm() != incoming.algorithm())
480 CssmError::throwMe(CSSMERR_CSP_INVALID_ALGORITHM);
481
482 // re-insert held bits
483 key.header().KeyAttr |= heldAttributes;
484
485 if(inTheClear && (real.keyClass() != CSSM_KEYCLASS_PUBLIC_KEY)) {
486 /* Spoof - cleartext KeyBlob passed off as private key */
487 CssmError::throwMe(CSSMERR_CSP_INVALID_KEY);
488 }
489
490 // got a valid key: return the pieces
491 pubAcl = blob->publicAclBlob(); // points into blob (shared)
492 privAcl = privAclData; // was allocated by CSP decrypt, else NULL for
493 // cleatext keys
494 // key was set by unwrap operation
495 }
496
497
498 //
499 // Derive the blob-specific database blob encryption key from the passphrase and the salt.
500 //
501 CssmClient::Key DatabaseCryptoCore::deriveDbMasterKey(const CssmData &passphrase) const
502 {
503 // derive an encryption key and IV from passphrase and salt
504 CssmClient::DeriveKey makeKey(Server::csp(),
505 CSSM_ALGID_PKCS5_PBKDF2, CSSM_ALGID_3DES_3KEY_EDE, 24 * 8);
506 makeKey.iterationCount(1000);
507 CssmData salt = CssmData::wrap(mSalt);
508 makeKey.salt(salt);
509 CSSM_PKCS5_PBKDF2_PARAMS params;
510 params.Passphrase = passphrase;
511 params.PseudoRandomFunction = CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1;
512 CssmData paramData = CssmData::wrap(params);
513 return makeKey(&paramData, KeySpec(CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT,
514 CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE));
515 }
516
517
518 //
519 // Turn raw keybits into a symmetric key in the CSP
520 //
521 CssmClient::Key DatabaseCryptoCore::makeRawKey(void *data, size_t length,
522 CSSM_ALGORITHMS algid, CSSM_KEYUSE usage)
523 {
524 // build a fake key
525 CssmKey key;
526 key.header().BlobType = CSSM_KEYBLOB_RAW;
527 key.header().Format = CSSM_KEYBLOB_RAW_FORMAT_OCTET_STRING;
528 key.header().AlgorithmId = algid;
529 key.header().KeyClass = CSSM_KEYCLASS_SESSION_KEY;
530 key.header().KeyUsage = usage;
531 key.header().KeyAttr = 0;
532 key.KeyData = CssmData(data, length);
533
534 // unwrap it into the CSP (but keep it raw)
535 UnwrapKey unwrap(Server::csp(), CSSM_ALGID_NONE);
536 CssmKey unwrappedKey;
537 CssmData descriptiveData;
538 unwrap(key,
539 KeySpec(CSSM_KEYUSE_ANY, CSSM_KEYATTR_RETURN_DATA | CSSM_KEYATTR_EXTRACTABLE),
540 unwrappedKey, &descriptiveData, NULL);
541 return CssmClient::Key(Server::csp(), unwrappedKey);
542 }