2 * Copyright (c) 2003-2004,2011-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
28 #include "pkcs12Utils.h"
30 #include "pkcs7Templates.h"
31 #include "pkcs12Templates.h"
32 #include "pkcs12Crypto.h"
33 #include "pkcs12Debug.h"
34 #include <security_asn1/nssUtils.h>
35 #include <Security/secasn1t.h>
36 #include <security_utilities/errors.h>
37 #include <security_cdsa_utils/cuCdsaUtils.h>
38 #include <Security/oidsattr.h>
39 #include <Security/oidsalg.h>
40 #include <Security/cssmapple.h>
42 /* malloc a NULL-ed array of pointers of size num+1 */
43 void **p12NssNullArray(
47 unsigned len
= (num
+ 1) * sizeof(void *);
48 void **p
= (void **)coder
.malloc(len
);
53 /* CSSM_DATA --> uint32. Returns true if OK. */
55 const CSSM_DATA
&cdata
,
58 if((cdata
.Length
== 0) || (cdata
.Data
== NULL
)) {
59 /* default/not present */
63 CSSM_SIZE len
= cdata
.Length
;
64 if(len
> sizeof(uint32
)) {
69 uint8
*cp
= cdata
.Data
;
70 for(uint32 i
=0; i
<len
; i
++) {
71 rtn
= (rtn
<< 8) | *cp
++;
77 /* uint32 --> CSSM_DATA */
88 else if(num
< 0x10000) {
91 else if(num
< 0x1000000) {
97 coder
.allocItem(cdata
, len
);
98 uint8
*cp
= &cdata
.Data
[len
- 1];
99 for(unsigned i
=0; i
<len
; i
++) {
105 /* CFDataRef <--> CSSM_DATA */
106 CFDataRef CF_RETURNS_RETAINED
p12CssmDataToCf(
109 return CFDataCreate(NULL
, c
.Data
, c
.Length
);
112 void p12CfDataToCssm(
117 coder
.allocCopyItem(CFDataGetBytePtr(cf
),
118 CFDataGetLength(cf
), c
);
122 * Attempt to convert a CFStringRef, which represents a SafeBag's
123 * FriendlyName, to a UTF8-encoded CSSM_DATA. The CSSM_DATA and its
124 * referent are allocated in the specified SecNssCoder's memory.
125 * No guarantee that this conversion works. If it doesn't we return
126 * NULL and caller must be prepared to deal with that.
128 CSSM_DATA_PTR
p12StringToUtf8(
137 CFRange range
= { 0, CFStringGetLength(cfStr
) };
138 CFStringGetBytes(cfStr
, range
, kCFStringEncodingUTF8
, 0, FALSE
, NULL
, 0, &strLen
);
143 CSSM_DATA_PTR rtn
= coder
.mallocn
<CSSM_DATA
>();
144 coder
.allocItem(*rtn
, strLen
);
146 if(!CFStringGetBytes(cfStr
, range
, kCFStringEncodingUTF8
, 0, FALSE
, (UInt8
*)rtn
->Data
, strLen
, &strLen
)) {
147 /* not convertible from native Unicode to UTF8 */
154 * Enum to string mappper.
158 * Each type of attribute has a name/value pair in a table of these:
165 /* declare one entry in a table of p12NameValuePair */
166 #define NVP(attr) {attr, #attr}
168 /* the NULL entry which terminates all p12NameValuePair tables */
169 #define NVP_END {0, NULL}
171 static const p12NameValuePair p7CITypeNames
[] =
177 NVP(CT_SignedEnvData
),
179 NVP(CT_EncryptedData
),
183 static const p12NameValuePair p12BagTypeNames
[] =
187 NVP(BT_ShroudedKeyBag
),
191 NVP(BT_SafeContentsBag
),
195 static const char *typeToStr(
197 const p12NameValuePair
*table
)
200 if(table
->value
== type
) {
208 const char *p12BagTypeStr(
209 NSS_P12_SB_Type type
)
211 return typeToStr(type
, p12BagTypeNames
);
214 const char *p7ContentInfoTypeStr(
217 return typeToStr(type
, p7CITypeNames
);
221 * OIDS for P12 and PKCS5 v1.5 (PBES1) encrypt and decrypt map to the following
226 CSSM_ALGORITHMS keyAlg
; // e.g., CSSM_ALGID_DES
227 CSSM_ALGORITHMS encrAlg
; // e.g., CSSM_ALGID_3DES_3KEY_EDE
228 CSSM_ALGORITHMS pbeHashAlg
; // SHA1 or MD5
229 uint32 keySizeInBits
;
230 uint32 blockSizeInBytes
; // for IV, optional
231 CSSM_PADDING padding
; // CSSM_PADDING_PKCS7, etc.
232 CSSM_ENCRYPT_MODE mode
; // CSSM_ALGMODE_CBCPadIV8, etc.
233 PKCS_Which pkcs
; // PW_PKCS12 (for this module) or PW_PKCS5_v1_5
236 static const PKCSOidInfo pkcsOidInfos
[] = {
237 /* PKCS12 first, the ones this module uses */
239 &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC4
,
244 0, // RC4 is a stream cipher
250 &CSSMOID_PKCS12_pbeWithSHAAnd40BitRC4
,
255 0, // RC4 is a stream cipher
261 &CSSMOID_PKCS12_pbeWithSHAAnd3Key3DESCBC
,
262 CSSM_ALGID_3DES_3KEY
,
263 CSSM_ALGID_3DES_3KEY_EDE
,
268 CSSM_ALGMODE_CBCPadIV8
,
272 &CSSMOID_PKCS12_pbeWithSHAAnd2Key3DESCBC
,
273 CSSM_ALGID_3DES_2KEY
,
274 CSSM_ALGID_3DES_2KEY_EDE
,
279 CSSM_ALGMODE_CBCPadIV8
,
283 &CSSMOID_PKCS12_pbeWithSHAAnd128BitRC2CBC
,
290 CSSM_ALGMODE_CBCPadIV8
,
294 &CSSMOID_PKCS12_pbewithSHAAnd40BitRC2CBC
,
301 CSSM_ALGMODE_CBCPadIV8
,
305 /* PKCS5 v1.5, used for SecImportExport module */
307 &CSSMOID_PKCS5_pbeWithMD2AndDES
,
314 CSSM_ALGMODE_CBCPadIV8
,
318 &CSSMOID_PKCS5_pbeWithMD2AndRC2
,
325 CSSM_ALGMODE_CBCPadIV8
,
329 &CSSMOID_PKCS5_pbeWithMD5AndDES
,
336 CSSM_ALGMODE_CBCPadIV8
,
340 &CSSMOID_PKCS5_pbeWithMD5AndRC2
,
347 CSSM_ALGMODE_CBCPadIV8
,
351 &CSSMOID_PKCS5_pbeWithSHA1AndDES
,
358 CSSM_ALGMODE_CBCPadIV8
,
362 &CSSMOID_PKCS5_pbeWithSHA1AndRC2
,
369 CSSM_ALGMODE_CBCPadIV8
,
373 /* finally one for PKCS5 v2.0, which has its own means of
374 * cooking up all the parameters */
376 &CSSMOID_PKCS5_PBES2
,
385 #define NUM_PKCS_OID_INFOS (sizeof(pkcsOidInfos) / sizeof(pkcsOidInfos[1]))
387 /* map an OID to the components */
388 /* returns false if OID not found */
391 * NOTE: as of March 8 2004 this is also used by the SecImportExport
392 * module...not just PKCS12!
394 bool pkcsOidToParams(
396 CSSM_ALGORITHMS
&keyAlg
, // e.g., CSSM_ALGID_DES
397 CSSM_ALGORITHMS
&encrAlg
, // e.g., CSSM_ALGID_3DES_3KEY_EDE
398 CSSM_ALGORITHMS
&pbeHashAlg
, // SHA1 or MD5
399 uint32
&keySizeInBits
,
400 uint32
&blockSizeInBytes
, // for IV, optional
401 CSSM_PADDING
&padding
, // CSSM_PADDING_PKCS7, etc.
402 CSSM_ENCRYPT_MODE
&mode
, // CSSM_ALGMODE_CBCPadIV8, etc.
403 PKCS_Which
&pkcs
) // PW_PKCS5_v1_5 or PW_PKCS12
405 const PKCSOidInfo
*info
= pkcsOidInfos
;
408 for(unsigned dex
=0; dex
<NUM_PKCS_OID_INFOS
; dex
++) {
409 if(nssCompareCssmData(oid
, info
->oid
)) {
410 keyAlg
= info
->keyAlg
;
411 encrAlg
= info
->encrAlg
;
412 pbeHashAlg
= info
->pbeHashAlg
;
413 keySizeInBits
= info
->keySizeInBits
;
414 blockSizeInBytes
= info
->blockSizeInBytes
;
415 padding
= info
->padding
;
426 * Verify MAC on an existing PFX.
428 CSSM_RETURN
p12VerifyMac(
429 const NSS_P12_DecodedPFX
&pfx
,
430 CSSM_CSP_HANDLE cspHand
,
431 const CSSM_DATA
*pwd
, // unicode, double null terminated
432 const CSSM_KEY
*passKey
,
433 SecNssCoder
&coder
) // for temp mallocs
435 if(pfx
.macData
== NULL
) {
436 return CSSMERR_CSP_INVALID_SIGNATURE
;
438 NSS_P12_MacData
&macData
= *pfx
.macData
;
439 NSS_P7_DigestInfo
&digestInfo
= macData
.mac
;
440 CSSM_OID
&algOid
= digestInfo
.digestAlgorithm
.algorithm
;
441 CSSM_ALGORITHMS macAlg
;
442 if(!cssmOidToAlg(&algOid
, &macAlg
)) {
443 return CSSMERR_CSP_INVALID_ALGORITHM
;
445 uint32 iterCount
= 0;
446 CSSM_DATA
&citer
= macData
.iterations
;
447 if(!p12DataToInt(citer
, iterCount
)) {
448 return CSSMERR_CSP_INVALID_ATTR_ROUNDS
;
451 /* optional, default 1 */
456 * In classic fashion, the PKCS12 spec now says:
458 * When password integrity mode is used to secure a PFX PDU,
459 * an SHA-1 HMAC is computed on the BER-encoding of the contents
460 * of the content field of the authSafe field in the PFX PDU.
465 CSSM_RETURN crtn
= p12GenMac(cspHand
, *pfx
.authSafe
.content
.data
,
466 macAlg
, iterCount
, macData
.macSalt
, pwd
, passKey
, coder
, genMac
);
470 if(nssCompareCssmData(&genMac
, &digestInfo
.digest
)) {
474 return CSSMERR_CSP_VERIFY_FAILED
;
478 /* we generate 8 random bytes of salt */
479 #define P12_SALT_LEN 8
485 coder
.allocItem(salt
, P12_SALT_LEN
);
486 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault
, P12_SALT_LEN
, salt
.Data
));
490 * Generate random label string to allow associating an imported private
497 /* first a random uint32 */
499 MacOSError::check(SecRandomCopyBytes(kSecRandomDefault
, 4, d
));
500 CSSM_DATA cd
= {4, d
};
504 /* sprintf that into a real string */
505 coder
.allocItem(label
, 9);
506 memset(label
.Data
, 0, 9);
507 sprintf((char *)label
.Data
, "%08X", (unsigned)i
);
510 /* NULL algorithm parameters */
512 static const uint8 nullAlg
[2] = {SEC_ASN1_NULL
, 0};
514 void p12NullAlgParams(
515 CSSM_X509_ALGORITHM_IDENTIFIER
&algId
)
517 CSSM_DATA
&p
= algId
.parameters
;
518 p
.Data
= (uint8
*)nullAlg
;
523 * Free memory via specified plugin's app-level allocator
529 CSSM_API_MEMORY_FUNCS memFuncs
;
530 CSSM_RETURN crtn
= CSSM_GetAPIMemoryFunctions(hand
, &memFuncs
);
532 p12LogCssmError("CSSM_GetAPIMemoryFunctions", crtn
);
533 /* oh well, leak and continue */
536 memFuncs
.free_func(p
, memFuncs
.AllocRef
);
540 * Find private key by label, modify its Label attr to be the
541 * hash of the associated public key.
542 * Also optionally re-sets the key's PrintName attribute; used to reset
543 * this attr from the random label we create when first unwrap it
544 * to the friendly name we find later after parsing attributes.
545 * Detection of a duplicate key when updating the key's attributes
546 * results in a lookup of the original key and returning it in
549 CSSM_RETURN
p12SetPubKeyHash(
550 CSSM_CSP_HANDLE cspHand
, // where the key lives
551 CSSM_DL_DB_HANDLE dlDbHand
, // ditto
552 CSSM_DATA
&keyLabel
, // for DB lookup
553 CSSM_DATA_PTR newPrintName
, // optional
554 SecNssCoder
&coder
, // for mallocing newLabel
555 CSSM_DATA
&newLabel
, // RETURNED with label as hash
556 CSSM_KEY_PTR
&foundKey
) // RETURNED
559 CSSM_SELECTION_PREDICATE predicate
;
560 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
562 CSSM_HANDLE resultHand
= 0;
563 CSSM_DATA keyData
= {0, NULL
};
564 CSSM_CC_HANDLE ccHand
= 0;
565 CSSM_KEY_PTR privKey
= NULL
;
566 CSSM_DATA_PTR keyDigest
= NULL
;
568 assert(cspHand
!= 0);
569 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
570 query
.Conjunctive
= CSSM_DB_NONE
;
571 query
.NumSelectionPredicates
= 1;
572 predicate
.DbOperator
= CSSM_DB_EQUAL
;
574 predicate
.Attribute
.Info
.AttributeNameFormat
=
575 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
576 predicate
.Attribute
.Info
.Label
.AttributeName
=
577 (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
578 predicate
.Attribute
.Info
.AttributeFormat
=
579 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
580 /* hope this cast is OK */
581 predicate
.Attribute
.Value
= &keyLabel
;
582 query
.SelectionPredicate
= &predicate
;
584 query
.QueryLimits
.TimeLimit
= 0; // FIXME - meaningful?
585 query
.QueryLimits
.SizeLimit
= 1; // FIXME - meaningful?
586 query
.QueryFlags
= CSSM_QUERY_RETURN_DATA
;
588 /* build Record attribute with one or two attrs */
589 CSSM_DB_RECORD_ATTRIBUTE_DATA recordAttrs
;
590 CSSM_DB_ATTRIBUTE_DATA attr
[2];
591 attr
[0].Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
592 attr
[0].Info
.Label
.AttributeName
= (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
593 attr
[0].Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
595 attr
[1].Info
.AttributeNameFormat
= CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
596 attr
[1].Info
.Label
.AttributeName
= (char*) P12_KEY_ATTR_PRINT_NAME
;
597 attr
[1].Info
.AttributeFormat
= CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
599 recordAttrs
.DataRecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
600 recordAttrs
.NumberOfAttributes
= newPrintName
? 2 : 1;
601 recordAttrs
.AttributeData
= attr
;
603 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
609 /* abort only on success */
610 if(crtn
!= CSSM_OK
) {
611 p12LogCssmError("CSSM_DL_DataGetFirst", crtn
);
612 p12ErrorLog("***p12SetPubKeyHash: can't find private key\n");
615 /* subsequent errors to errOut: */
616 if(keyData
.Data
== NULL
) {
617 p12ErrorLog("***p12SetPubKeyHash: private key lookup failure\n");
618 crtn
= CSSMERR_CSSM_INTERNAL_ERROR
;
621 privKey
= (CSSM_KEY_PTR
)keyData
.Data
;
623 /* public key hash via passthrough - works on any key, any CSP/CSPDL.... */
625 * Warning! This relies on the current default ACL meaning "allow this
626 * current app to access this private key" since we created the key.
628 crtn
= CSSM_CSP_CreatePassThroughContext(cspHand
, privKey
, &ccHand
);
630 p12LogCssmError("CSSM_CSP_CreatePassThroughContext", crtn
);
633 crtn
= CSSM_CSP_PassThrough(ccHand
,
634 CSSM_APPLECSP_KEYDIGEST
,
636 (void **)&keyDigest
);
638 p12LogCssmError("CSSM_CSP_PassThrough", crtn
);
643 * Replace Label attr data with hash.
644 * NOTE: the module which allocated this attribute data - a DL -
645 * was loaded and attached by out client layer, not by us. Thus
646 * we can't use the memory allocator functions *we* used when
647 * attaching to the CSP - we have to use the ones
648 * which the client registered with the DL.
650 freeCssmMemory(dlDbHand
.DLHandle
, attr
[0].Value
->Data
);
651 freeCssmMemory(dlDbHand
.DLHandle
, attr
[0].Value
);
653 freeCssmMemory(dlDbHand
.DLHandle
, attr
[1].Value
->Data
);
654 freeCssmMemory(dlDbHand
.DLHandle
, attr
[1].Value
);
656 /* modify key attributes */
657 attr
[0].Value
= keyDigest
;
659 attr
[1].Value
= newPrintName
;
661 crtn
= CSSM_DL_DataModify(dlDbHand
,
662 CSSM_DL_DB_RECORD_PRIVATE_KEY
,
665 NULL
, // DataToBeModified
666 CSSM_DB_MODIFY_ATTRIBUTE_REPLACE
);
669 /* give caller the key's new label */
670 coder
.allocCopyItem(*keyDigest
, newLabel
);
673 p12LogCssmError("CSSM_DL_DataModify", crtn
);
675 case CSSMERR_DL_INVALID_UNIQUE_INDEX_DATA
:
678 * Special case: dup private key. The label we just tried to modify is
679 * the public key hash so we can be confident that this really is a dup.
680 * Delete it, look up the original, and return the original to caller.
682 CSSM_RETURN drtn
= CSSM_DL_DataDelete(dlDbHand
, record
);
684 p12LogCssmError("CSSM_DL_DataDelete on dup key", drtn
);
689 /* Free items created in last search */
690 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
692 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
695 /* lookup by label as public key hash this time */
696 predicate
.Attribute
.Value
= keyDigest
;
697 drtn
= CSSM_DL_DataGetFirst(dlDbHand
,
700 NULL
, // no attrs this time
704 p12LogCssmError("CSSM_DL_DataGetFirst on original key", crtn
);
708 foundKey
= (CSSM_KEY_PTR
)keyData
.Data
;
709 /* give caller the key's actual label */
710 coder
.allocCopyItem(*keyDigest
, newLabel
);
718 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
721 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
724 CSSM_DeleteContext(ccHand
);
727 /* key created by the CSPDL */
728 CSSM_FreeKey(cspHand
, NULL
, privKey
, CSSM_FALSE
);
729 freeCssmMemory(dlDbHand
.DLHandle
, privKey
);
732 /* mallocd by someone else's CSP */
733 freeCssmMemory(cspHand
, keyDigest
->Data
);
734 freeCssmMemory(cspHand
, keyDigest
);
740 * Given a context specified via a CSSM_CC_HANDLE, add a new
741 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
742 * AttributeLength, and an untyped pointer.
744 CSSM_RETURN
p12AddContextAttribute(CSSM_CC_HANDLE CCHandle
,
745 uint32 AttributeType
,
746 uint32 AttributeLength
,
747 const void *AttributePtr
)
749 CSSM_CONTEXT_ATTRIBUTE newAttr
;
752 newAttr
.AttributeType
= AttributeType
;
753 newAttr
.AttributeLength
= AttributeLength
;
754 newAttr
.Attribute
.Data
= (CSSM_DATA_PTR
)AttributePtr
;
755 crtn
= CSSM_UpdateContextAttributes(CCHandle
, 1, &newAttr
);
757 p12LogCssmError("CSSM_UpdateContextAttributes", crtn
);
763 * Find private key by specified label, delete it.
765 CSSM_RETURN
p12DeleteKey(
766 CSSM_DL_DB_HANDLE dlDbHand
,
767 const CSSM_DATA
&keyLabel
)
770 CSSM_SELECTION_PREDICATE predicate
;
771 CSSM_DB_UNIQUE_RECORD_PTR record
= NULL
;
773 CSSM_HANDLE resultHand
= 0;
775 query
.RecordType
= CSSM_DL_DB_RECORD_PRIVATE_KEY
;
776 query
.Conjunctive
= CSSM_DB_NONE
;
777 query
.NumSelectionPredicates
= 1;
778 predicate
.DbOperator
= CSSM_DB_EQUAL
;
780 predicate
.Attribute
.Info
.AttributeNameFormat
=
781 CSSM_DB_ATTRIBUTE_NAME_AS_STRING
;
782 predicate
.Attribute
.Info
.Label
.AttributeName
=
783 (char*) P12_KEY_ATTR_LABEL_AND_HASH
;
784 predicate
.Attribute
.Info
.AttributeFormat
=
785 CSSM_DB_ATTRIBUTE_FORMAT_BLOB
;
786 predicate
.Attribute
.Value
= const_cast<CSSM_DATA_PTR
>(&keyLabel
);
788 query
.SelectionPredicate
= &predicate
;
789 query
.QueryLimits
.TimeLimit
= 0;
790 query
.QueryLimits
.SizeLimit
= 1;
791 query
.QueryFlags
= 0;
793 crtn
= CSSM_DL_DataGetFirst(dlDbHand
,
796 NULL
, // attrs - don't need 'em
797 NULL
, // theData - don't need it
799 /* abort only on success */
801 p12LogCssmError("CSSM_DL_DataGetFirst", crtn
);
802 p12ErrorLog("***p12DeleteKey: can't find private key\n");
806 crtn
= CSSM_DL_DataDelete(dlDbHand
, record
);
808 p12LogCssmError("CSSM_DL_DataDelete", crtn
);
809 p12ErrorLog("***p12DeleteKey: can't delete private key\n");
812 CSSM_DL_DataAbortQuery(dlDbHand
, resultHand
);
813 CSSM_DL_FreeUniqueRecord(dlDbHand
, record
);
817 /* convert App passphrase to array of chars used in P12 PBE */
818 void p12ImportPassPhrase(
819 CFStringRef inPhrase
,
821 CSSM_DATA
&outPhrase
)
823 CFDataRef cfData
= CFStringCreateExternalRepresentation(NULL
,
824 inPhrase
, kCFStringEncodingUTF8
, 0);
826 p12ErrorLog("***p12ImportPassPhrase: can't convert passphrase to UTF8\n");
827 MacOSError::throwMe(errSecParam
);
829 CFIndex keyLen
= CFDataGetLength(cfData
);
830 coder
.allocItem(outPhrase
, keyLen
);
831 memmove(outPhrase
.Data
, CFDataGetBytePtr(cfData
), keyLen
);