2 * Copyright (c) 2006-2015 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@
25 * SecKey.c - CoreFoundation based key object
29 #include <Security/SecKeyInternal.h>
30 #include <Security/SecItem.h>
31 #include <Security/SecItemPriv.h>
32 #include <Security/SecFramework.h>
34 #include <utilities/SecIOFormat.h>
36 #include <utilities/SecCFWrappers.h>
38 #include "SecRSAKeyPriv.h"
39 #include "SecECKeyPriv.h"
40 #include "SecCTKKeyPriv.h"
41 #include "SecBasePriv.h"
43 #include <CoreFoundation/CFNumber.h>
44 #include <CoreFoundation/CFString.h>
45 #include <Security/SecBase.h>
48 #include <AssertMacros.h>
49 #include <utilities/debugging.h>
50 #include <utilities/SecCFError.h>
51 #include <CommonCrypto/CommonDigest.h>
52 #include <Security/SecAsn1Coder.h>
53 #include <Security/oidsalg.h>
54 #include <Security/SecInternal.h>
55 #include <Security/SecRandom.h>
56 #include <corecrypto/ccrng_system.h>
61 #include <libDER/asn1Types.h>
62 #include <libDER/DER_Keys.h>
63 #include <libDER/DER_Encode.h>
66 /* Static functions. */
67 #define MAX_DIGEST_LEN (CC_SHA512_DIGEST_LENGTH)
69 /* Currently length of SHA512 oid + 1 */
70 #define MAX_OID_LEN (10)
72 #define DER_MAX_DIGEST_INFO_LEN (10 + MAX_DIGEST_LEN + MAX_OID_LEN)
74 /* Encode the digestInfo header into digestInfo and return the offset from
75 digestInfo at which to put the actual digest. Returns 0 if digestInfo
76 won't fit within digestInfoLength bytes.
80 0x06, oid.Len, oid.Data,
86 static size_t DEREncodeDigestInfoPrefix(const SecAsn1Oid
*oid
,
87 size_t digestLength
, uint8_t *digestInfo
, size_t digestInfoLength
) {
88 size_t algIdLen
= oid
->Length
+ 4;
89 size_t topLen
= algIdLen
+ digestLength
+ 4;
90 size_t totalLen
= topLen
+ 2;
92 if (totalLen
> digestInfoLength
) {
97 digestInfo
[ix
++] = (SEC_ASN1_SEQUENCE
| SEC_ASN1_CONSTRUCTED
);
98 digestInfo
[ix
++] = topLen
;
99 digestInfo
[ix
++] = (SEC_ASN1_SEQUENCE
| SEC_ASN1_CONSTRUCTED
);
100 digestInfo
[ix
++] = algIdLen
;
101 digestInfo
[ix
++] = SEC_ASN1_OBJECT_ID
;
102 digestInfo
[ix
++] = oid
->Length
;
103 memcpy(&digestInfo
[ix
], oid
->Data
, oid
->Length
);
105 digestInfo
[ix
++] = SEC_ASN1_NULL
;
106 digestInfo
[ix
++] = 0;
107 digestInfo
[ix
++] = SEC_ASN1_OCTET_STRING
;
108 digestInfo
[ix
++] = digestLength
;
113 CFDataRef
SecKeyCopyPublicKeyHash(SecKeyRef key
)
115 CFDataRef pubKeyDigest
= NULL
, pubKeyBlob
= NULL
;
117 /* encode the public key. */
118 require_noerr(SecKeyCopyPublicBytes(key
, &pubKeyBlob
), errOut
);
119 require(pubKeyBlob
, errOut
);
121 /* Calculate the digest of the public key. */
122 require(pubKeyDigest
= SecSHA1DigestCreate(CFGetAllocator(key
),
123 CFDataGetBytePtr(pubKeyBlob
), CFDataGetLength(pubKeyBlob
)),
126 CFReleaseNull(pubKeyBlob
);
133 static CFDictionaryRef
SecKeyCopyAttributeDictionaryWithLocalKey(SecKeyRef key
,
135 CFDataRef privateBlob
)
137 CFAllocatorRef allocator
= CFGetAllocator(key
);
139 CFDataRef pubKeyDigest
= NULL
, pubKeyBlob
= NULL
;
140 CFDictionaryRef dict
= NULL
;
142 size_t sizeValue
= SecKeyGetSize(key
, kSecKeyKeySizeInBits
);
143 CFNumberRef sizeInBits
= CFNumberCreate(allocator
, kCFNumberLongType
, &sizeValue
);
145 /* encode the public key. */
146 require_noerr(SecKeyCopyPublicBytes(key
, &pubKeyBlob
), errOut
);
147 require(pubKeyBlob
, errOut
);
149 /* Calculate the digest of the public key. */
150 require(pubKeyDigest
= SecSHA1DigestCreate(allocator
,
151 CFDataGetBytePtr(pubKeyBlob
), CFDataGetLength(pubKeyBlob
)),
154 DICT_ADDPAIR(kSecClass
, kSecClassKey
);
155 DICT_ADDPAIR(kSecAttrKeyClass
, privateBlob
? kSecAttrKeyClassPrivate
: kSecAttrKeyClassPublic
);
156 DICT_ADDPAIR(kSecAttrApplicationLabel
, pubKeyDigest
);
157 DICT_ADDPAIR(kSecAttrIsPermanent
, kCFBooleanTrue
);
158 DICT_ADDPAIR(kSecAttrIsPrivate
, kCFBooleanTrue
);
159 DICT_ADDPAIR(kSecAttrIsModifiable
, kCFBooleanTrue
);
160 DICT_ADDPAIR(kSecAttrKeyType
, keyType
);
161 DICT_ADDPAIR(kSecAttrKeySizeInBits
, sizeInBits
);
162 DICT_ADDPAIR(kSecAttrEffectiveKeySize
, sizeInBits
);
163 DICT_ADDPAIR(kSecAttrIsSensitive
, kCFBooleanFalse
);
164 DICT_ADDPAIR(kSecAttrWasAlwaysSensitive
, kCFBooleanFalse
);
165 DICT_ADDPAIR(kSecAttrIsExtractable
, kCFBooleanTrue
);
166 DICT_ADDPAIR(kSecAttrWasNeverExtractable
, kCFBooleanFalse
);
167 DICT_ADDPAIR(kSecAttrCanEncrypt
, kCFBooleanFalse
);
168 DICT_ADDPAIR(kSecAttrCanDecrypt
, kCFBooleanTrue
);
169 DICT_ADDPAIR(kSecAttrCanDerive
, kCFBooleanTrue
);
170 DICT_ADDPAIR(kSecAttrCanSign
, kCFBooleanTrue
);
171 DICT_ADDPAIR(kSecAttrCanVerify
, kCFBooleanFalse
);
172 DICT_ADDPAIR(kSecAttrCanSignRecover
, kCFBooleanFalse
);
173 DICT_ADDPAIR(kSecAttrCanVerifyRecover
, kCFBooleanFalse
);
174 DICT_ADDPAIR(kSecAttrCanWrap
, kCFBooleanFalse
);
175 DICT_ADDPAIR(kSecAttrCanUnwrap
, kCFBooleanTrue
);
176 DICT_ADDPAIR(kSecValueData
, privateBlob
? privateBlob
: pubKeyBlob
);
177 dict
= DICT_CREATE(allocator
);
180 // @@@ Zero out key material.
181 CFReleaseSafe(pubKeyDigest
);
182 CFReleaseSafe(pubKeyBlob
);
183 CFReleaseSafe(sizeInBits
);
188 CFDictionaryRef
SecKeyGeneratePrivateAttributeDictionary(SecKeyRef key
,
190 CFDataRef privateBlob
)
192 return SecKeyCopyAttributeDictionaryWithLocalKey(key
, keyType
, privateBlob
);
195 CFDictionaryRef
SecKeyGeneratePublicAttributeDictionary(SecKeyRef key
, CFTypeRef keyType
)
197 return SecKeyCopyAttributeDictionaryWithLocalKey(key
, keyType
, NULL
);
200 static CFStringRef
SecKeyCopyDescription(CFTypeRef cf
) {
201 SecKeyRef key
= (SecKeyRef
)cf
;
203 if(key
->key_class
->describe
)
204 return key
->key_class
->describe(key
);
206 return CFStringCreateWithFormat(kCFAllocatorDefault
,NULL
,CFSTR("<SecKeyRef: %p>"), key
);
209 static void SecKeyDestroy(CFTypeRef cf
) {
210 SecKeyRef key
= (SecKeyRef
)cf
;
211 if (key
->key_class
->destroy
)
212 key
->key_class
->destroy(key
);
215 static Boolean
SecKeyEqual(CFTypeRef cf1
, CFTypeRef cf2
)
217 SecKeyRef key1
= (SecKeyRef
)cf1
;
218 SecKeyRef key2
= (SecKeyRef
)cf2
;
221 if (!key2
|| key1
->key_class
!= key2
->key_class
)
223 if (key1
->key_class
->extraBytes
)
224 return !memcmp(key1
->key
, key2
->key
, key1
->key_class
->extraBytes
);
226 /* TODO: Won't work when we get reference keys. */
227 CFDictionaryRef d1
, d2
;
228 d1
= SecKeyCopyAttributeDictionary(key1
);
229 d2
= SecKeyCopyAttributeDictionary(key2
);
230 Boolean result
= CFEqual(d1
, d2
);
236 struct ccrng_state
*ccrng_seckey
;
238 CFGiblisWithFunctions(SecKey
, NULL
, NULL
, SecKeyDestroy
, SecKeyEqual
, NULL
, NULL
, SecKeyCopyDescription
, NULL
, NULL
, ^{
239 static struct ccrng_system_state ccrng_system_state_seckey
;
240 ccrng_seckey
= (struct ccrng_state
*)&ccrng_system_state_seckey
;
241 ccrng_system_init(&ccrng_system_state_seckey
);
244 static bool getBoolForKey(CFDictionaryRef dict
, CFStringRef key
, bool default_value
) {
245 CFTypeRef value
= CFDictionaryGetValue(dict
, key
);
247 if (CFGetTypeID(value
) == CFBooleanGetTypeID()) {
248 return CFBooleanGetValue(value
);
250 secwarning("Value %@ for key %@ is not bool", value
, key
);
254 return default_value
;
257 static OSStatus
add_ref(CFTypeRef item
, CFMutableDictionaryRef dict
) {
258 CFDictionarySetValue(dict
, kSecValueRef
, item
);
259 return SecItemAdd(dict
, NULL
);
262 static void merge_params_applier(const void *key
, const void *value
,
264 CFMutableDictionaryRef result
= (CFMutableDictionaryRef
)context
;
265 CFDictionaryAddValue(result
, key
, value
);
268 /* Create a mutable dictionary that is based on the subdictionary for key
269 with any attributes from the top level dict merged in. */
270 static CF_RETURNS_RETAINED CFMutableDictionaryRef
merge_params(CFDictionaryRef dict
,
272 CFDictionaryRef subdict
= CFDictionaryGetValue(dict
, key
);
273 CFMutableDictionaryRef result
;
276 result
= CFDictionaryCreateMutableCopy(NULL
, 0, subdict
);
277 /* Add everything in dict not already in result to result. */
278 CFDictionaryApplyFunction(dict
, merge_params_applier
, result
);
280 result
= CFDictionaryCreateMutableCopy(NULL
, 0, dict
);
283 /* Remove values that only belong in the top level dict. */
284 CFDictionaryRemoveValue(result
, kSecPublicKeyAttrs
);
285 CFDictionaryRemoveValue(result
, kSecPrivateKeyAttrs
);
286 CFDictionaryRemoveValue(result
, kSecAttrKeyType
);
287 CFDictionaryRemoveValue(result
, kSecAttrKeySizeInBits
);
292 CFIndex
SecKeyGetAlgorithmIdentifier(SecKeyRef key
) {
293 if (!key
|| !key
->key_class
) {
294 // TBD: somehow, a key can be created with a NULL key_class in the
295 // SecCertificateCopyPublicKey -> SecKeyCreatePublicFromDER code path
296 return kSecNullAlgorithmID
;
298 /* This method was added to version 1 keys. */
299 if (key
->key_class
->version
> 0 && key
->key_class
->getAlgorithmID
) {
300 return key
->key_class
->getAlgorithmID(key
);
302 /* All version 0 key were RSA. */
303 return kSecRSAAlgorithmID
;
306 /* Generate a private/public keypair. */
307 OSStatus
SecKeyGeneratePair(CFDictionaryRef parameters
,
308 SecKeyRef
*publicKey
, SecKeyRef
*privateKey
) {
309 OSStatus result
= errSecUnsupportedAlgorithm
;
310 SecKeyRef privKey
= NULL
;
311 SecKeyRef pubKey
= NULL
;
312 CFMutableDictionaryRef pubParams
= merge_params(parameters
, kSecPublicKeyAttrs
),
313 privParams
= merge_params(parameters
, kSecPrivateKeyAttrs
);
314 CFStringRef ktype
= CFDictionaryGetValue(parameters
, kSecAttrKeyType
);
315 CFStringRef tokenID
= CFDictionaryGetValue(parameters
, kSecAttrTokenID
);
317 require(ktype
, errOut
);
319 if (tokenID
!= NULL
) {
320 result
= SecCTKKeyGeneratePair(parameters
, &pubKey
, &privKey
);
321 } else if (CFEqual(ktype
, kSecAttrKeyTypeEC
)) {
322 result
= SecECKeyGeneratePair(parameters
, &pubKey
, &privKey
);
323 } else if (CFEqual(ktype
, kSecAttrKeyTypeRSA
)) {
324 result
= SecRSAKeyGeneratePair(parameters
, &pubKey
, &privKey
);
327 require_noerr(result
, errOut
);
329 /* Store the keys in the keychain if they are marked as permanent. */
330 if (getBoolForKey(pubParams
, kSecAttrIsPermanent
, false)) {
331 require_noerr_quiet(result
= add_ref(pubKey
, pubParams
), errOut
);
333 /* Token-based private keys are automatically stored on the token. */
334 if (tokenID
== NULL
&& getBoolForKey(privParams
, kSecAttrIsPermanent
, false)) {
335 require_noerr_quiet(result
= add_ref(privKey
, privParams
), errOut
);
343 *privateKey
= privKey
;
348 CFReleaseSafe(pubParams
);
349 CFReleaseSafe(privParams
);
350 CFReleaseSafe(pubKey
);
351 CFReleaseSafe(privKey
);
356 SecKeyRef
SecKeyCreatePublicFromPrivate(SecKeyRef privateKey
) {
357 CFDataRef serializedPublic
= NULL
;
358 SecKeyRef result
= NULL
;
360 require_noerr_quiet(SecKeyCopyPublicBytes(privateKey
, &serializedPublic
), fail
);
361 require_quiet(serializedPublic
, fail
);
363 result
= SecKeyCreateFromPublicData(kCFAllocatorDefault
, SecKeyGetAlgorithmIdentifier(privateKey
), serializedPublic
);
366 CFReleaseSafe(serializedPublic
);
371 CFDictionaryRef
CreatePrivateKeyMatchingQuery(SecKeyRef publicKey
, bool returnPersistentRef
)
373 const CFTypeRef refType
= (returnPersistentRef
) ? kSecReturnPersistentRef
: kSecReturnRef
;
375 CFDataRef public_key_hash
= SecKeyCopyPublicKeyHash(publicKey
);
377 CFDictionaryRef query
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
378 kSecClass
, kSecClassKey
,
379 kSecAttrKeyClass
, kSecAttrKeyClassPrivate
,
380 kSecAttrSynchronizable
, kSecAttrSynchronizableAny
,
381 kSecAttrApplicationLabel
, public_key_hash
,
382 refType
, kCFBooleanTrue
,
384 CFReleaseNull(public_key_hash
);
389 CFDataRef
SecKeyCreatePersistentRefToMatchingPrivateKey(SecKeyRef publicKey
, CFErrorRef
*error
) {
390 CFTypeRef persistentRef
= NULL
;
391 CFDictionaryRef query
= CreatePrivateKeyMatchingQuery(publicKey
, true);
393 require_quiet(SecError(SecItemCopyMatching(query
, &persistentRef
),error
,
394 CFSTR("Error finding persistent ref to key from public: %@"), publicKey
), fail
);
396 CFReleaseNull(query
);
397 return (CFDataRef
)persistentRef
;
400 SecKeyRef
SecKeyCopyMatchingPrivateKey(SecKeyRef publicKey
, CFErrorRef
*error
) {
401 SecKeyRef privateKey
= NULL
;
402 CFTypeRef queryResult
= NULL
;
403 CFDictionaryRef query
= NULL
;
405 require_action_quiet(publicKey
!= NULL
, errOut
, SecError(errSecParam
, error
, CFSTR("Null Public Key")));
407 query
= CreatePrivateKeyMatchingQuery(publicKey
, false);
409 require_quiet(SecError(SecItemCopyMatching(query
, &queryResult
), error
,
410 CFSTR("Error finding private key from public: %@"), publicKey
), errOut
);
412 if (CFGetTypeID(queryResult
) == SecKeyGetTypeID()) {
413 privateKey
= (SecKeyRef
) queryResult
;
418 CFReleaseNull(query
);
419 CFReleaseNull(queryResult
);
423 OSStatus
SecKeyGetMatchingPrivateKeyStatus(SecKeyRef publicKey
, CFErrorRef
*error
) {
424 OSStatus retval
= errSecParam
;
425 CFTypeRef private_key
= NULL
;
426 CFDictionaryRef query
= NULL
;
428 require_action_quiet(publicKey
!= NULL
, errOut
, SecError(errSecParam
, error
, NULL
, CFSTR("Null Public Key")));
430 query
= CreatePrivateKeyMatchingQuery(publicKey
, false);
432 retval
= SecItemCopyMatching(query
, &private_key
);
434 if (!retval
&& CFGetTypeID(private_key
) != SecKeyGetTypeID()) {
435 retval
= errSecInternalComponent
;
439 CFReleaseNull(query
);
440 CFReleaseNull(private_key
);
445 SecKeyRef
SecKeyCreatePublicFromDER(CFAllocatorRef allocator
,
446 const SecAsn1Oid
*oid
, const SecAsn1Item
*params
,
447 const SecAsn1Item
*keyData
) {
448 SecKeyRef publicKey
= NULL
;
449 if (SecAsn1OidCompare(oid
, &CSSMOID_RSA
)) {
451 publicKey
= SecKeyCreateRSAPublicKey(kCFAllocatorDefault
,
452 keyData
->Data
, keyData
->Length
, kSecKeyEncodingPkcs1
);
453 } else if (SecAsn1OidCompare(oid
, &CSSMOID_ecPublicKey
)) {
456 .oidLength
= oid
->Length
,
457 .key
= keyData
->Data
,
458 .keyLength
= keyData
->Length
,
461 derKey
.parameters
= params
->Data
;
462 derKey
.parametersLength
= params
->Length
;
464 publicKey
= SecKeyCreateECPublicKey(kCFAllocatorDefault
,
465 (const uint8_t *)&derKey
, sizeof(derKey
), kSecDERKeyEncoding
);
467 secwarning("Unsupported algorithm oid");
474 SecKeyRef
SecKeyCreateFromSubjectPublicKeyInfoData(CFAllocatorRef allocator
, CFDataRef subjectPublicKeyInfoData
)
478 DERItem subjectPublicKeyInfoDER
= {
479 .data
= (uint8_t *)CFDataGetBytePtr(subjectPublicKeyInfoData
),
480 .length
= (DERSize
)CFDataGetLength(subjectPublicKeyInfoData
),
482 DERSubjPubKeyInfo subjectPublicKeyInfo
;
483 DERAlgorithmId algorithmId
;
486 drtn
= DERParseSequence(&subjectPublicKeyInfoDER
,
487 DERNumSubjPubKeyInfoItemSpecs
, DERSubjPubKeyInfoItemSpecs
,
488 &subjectPublicKeyInfo
, sizeof(subjectPublicKeyInfo
));
490 require_noerr_quiet(drtn
, out
);
492 drtn
= DERParseSequenceContent(&subjectPublicKeyInfo
.algId
,
493 DERNumAlgorithmIdItemSpecs
, DERAlgorithmIdItemSpecs
,
494 &algorithmId
, sizeof(algorithmId
));
495 require_noerr_quiet(drtn
, out
);
498 drtn
= DERParseBitString(&subjectPublicKeyInfo
.pubKey
, &pubKeyBytes
, &unusedBits
);
499 require_noerr_quiet(drtn
, out
);
501 /* Convert DERItem to SecAsn1Item : */
502 const SecAsn1Oid oid
= { .Data
= algorithmId
.oid
.data
, .Length
= algorithmId
.oid
.length
};
503 const SecAsn1Item params
= { .Data
= algorithmId
.params
.data
, .Length
= algorithmId
.params
.length
};
504 const SecAsn1Item pubKey
= { .Data
= pubKeyBytes
.data
, .Length
= pubKeyBytes
.length
};
506 return SecKeyCreatePublicFromDER(allocator
, &oid
, ¶ms
, &pubKey
);
516 SecKeyRef
SecKeyCreate(CFAllocatorRef allocator
,
517 const SecKeyDescriptor
*key_class
, const uint8_t *keyData
,
518 CFIndex keyDataLength
, SecKeyEncoding encoding
) {
519 if (!key_class
) return NULL
;
520 size_t size
= sizeof(struct __SecKey
) + key_class
->extraBytes
;
521 SecKeyRef result
= (SecKeyRef
)_CFRuntimeCreateInstance(allocator
,
522 SecKeyGetTypeID(), size
- sizeof(CFRuntimeBase
), NULL
);
524 memset((char*)result
+ sizeof(result
->_base
), 0, size
- sizeof(result
->_base
));
525 result
->key_class
= key_class
;
526 if (key_class
->extraBytes
) {
527 /* Make result->key point to the extraBytes we allocated. */
528 result
->key
= ((char*)result
) + sizeof(*result
);
530 if (key_class
->init
) {
532 status
= key_class
->init(result
, keyData
, keyDataLength
, encoding
);
534 secwarning("init %s key: %" PRIdOSStatus
, key_class
->name
, status
);
544 kSecKeyDigestInfoSign
,
545 kSecKeyDigestInfoVerify
548 static OSStatus
SecKeyDigestInfoSignVerify(
549 SecKeyRef key
, /* Private key */
550 SecPadding padding
, /* kSecPaddingPKCS1@@@ */
551 const uint8_t *dataToSign
, /* signature over this data */
552 size_t dataToSignLen
, /* length of dataToSign */
553 uint8_t *sig
, /* signature, RETURNED */
554 size_t *sigLen
, /* IN/OUT */
556 size_t digestInfoLength
= DER_MAX_DIGEST_INFO_LEN
;
557 uint8_t digestInfo
[digestInfoLength
];
558 const SecAsn1Oid
*digestOid
;
563 case kSecPaddingPKCS1MD2
:
564 digestLen
= CC_MD2_DIGEST_LENGTH
;
565 digestOid
= &CSSMOID_MD2
;
567 case kSecPaddingPKCS1MD4
:
568 digestLen
= CC_MD4_DIGEST_LENGTH
;
569 digestOid
= &CSSMOID_MD4
;
571 case kSecPaddingPKCS1MD5
:
572 digestLen
= CC_MD5_DIGEST_LENGTH
;
573 digestOid
= &CSSMOID_MD5
;
576 case kSecPaddingPKCS1SHA1
:
577 digestLen
= CC_SHA1_DIGEST_LENGTH
;
578 digestOid
= &CSSMOID_SHA1
;
580 case kSecPaddingPKCS1SHA224
:
581 digestLen
= CC_SHA224_DIGEST_LENGTH
;
582 digestOid
= &CSSMOID_SHA224
;
584 case kSecPaddingPKCS1SHA256
:
585 digestLen
= CC_SHA256_DIGEST_LENGTH
;
586 digestOid
= &CSSMOID_SHA256
;
588 case kSecPaddingPKCS1SHA384
:
589 digestLen
= CC_SHA384_DIGEST_LENGTH
;
590 digestOid
= &CSSMOID_SHA384
;
592 case kSecPaddingPKCS1SHA512
:
593 digestLen
= CC_SHA512_DIGEST_LENGTH
;
594 digestOid
= &CSSMOID_SHA512
;
597 return errSecUnsupportedPadding
;
600 if (dataToSignLen
!= digestLen
)
603 size_t offset
= DEREncodeDigestInfoPrefix(digestOid
, digestLen
,
604 digestInfo
, digestInfoLength
);
606 return errSecBufferTooSmall
;
608 /* Append the digest to the digestInfo prefix and adjust the length. */
609 memcpy(&digestInfo
[offset
], dataToSign
, digestLen
);
610 digestInfoLength
= offset
+ digestLen
;
612 if (mode
== kSecKeyDigestInfoSign
) {
613 return key
->key_class
->rawSign(key
, kSecPaddingPKCS1
,
614 digestInfo
, digestInfoLength
, sig
, sigLen
);
616 return key
->key_class
->rawVerify(key
, kSecPaddingPKCS1
,
617 digestInfo
, digestInfoLength
, sig
, *sigLen
);
620 return errSecSuccess
;
623 OSStatus
SecKeyRawSign(
624 SecKeyRef key
, /* Private key */
625 SecPadding padding
, /* kSecPaddingNone or kSecPaddingPKCS1 */
626 const uint8_t *dataToSign
, /* signature over this data */
627 size_t dataToSignLen
, /* length of dataToSign */
628 uint8_t *sig
, /* signature, RETURNED */
629 size_t *sigLen
) { /* IN/OUT */
630 if (!key
->key_class
->rawSign
)
631 return errSecUnsupportedOperation
;
633 if (padding
< kSecPaddingPKCS1MD2
) {
634 return key
->key_class
->rawSign(key
, padding
, dataToSign
, dataToSignLen
,
637 return SecKeyDigestInfoSignVerify(key
, padding
, dataToSign
, dataToSignLen
,
638 sig
, sigLen
, kSecKeyDigestInfoSign
);
642 OSStatus
SecKeyRawVerify(
643 SecKeyRef key
, /* Public key */
644 SecPadding padding
, /* kSecPaddingNone or kSecPaddingPKCS1 */
645 const uint8_t *signedData
, /* signature over this data */
646 size_t signedDataLen
, /* length of dataToSign */
647 const uint8_t *sig
, /* signature */
648 size_t sigLen
) { /* length of signature */
649 if (!key
->key_class
->rawVerify
)
650 return errSecUnsupportedOperation
;
652 if (padding
< kSecPaddingPKCS1MD2
) {
653 return key
->key_class
->rawVerify(key
, padding
, signedData
, signedDataLen
,
656 /* Casting away the constness of sig is safe since
657 SecKeyDigestInfoSignVerify only modifies sig if
658 mode == kSecKeyDigestInfoSign. */
659 return SecKeyDigestInfoSignVerify(key
, padding
,
660 signedData
, signedDataLen
, (uint8_t *)sig
, &sigLen
,
661 kSecKeyDigestInfoVerify
);
665 OSStatus
SecKeyEncrypt(
666 SecKeyRef key
, /* Public key */
667 SecPadding padding
, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */
668 const uint8_t *plainText
,
669 size_t plainTextLen
, /* length of plainText */
671 size_t *cipherTextLen
) { /* IN/OUT */
672 if (key
->key_class
->encrypt
)
673 return key
->key_class
->encrypt(key
, padding
, plainText
, plainTextLen
,
674 cipherText
, cipherTextLen
);
675 return errSecUnsupportedOperation
;
678 OSStatus
SecKeyDecrypt(
679 SecKeyRef key
, /* Private key */
680 SecPadding padding
, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */
681 const uint8_t *cipherText
,
682 size_t cipherTextLen
, /* length of cipherText */
684 size_t *plainTextLen
) { /* IN/OUT */
685 if (key
->key_class
->decrypt
)
686 return key
->key_class
->decrypt(key
, padding
, cipherText
, cipherTextLen
,
687 plainText
, plainTextLen
);
688 return errSecUnsupportedOperation
;
691 size_t SecKeyGetBlockSize(SecKeyRef key
) {
692 if (key
->key_class
->blockSize
)
693 return key
->key_class
->blockSize(key
);
697 /* Private API functions. */
699 CFDictionaryRef
SecKeyCopyAttributeDictionary(SecKeyRef key
) {
700 if (key
->key_class
->copyDictionary
)
701 return key
->key_class
->copyDictionary(key
);
705 SecKeyRef
SecKeyCreateFromAttributeDictionary(CFDictionaryRef refAttributes
) {
706 /* TODO: Support having an allocator in refAttributes. */
707 CFAllocatorRef allocator
= NULL
;
708 CFDataRef data
= CFDictionaryGetValue(refAttributes
, kSecValueData
);
709 CFTypeRef ktype
= CFDictionaryGetValue(refAttributes
, kSecAttrKeyType
);
713 /* First figure out the key type (algorithm). */
714 if (CFGetTypeID(ktype
) == CFNumberGetTypeID()) {
715 CFNumberGetValue(ktype
, kCFNumberSInt32Type
, &algorithm
);
716 } else if (isString(ktype
)) {
717 algorithm
= CFStringGetIntValue(ktype
);
718 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) algorithm
);
719 if (!CFEqual(t
, ktype
)) {
720 secwarning("Unsupported key class: %@", ktype
);
726 secwarning("Unsupported key type: %@", ktype
);
730 /* TODO: The code below won't scale well, consider moving to something
733 CFTypeRef kclass
= CFDictionaryGetValue(refAttributes
, kSecAttrKeyClass
);
734 if (CFGetTypeID(kclass
) == CFNumberGetTypeID()) {
735 CFNumberGetValue(kclass
, kCFNumberSInt32Type
, &class);
736 } else if (isString(kclass
)) {
737 class = CFStringGetIntValue(kclass
);
738 CFStringRef t
= CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) class);
739 if (!CFEqual(t
, kclass
)) {
741 secwarning("Unsupported key class: %@", kclass
);
746 secwarning("Unsupported key class: %@", kclass
);
751 case 0: // kSecAttrKeyClassPublic
753 case 42: // kSecAlgorithmRSA
754 ref
= SecKeyCreateRSAPublicKey(allocator
,
755 CFDataGetBytePtr(data
), CFDataGetLength(data
),
756 kSecKeyEncodingBytes
);
758 case 43: // kSecAlgorithmECDSA
759 case 73: // kSecAlgorithmEC
760 ref
= SecKeyCreateECPublicKey(allocator
,
761 CFDataGetBytePtr(data
), CFDataGetLength(data
),
762 kSecKeyEncodingBytes
);
765 secwarning("Unsupported public key type: %@", ktype
);
770 case 1: // kSecAttrKeyClassPrivate
771 if (CFDictionaryGetValue(refAttributes
, kSecAttrTokenID
) != NULL
) {
772 ref
= SecKeyCreateCTKKey(allocator
, refAttributes
);
776 case 42: // kSecAlgorithmRSA
777 ref
= SecKeyCreateRSAPrivateKey(allocator
,
778 CFDataGetBytePtr(data
), CFDataGetLength(data
),
779 kSecKeyEncodingBytes
);
781 case 43: // kSecAlgorithmECDSA
782 case 73: // kSecAlgorithmEC
783 ref
= SecKeyCreateECPrivateKey(allocator
,
784 CFDataGetBytePtr(data
), CFDataGetLength(data
),
785 kSecKeyEncodingBytes
);
788 secwarning("Unsupported private key type: %@", ktype
);
793 case 2: // kSecAttrKeyClassSymmetric
794 secwarning("Unsupported symmetric key type: %@", ktype
);
798 secwarning("Unsupported key class: %@", kclass
);
805 /* TODO: This function should ensure that this keys algorithm matches the
806 signature algorithm. */
807 static OSStatus
SecKeyGetDigestInfo(SecKeyRef
this, const SecAsn1AlgId
*algId
,
808 const uint8_t *data
, size_t dataLen
, bool digestData
,
809 uint8_t *digestInfo
, size_t *digestInfoLen
/* IN/OUT */) {
810 unsigned char *(*digestFcn
)(const void *, CC_LONG
, unsigned char *);
811 CFIndex keyAlgID
= kSecNullAlgorithmID
;
812 const SecAsn1Oid
*digestOid
;
816 /* Since these oids all have the same prefix, use switch. */
817 if ((algId
->algorithm
.Length
== CSSMOID_RSA
.Length
) &&
818 !memcmp(algId
->algorithm
.Data
, CSSMOID_RSA
.Data
,
819 algId
->algorithm
.Length
- 1)) {
820 keyAlgID
= kSecRSAAlgorithmID
;
821 switch (algId
->algorithm
.Data
[algId
->algorithm
.Length
- 1]) {
823 case 2: /* oidMD2WithRSA */
825 digestLen
= CC_MD2_DIGEST_LENGTH
;
826 digestOid
= &CSSMOID_MD2
;
828 case 3: /* oidMD4WithRSA */
830 digestLen
= CC_MD4_DIGEST_LENGTH
;
831 digestOid
= &CSSMOID_MD4
;
833 case 4: /* oidMD5WithRSA */
835 digestLen
= CC_MD5_DIGEST_LENGTH
;
836 digestOid
= &CSSMOID_MD5
;
839 case 5: /* oidSHA1WithRSA */
841 digestLen
= CC_SHA1_DIGEST_LENGTH
;
842 digestOid
= &CSSMOID_SHA1
;
844 case 11: /* oidSHA256WithRSA */
845 digestFcn
= CC_SHA256
;
846 digestLen
= CC_SHA256_DIGEST_LENGTH
;
847 digestOid
= &CSSMOID_SHA256
;
849 case 12: /* oidSHA384WithRSA */
851 digestFcn
= CC_SHA384
;
852 digestLen
= CC_SHA384_DIGEST_LENGTH
;
853 digestOid
= &CSSMOID_SHA384
;
855 case 13: /* oidSHA512WithRSA */
856 digestFcn
= CC_SHA512
;
857 digestLen
= CC_SHA512_DIGEST_LENGTH
;
858 digestOid
= &CSSMOID_SHA512
;
860 case 14: /* oidSHA224WithRSA */
861 digestFcn
= CC_SHA224
;
862 digestLen
= CC_SHA224_DIGEST_LENGTH
;
863 digestOid
= &CSSMOID_SHA224
;
866 secdebug("key", "unsupported rsa signature algorithm");
867 return errSecUnsupportedAlgorithm
;
869 } else if ((algId
->algorithm
.Length
== CSSMOID_ECDSA_WithSHA224
.Length
) &&
870 !memcmp(algId
->algorithm
.Data
, CSSMOID_ECDSA_WithSHA224
.Data
,
871 algId
->algorithm
.Length
- 1)) {
872 keyAlgID
= kSecECDSAAlgorithmID
;
873 switch (algId
->algorithm
.Data
[algId
->algorithm
.Length
- 1]) {
874 case 1: /* oidSHA224WithECDSA */
875 digestFcn
= CC_SHA224
;
876 digestLen
= CC_SHA224_DIGEST_LENGTH
;
878 case 2: /* oidSHA256WithECDSA */
879 digestFcn
= CC_SHA256
;
880 digestLen
= CC_SHA256_DIGEST_LENGTH
;
882 case 3: /* oidSHA384WithECDSA */
884 digestFcn
= CC_SHA384
;
885 digestLen
= CC_SHA384_DIGEST_LENGTH
;
887 case 4: /* oidSHA512WithECDSA */
888 digestFcn
= CC_SHA512
;
889 digestLen
= CC_SHA512_DIGEST_LENGTH
;
892 secdebug("key", "unsupported ecdsa signature algorithm");
893 return errSecUnsupportedAlgorithm
;
895 } else if (SecAsn1OidCompare(&algId
->algorithm
, &CSSMOID_ECDSA_WithSHA1
)) {
896 keyAlgID
= kSecECDSAAlgorithmID
;
898 digestLen
= CC_SHA1_DIGEST_LENGTH
;
899 } else if (SecAsn1OidCompare(&algId
->algorithm
, &CSSMOID_SHA1
)) {
901 digestLen
= CC_SHA1_DIGEST_LENGTH
;
902 digestOid
= &CSSMOID_SHA1
;
903 } else if ((algId
->algorithm
.Length
== CSSMOID_SHA224
.Length
) &&
904 !memcmp(algId
->algorithm
.Data
, CSSMOID_SHA224
.Data
, algId
->algorithm
.Length
- 1))
906 switch (algId
->algorithm
.Data
[algId
->algorithm
.Length
- 1]) {
907 case 4: /* OID_SHA224 */
908 digestFcn
= CC_SHA224
;
909 digestLen
= CC_SHA224_DIGEST_LENGTH
;
910 digestOid
= &CSSMOID_SHA224
;
912 case 1: /* OID_SHA256 */
913 digestFcn
= CC_SHA256
;
914 digestLen
= CC_SHA256_DIGEST_LENGTH
;
915 digestOid
= &CSSMOID_SHA256
;
917 case 2: /* OID_SHA384 */
919 digestFcn
= CC_SHA384
;
920 digestLen
= CC_SHA384_DIGEST_LENGTH
;
921 digestOid
= &CSSMOID_SHA384
;
923 case 3: /* OID_SHA512 */
924 digestFcn
= CC_SHA512
;
925 digestLen
= CC_SHA512_DIGEST_LENGTH
;
926 digestOid
= &CSSMOID_SHA512
;
929 secdebug("key", "unsupported sha-2 signature algorithm");
930 return errSecUnsupportedAlgorithm
;
932 } else if (SecAsn1OidCompare(&algId
->algorithm
, &CSSMOID_MD5
)) {
934 digestLen
= CC_MD5_DIGEST_LENGTH
;
935 digestOid
= &CSSMOID_MD5
;
937 secdebug("key", "unsupported digesting algorithm");
938 return errSecUnsupportedAlgorithm
;
941 /* check key is appropriate for signature (superfluous for digest only oid) */
942 if (keyAlgID
== kSecNullAlgorithmID
)
943 keyAlgID
= SecKeyGetAlgorithmIdentifier(this);
944 else if (keyAlgID
!= SecKeyGetAlgorithmIdentifier(this))
945 return errSecUnsupportedAlgorithm
;
948 case kSecRSAAlgorithmID
:
949 offset
= DEREncodeDigestInfoPrefix(digestOid
, digestLen
,
950 digestInfo
, *digestInfoLen
);
952 return errSecBufferTooSmall
;
954 case kSecDSAAlgorithmID
:
955 if (digestOid
!= &CSSMOID_SHA1
)
956 return errSecUnsupportedAlgorithm
;
958 case kSecECDSAAlgorithmID
:
961 secdebug("key", "unsupported signature algorithm");
962 return errSecUnsupportedAlgorithm
;
966 if(dataLen
>UINT32_MAX
) /* Check for overflow with CC_LONG cast */
968 digestFcn(data
, (CC_LONG
)dataLen
, &digestInfo
[offset
]);
969 *digestInfoLen
= offset
+ digestLen
;
971 if (dataLen
!= digestLen
)
973 memcpy(&digestInfo
[offset
], data
, dataLen
);
974 *digestInfoLen
= offset
+ dataLen
;
977 return errSecSuccess
;
980 OSStatus
SecKeyDigestAndVerify(
981 SecKeyRef
this, /* Private key */
982 const SecAsn1AlgId
*algId
, /* algorithm oid/params */
983 const uint8_t *dataToDigest
, /* signature over this data */
984 size_t dataToDigestLen
,/* length of dataToDigest */
985 const uint8_t *sig
, /* signature to verify */
986 size_t sigLen
) { /* length of sig */
987 size_t digestInfoLength
= DER_MAX_DIGEST_INFO_LEN
;
988 uint8_t digestInfo
[digestInfoLength
];
994 status
= SecKeyGetDigestInfo(this, algId
, dataToDigest
, dataToDigestLen
, true,
995 digestInfo
, &digestInfoLength
);
998 return SecKeyRawVerify(this, kSecPaddingPKCS1
,
999 digestInfo
, digestInfoLength
, sig
, sigLen
);
1002 OSStatus
SecKeyDigestAndSign(
1003 SecKeyRef
this, /* Private key */
1004 const SecAsn1AlgId
*algId
, /* algorithm oid/params */
1005 const uint8_t *dataToDigest
, /* signature over this data */
1006 size_t dataToDigestLen
,/* length of dataToDigest */
1007 uint8_t *sig
, /* signature, RETURNED */
1008 size_t *sigLen
) { /* IN/OUT */
1009 size_t digestInfoLength
= DER_MAX_DIGEST_INFO_LEN
;
1010 uint8_t digestInfo
[digestInfoLength
];
1013 status
= SecKeyGetDigestInfo(this, algId
, dataToDigest
, dataToDigestLen
, true /* digest data */,
1014 digestInfo
, &digestInfoLength
);
1017 return SecKeyRawSign(this, kSecPaddingPKCS1
,
1018 digestInfo
, digestInfoLength
, sig
, sigLen
);
1021 OSStatus
SecKeyVerifyDigest(
1022 SecKeyRef
this, /* Private key */
1023 const SecAsn1AlgId
*algId
, /* algorithm oid/params */
1024 const uint8_t *digestData
, /* signature over this digest */
1025 size_t digestDataLen
,/* length of dataToDigest */
1026 const uint8_t *sig
, /* signature to verify */
1027 size_t sigLen
) { /* length of sig */
1028 size_t digestInfoLength
= DER_MAX_DIGEST_INFO_LEN
;
1029 uint8_t digestInfo
[digestInfoLength
];
1032 status
= SecKeyGetDigestInfo(this, algId
, digestData
, digestDataLen
, false /* data is digest */,
1033 digestInfo
, &digestInfoLength
);
1036 return SecKeyRawVerify(this, kSecPaddingPKCS1
,
1037 digestInfo
, digestInfoLength
, sig
, sigLen
);
1040 OSStatus
SecKeySignDigest(
1041 SecKeyRef
this, /* Private key */
1042 const SecAsn1AlgId
*algId
, /* algorithm oid/params */
1043 const uint8_t *digestData
, /* signature over this digest */
1044 size_t digestDataLen
,/* length of digestData */
1045 uint8_t *sig
, /* signature, RETURNED */
1046 size_t *sigLen
) { /* IN/OUT */
1047 size_t digestInfoLength
= DER_MAX_DIGEST_INFO_LEN
;
1048 uint8_t digestInfo
[digestInfoLength
];
1051 status
= SecKeyGetDigestInfo(this, algId
, digestData
, digestDataLen
, false,
1052 digestInfo
, &digestInfoLength
);
1055 return SecKeyRawSign(this, kSecPaddingPKCS1
,
1056 digestInfo
, digestInfoLength
, sig
, sigLen
);
1059 CFIndex
SecKeyGetAlgorithmId(SecKeyRef key
) {
1060 return SecKeyGetAlgorithmIdentifier(key
);
1063 #if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
1064 /* On OS X, SecKeyGetAlgorithmID has a different function signature (two arguments,
1065 with output in the second argument). Therefore, avoid implementing this function here
1066 if compiling for OS X.
1069 CFIndex
SecKeyGetAlgorithmID(SecKeyRef key
) {
1070 return SecKeyGetAlgorithmIdentifier(key
);
1074 OSStatus
SecKeyCopyPublicBytes(SecKeyRef key
, CFDataRef
* serializedPublic
) {
1075 if (key
->key_class
->version
> 1 && key
->key_class
->copyPublic
)
1076 return key
->key_class
->copyPublic(key
, serializedPublic
);
1077 return errSecUnimplemented
;
1080 SecKeyRef
SecKeyCreateFromPublicBytes(CFAllocatorRef allocator
, CFIndex algorithmID
, const uint8_t *keyData
, CFIndex keyDataLength
)
1082 switch (algorithmID
)
1084 case kSecRSAAlgorithmID
:
1085 return SecKeyCreateRSAPublicKey(allocator
,
1086 keyData
, keyDataLength
,
1087 kSecKeyEncodingBytes
);
1088 case kSecECDSAAlgorithmID
:
1089 return SecKeyCreateECPublicKey(allocator
,
1090 keyData
, keyDataLength
,
1091 kSecKeyEncodingBytes
);
1097 SecKeyRef
SecKeyCreateFromPublicData(CFAllocatorRef allocator
, CFIndex algorithmID
, CFDataRef serialized
)
1099 return SecKeyCreateFromPublicBytes(allocator
, algorithmID
, CFDataGetBytePtr(serialized
), CFDataGetLength(serialized
));
1102 // This is a bit icky hack to avoid changing the vtable for
1104 size_t SecKeyGetSize(SecKeyRef key
, SecKeySize whichSize
)
1106 size_t result
= SecKeyGetBlockSize(key
);
1108 if (kSecECDSAAlgorithmID
== SecKeyGetAlgorithmIdentifier(key
)) {
1109 switch (whichSize
) {
1110 case kSecKeyEncryptedDataSize
:
1113 case kSecKeySignatureSize
:
1114 result
= (result
>= 66 ? 9 : 8) + 2 * result
;
1116 case kSecKeyKeySizeInBits
:
1122 if (whichSize
== kSecKeyKeySizeInBits
)
1129 OSStatus
SecKeyFindWithPersistentRef(CFDataRef persistentRef
, SecKeyRef
* lookedUpData
)
1131 CFDictionaryRef query
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1132 kSecReturnRef
, kCFBooleanTrue
,
1133 kSecClass
, kSecClassKey
,
1134 kSecValuePersistentRef
, persistentRef
,
1136 CFTypeRef foundRef
= NULL
;
1137 OSStatus status
= SecItemCopyMatching(query
, &foundRef
);
1139 if (status
== errSecSuccess
) {
1140 if (CFGetTypeID(foundRef
) == SecKeyGetTypeID()) {
1141 *lookedUpData
= (SecKeyRef
) foundRef
;
1143 status
= errSecSuccess
;
1145 status
= errSecItemNotFound
;
1149 CFReleaseSafe(foundRef
);
1150 CFReleaseSafe(query
);
1155 OSStatus
SecKeyCopyPersistentRef(SecKeyRef key
, CFDataRef
* persistentRef
)
1157 CFDictionaryRef query
= CFDictionaryCreateForCFTypes(kCFAllocatorDefault
,
1158 kSecReturnPersistentRef
, kCFBooleanTrue
,
1160 kSecAttrSynchronizable
, kSecAttrSynchronizableAny
,
1162 CFTypeRef foundRef
= NULL
;
1163 OSStatus status
= SecItemCopyMatching(query
, &foundRef
);
1165 if (status
== errSecSuccess
) {
1166 if (CFGetTypeID(foundRef
) == CFDataGetTypeID()) {
1167 *persistentRef
= foundRef
;
1170 status
= errSecItemNotFound
;
1174 CFReleaseSafe(foundRef
);
1175 CFReleaseSafe(query
);
1184 #define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
1186 SEC_CONST_DECL(_kSecKeyWrapPGPSymAlg
, "kSecKeyWrapPGPSymAlg");
1187 SEC_CONST_DECL(_kSecKeyWrapPGPFingerprint
, "kSecKeyWrapPGPFingerprint");
1188 SEC_CONST_DECL(_kSecKeyWrapPGPWrapAlg
, "kSecKeyWrapPGPWrapAlg");
1189 SEC_CONST_DECL(_kSecKeyWrapRFC6637Flags
, "kSecKeyWrapPGPECFlags");
1190 SEC_CONST_DECL(_kSecKeyWrapRFC6637WrapDigestSHA256KekAES128
, "kSecKeyWrapPGPECWrapDigestSHA256KekAES128");
1191 SEC_CONST_DECL(_kSecKeyWrapRFC6637WrapDigestSHA512KekAES256
, "kSecKeyWrapPGPECWrapDigestSHA512KekAES256");
1193 #undef SEC_CONST_DECL
1196 _SecKeyCopyWrapKey(SecKeyRef key
, SecKeyWrapType type
, CFDataRef unwrappedKey
, CFDictionaryRef parameters
, CFDictionaryRef
*outParam
, CFErrorRef
*error
)
1202 if (key
->key_class
->version
> 2 && key
->key_class
->copyWrapKey
)
1203 return key
->key_class
->copyWrapKey(key
, type
, unwrappedKey
, parameters
, outParam
, error
);
1204 SecError(errSecUnsupportedOperation
, error
, CFSTR("No key wrap supported for key %@"), key
);
1209 _SecKeyCopyUnwrapKey(SecKeyRef key
, SecKeyWrapType type
, CFDataRef wrappedKey
, CFDictionaryRef parameters
, CFDictionaryRef
*outParam
, CFErrorRef
*error
)
1215 if (key
->key_class
->version
> 2 && key
->key_class
->copyUnwrapKey
)
1216 return key
->key_class
->copyUnwrapKey(key
, type
, wrappedKey
, parameters
, outParam
, error
);
1218 SecError(errSecUnsupportedOperation
, error
, CFSTR("No key unwrap for key %@"), key
);