--- /dev/null
+/*
+ * Copyright (c) 2006-2015 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@
+ */
+
+/*
+ * SecKey.c - CoreFoundation based key object
+ */
+
+
+#include <Security/SecKeyInternal.h>
+#include <Security/SecItem.h>
+#include <Security/SecItemPriv.h>
+#include <Security/SecFramework.h>
+
+#include <utilities/SecIOFormat.h>
+
+#include <utilities/SecCFWrappers.h>
+
+#include "SecRSAKeyPriv.h"
+#include "SecECKeyPriv.h"
+#include "SecCTKKeyPriv.h"
+#include "SecBasePriv.h"
+
+#include <CoreFoundation/CFNumber.h>
+#include <CoreFoundation/CFString.h>
+#include <Security/SecBase.h>
+#include <pthread.h>
+#include <string.h>
+#include <AssertMacros.h>
+#include <utilities/debugging.h>
+#include <utilities/SecCFError.h>
+#include <CommonCrypto/CommonDigest.h>
+#include <Security/SecAsn1Coder.h>
+#include <Security/oidsalg.h>
+#include <Security/SecInternal.h>
+#include <Security/SecRandom.h>
+#include <corecrypto/ccrng_system.h>
+#include <asl.h>
+#include <stdlib.h>
+#include <syslog.h>
+
+#include <libDER/asn1Types.h>
+#include <libDER/DER_Keys.h>
+#include <libDER/DER_Encode.h>
+
+
+/* Static functions. */
+#define MAX_DIGEST_LEN (CC_SHA512_DIGEST_LENGTH)
+
+/* Currently length of SHA512 oid + 1 */
+#define MAX_OID_LEN (10)
+
+#define DER_MAX_DIGEST_INFO_LEN (10 + MAX_DIGEST_LEN + MAX_OID_LEN)
+
+/* Encode the digestInfo header into digestInfo and return the offset from
+ digestInfo at which to put the actual digest. Returns 0 if digestInfo
+ won't fit within digestInfoLength bytes.
+
+ 0x30, topLen,
+ 0x30, algIdLen,
+ 0x06, oid.Len, oid.Data,
+ 0x05, 0x00
+ 0x04, digestLen
+ digestData
+ */
+
+static size_t DEREncodeDigestInfoPrefix(const SecAsn1Oid *oid,
+ size_t digestLength, uint8_t *digestInfo, size_t digestInfoLength) {
+ size_t algIdLen = oid->Length + 4;
+ size_t topLen = algIdLen + digestLength + 4;
+ size_t totalLen = topLen + 2;
+
+ if (totalLen > digestInfoLength) {
+ return 0;
+ }
+
+ size_t ix = 0;
+ digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED);
+ digestInfo[ix++] = topLen;
+ digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED);
+ digestInfo[ix++] = algIdLen;
+ digestInfo[ix++] = SEC_ASN1_OBJECT_ID;
+ digestInfo[ix++] = oid->Length;
+ memcpy(&digestInfo[ix], oid->Data, oid->Length);
+ ix += oid->Length;
+ digestInfo[ix++] = SEC_ASN1_NULL;
+ digestInfo[ix++] = 0;
+ digestInfo[ix++] = SEC_ASN1_OCTET_STRING;
+ digestInfo[ix++] = digestLength;
+
+ return ix;
+}
+
+CFDataRef SecKeyCopyPublicKeyHash(SecKeyRef key)
+{
+ CFDataRef pubKeyDigest = NULL, pubKeyBlob = NULL;
+
+ /* encode the public key. */
+ require_noerr(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut);
+ require(pubKeyBlob, errOut);
+
+ /* Calculate the digest of the public key. */
+ require(pubKeyDigest = SecSHA1DigestCreate(CFGetAllocator(key),
+ CFDataGetBytePtr(pubKeyBlob), CFDataGetLength(pubKeyBlob)),
+ errOut);
+errOut:
+ CFReleaseNull(pubKeyBlob);
+ return pubKeyDigest;
+}
+
+
+/*
+ */
+static CFDictionaryRef SecKeyCopyAttributeDictionaryWithLocalKey(SecKeyRef key,
+ CFTypeRef keyType,
+ CFDataRef privateBlob)
+{
+ CFAllocatorRef allocator = CFGetAllocator(key);
+ DICT_DECLARE(25);
+ CFDataRef pubKeyDigest = NULL, pubKeyBlob = NULL;
+ CFDictionaryRef dict = NULL;
+
+ size_t sizeValue = SecKeyGetSize(key, kSecKeyKeySizeInBits);
+ CFNumberRef sizeInBits = CFNumberCreate(allocator, kCFNumberLongType, &sizeValue);
+
+ /* encode the public key. */
+ require_noerr(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut);
+ require(pubKeyBlob, errOut);
+
+ /* Calculate the digest of the public key. */
+ require(pubKeyDigest = SecSHA1DigestCreate(allocator,
+ CFDataGetBytePtr(pubKeyBlob), CFDataGetLength(pubKeyBlob)),
+ errOut);
+
+ DICT_ADDPAIR(kSecClass, kSecClassKey);
+ DICT_ADDPAIR(kSecAttrKeyClass, privateBlob ? kSecAttrKeyClassPrivate : kSecAttrKeyClassPublic);
+ DICT_ADDPAIR(kSecAttrApplicationLabel, pubKeyDigest);
+ DICT_ADDPAIR(kSecAttrIsPermanent, kCFBooleanTrue);
+ DICT_ADDPAIR(kSecAttrIsPrivate, kCFBooleanTrue);
+ DICT_ADDPAIR(kSecAttrIsModifiable, kCFBooleanTrue);
+ DICT_ADDPAIR(kSecAttrKeyType, keyType);
+ DICT_ADDPAIR(kSecAttrKeySizeInBits, sizeInBits);
+ DICT_ADDPAIR(kSecAttrEffectiveKeySize, sizeInBits);
+ DICT_ADDPAIR(kSecAttrIsSensitive, kCFBooleanFalse);
+ DICT_ADDPAIR(kSecAttrWasAlwaysSensitive, kCFBooleanFalse);
+ DICT_ADDPAIR(kSecAttrIsExtractable, kCFBooleanTrue);
+ DICT_ADDPAIR(kSecAttrWasNeverExtractable, kCFBooleanFalse);
+ DICT_ADDPAIR(kSecAttrCanEncrypt, kCFBooleanFalse);
+ DICT_ADDPAIR(kSecAttrCanDecrypt, kCFBooleanTrue);
+ DICT_ADDPAIR(kSecAttrCanDerive, kCFBooleanTrue);
+ DICT_ADDPAIR(kSecAttrCanSign, kCFBooleanTrue);
+ DICT_ADDPAIR(kSecAttrCanVerify, kCFBooleanFalse);
+ DICT_ADDPAIR(kSecAttrCanSignRecover, kCFBooleanFalse);
+ DICT_ADDPAIR(kSecAttrCanVerifyRecover, kCFBooleanFalse);
+ DICT_ADDPAIR(kSecAttrCanWrap, kCFBooleanFalse);
+ DICT_ADDPAIR(kSecAttrCanUnwrap, kCFBooleanTrue);
+ DICT_ADDPAIR(kSecValueData, privateBlob ? privateBlob : pubKeyBlob);
+ dict = DICT_CREATE(allocator);
+
+errOut:
+ // @@@ Zero out key material.
+ CFReleaseSafe(pubKeyDigest);
+ CFReleaseSafe(pubKeyBlob);
+ CFReleaseSafe(sizeInBits);
+
+ return dict;
+}
+
+CFDictionaryRef SecKeyGeneratePrivateAttributeDictionary(SecKeyRef key,
+ CFTypeRef keyType,
+ CFDataRef privateBlob)
+{
+ return SecKeyCopyAttributeDictionaryWithLocalKey(key, keyType, privateBlob);
+}
+
+CFDictionaryRef SecKeyGeneratePublicAttributeDictionary(SecKeyRef key, CFTypeRef keyType)
+{
+ return SecKeyCopyAttributeDictionaryWithLocalKey(key, keyType, NULL);
+}
+
+static CFStringRef SecKeyCopyDescription(CFTypeRef cf) {
+ SecKeyRef key = (SecKeyRef)cf;
+
+ if(key->key_class->describe)
+ return key->key_class->describe(key);
+ else
+ return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecKeyRef: %p>"), key);
+}
+
+static void SecKeyDestroy(CFTypeRef cf) {
+ SecKeyRef key = (SecKeyRef)cf;
+ if (key->key_class->destroy)
+ key->key_class->destroy(key);
+}
+
+static Boolean SecKeyEqual(CFTypeRef cf1, CFTypeRef cf2)
+{
+ SecKeyRef key1 = (SecKeyRef)cf1;
+ SecKeyRef key2 = (SecKeyRef)cf2;
+ if (key1 == key2)
+ return true;
+ if (!key2 || key1->key_class != key2->key_class)
+ return false;
+ if (key1->key_class->extraBytes)
+ return !memcmp(key1->key, key2->key, key1->key_class->extraBytes);
+
+ /* TODO: Won't work when we get reference keys. */
+ CFDictionaryRef d1, d2;
+ d1 = SecKeyCopyAttributeDictionary(key1);
+ d2 = SecKeyCopyAttributeDictionary(key2);
+ Boolean result = CFEqual(d1, d2);
+ CFReleaseSafe(d1);
+ CFReleaseSafe(d2);
+ return result;
+}
+
+struct ccrng_state *ccrng_seckey;
+
+CFGiblisWithFunctions(SecKey, NULL, NULL, SecKeyDestroy, SecKeyEqual, NULL, NULL, SecKeyCopyDescription, NULL, NULL, ^{
+ static struct ccrng_system_state ccrng_system_state_seckey;
+ ccrng_seckey = (struct ccrng_state *)&ccrng_system_state_seckey;
+ ccrng_system_init(&ccrng_system_state_seckey);
+})
+
+static bool getBoolForKey(CFDictionaryRef dict, CFStringRef key, bool default_value) {
+ CFTypeRef value = CFDictionaryGetValue(dict, key);
+ if (value) {
+ if (CFGetTypeID(value) == CFBooleanGetTypeID()) {
+ return CFBooleanGetValue(value);
+ } else {
+ secwarning("Value %@ for key %@ is not bool", value, key);
+ }
+ }
+
+ return default_value;
+}
+
+static OSStatus add_ref(CFTypeRef item, CFMutableDictionaryRef dict) {
+ CFDictionarySetValue(dict, kSecValueRef, item);
+ return SecItemAdd(dict, NULL);
+}
+
+static void merge_params_applier(const void *key, const void *value,
+ void *context) {
+ CFMutableDictionaryRef result = (CFMutableDictionaryRef)context;
+ CFDictionaryAddValue(result, key, value);
+}
+
+/* Create a mutable dictionary that is based on the subdictionary for key
+ with any attributes from the top level dict merged in. */
+static CF_RETURNS_RETAINED CFMutableDictionaryRef merge_params(CFDictionaryRef dict,
+ CFStringRef key) {
+ CFDictionaryRef subdict = CFDictionaryGetValue(dict, key);
+ CFMutableDictionaryRef result;
+
+ if (subdict) {
+ result = CFDictionaryCreateMutableCopy(NULL, 0, subdict);
+ /* Add everything in dict not already in result to result. */
+ CFDictionaryApplyFunction(dict, merge_params_applier, result);
+ } else {
+ result = CFDictionaryCreateMutableCopy(NULL, 0, dict);
+ }
+
+ /* Remove values that only belong in the top level dict. */
+ CFDictionaryRemoveValue(result, kSecPublicKeyAttrs);
+ CFDictionaryRemoveValue(result, kSecPrivateKeyAttrs);
+ CFDictionaryRemoveValue(result, kSecAttrKeyType);
+ CFDictionaryRemoveValue(result, kSecAttrKeySizeInBits);
+
+ return result;
+}
+
+CFIndex SecKeyGetAlgorithmIdentifier(SecKeyRef key) {
+ if (!key || !key->key_class) {
+ // TBD: somehow, a key can be created with a NULL key_class in the
+ // SecCertificateCopyPublicKey -> SecKeyCreatePublicFromDER code path
+ return kSecNullAlgorithmID;
+ }
+ /* This method was added to version 1 keys. */
+ if (key->key_class->version > 0 && key->key_class->getAlgorithmID) {
+ return key->key_class->getAlgorithmID(key);
+ }
+ /* All version 0 key were RSA. */
+ return kSecRSAAlgorithmID;
+}
+
+/* Generate a private/public keypair. */
+OSStatus SecKeyGeneratePair(CFDictionaryRef parameters,
+ SecKeyRef *publicKey, SecKeyRef *privateKey) {
+ OSStatus result = errSecUnsupportedAlgorithm;
+ SecKeyRef privKey = NULL;
+ SecKeyRef pubKey = NULL;
+ CFMutableDictionaryRef pubParams = merge_params(parameters, kSecPublicKeyAttrs),
+ privParams = merge_params(parameters, kSecPrivateKeyAttrs);
+ CFStringRef ktype = CFDictionaryGetValue(parameters, kSecAttrKeyType);
+ CFStringRef tokenID = CFDictionaryGetValue(parameters, kSecAttrTokenID);
+
+ require(ktype, errOut);
+
+ if (tokenID != NULL) {
+ result = SecCTKKeyGeneratePair(parameters, &pubKey, &privKey);
+ } else if (CFEqual(ktype, kSecAttrKeyTypeEC)) {
+ result = SecECKeyGeneratePair(parameters, &pubKey, &privKey);
+ } else if (CFEqual(ktype, kSecAttrKeyTypeRSA)) {
+ result = SecRSAKeyGeneratePair(parameters, &pubKey, &privKey);
+ }
+
+ require_noerr(result, errOut);
+
+ /* Store the keys in the keychain if they are marked as permanent. */
+ if (getBoolForKey(pubParams, kSecAttrIsPermanent, false)) {
+ require_noerr_quiet(result = add_ref(pubKey, pubParams), errOut);
+ }
+ /* Token-based private keys are automatically stored on the token. */
+ if (tokenID == NULL && getBoolForKey(privParams, kSecAttrIsPermanent, false)) {
+ require_noerr_quiet(result = add_ref(privKey, privParams), errOut);
+ }
+
+ if (publicKey) {
+ *publicKey = pubKey;
+ pubKey = NULL;
+ }
+ if (privateKey) {
+ *privateKey = privKey;
+ privKey = NULL;
+ }
+
+errOut:
+ CFReleaseSafe(pubParams);
+ CFReleaseSafe(privParams);
+ CFReleaseSafe(pubKey);
+ CFReleaseSafe(privKey);
+
+ return result;
+}
+
+SecKeyRef SecKeyCreatePublicFromPrivate(SecKeyRef privateKey) {
+ CFDataRef serializedPublic = NULL;
+ SecKeyRef result = NULL;
+
+ require_noerr_quiet(SecKeyCopyPublicBytes(privateKey, &serializedPublic), fail);
+ require_quiet(serializedPublic, fail);
+
+ result = SecKeyCreateFromPublicData(kCFAllocatorDefault, SecKeyGetAlgorithmIdentifier(privateKey), serializedPublic);
+
+fail:
+ CFReleaseSafe(serializedPublic);
+
+ return result;
+}
+
+CFDictionaryRef CreatePrivateKeyMatchingQuery(SecKeyRef publicKey, bool returnPersistentRef)
+{
+ const CFTypeRef refType = (returnPersistentRef) ? kSecReturnPersistentRef: kSecReturnRef;
+
+ CFDataRef public_key_hash = SecKeyCopyPublicKeyHash(publicKey);
+
+ CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+ kSecClass, kSecClassKey,
+ kSecAttrKeyClass, kSecAttrKeyClassPrivate,
+ kSecAttrSynchronizable, kSecAttrSynchronizableAny,
+ kSecAttrApplicationLabel, public_key_hash,
+ refType, kCFBooleanTrue,
+ NULL);
+ CFReleaseNull(public_key_hash);
+
+ return query;
+}
+
+CFDataRef SecKeyCreatePersistentRefToMatchingPrivateKey(SecKeyRef publicKey, CFErrorRef *error) {
+ CFTypeRef persistentRef = NULL;
+ CFDictionaryRef query = CreatePrivateKeyMatchingQuery(publicKey, true);
+
+ require_quiet(SecError(SecItemCopyMatching(query, &persistentRef),error ,
+ CFSTR("Error finding persistent ref to key from public: %@"), publicKey), fail);
+fail:
+ CFReleaseNull(query);
+ return (CFDataRef)persistentRef;
+}
+
+SecKeyRef SecKeyCopyMatchingPrivateKey(SecKeyRef publicKey, CFErrorRef *error) {
+ SecKeyRef privateKey = NULL;
+ CFTypeRef queryResult = NULL;
+ CFDictionaryRef query = NULL;
+
+ require_action_quiet(publicKey != NULL, errOut, SecError(errSecParam, error, NULL, CFSTR("Null Public Key")));
+
+ query = CreatePrivateKeyMatchingQuery(publicKey, false);
+
+ require_quiet(SecError(SecItemCopyMatching(query, &queryResult), error,
+ CFSTR("Error finding private key from public: %@"), publicKey), errOut);
+
+ if (CFGetTypeID(queryResult) == SecKeyGetTypeID()) {
+ privateKey = (SecKeyRef) queryResult;
+ queryResult = NULL;
+ }
+
+errOut:
+ CFReleaseNull(query);
+ CFReleaseNull(queryResult);
+ return privateKey;
+}
+
+OSStatus SecKeyGetMatchingPrivateKeyStatus(SecKeyRef publicKey, CFErrorRef *error) {
+ OSStatus retval = errSecParam;
+ CFTypeRef private_key = NULL;
+ CFDictionaryRef query = NULL;
+
+ require_action_quiet(publicKey != NULL, errOut, SecError(errSecParam, error, NULL, CFSTR("Null Public Key")));
+
+ query = CreatePrivateKeyMatchingQuery(publicKey, false);
+
+ retval = SecItemCopyMatching(query, &private_key);
+
+ if (!retval && CFGetTypeID(private_key) != SecKeyGetTypeID()) {
+ retval = errSecInternalComponent;
+ }
+
+errOut:
+ CFReleaseNull(query);
+ CFReleaseNull(private_key);
+ return retval;
+}
+
+
+SecKeyRef SecKeyCreatePublicFromDER(CFAllocatorRef allocator,
+ const SecAsn1Oid *oid, const SecAsn1Item *params,
+ const SecAsn1Item *keyData) {
+ SecKeyRef publicKey = NULL;
+ if (SecAsn1OidCompare(oid, &CSSMOID_RSA)) {
+ /* pkcs1 1 */
+ publicKey = SecKeyCreateRSAPublicKey(kCFAllocatorDefault,
+ keyData->Data, keyData->Length, kSecKeyEncodingPkcs1);
+ } else if (SecAsn1OidCompare(oid, &CSSMOID_ecPublicKey)) {
+ SecDERKey derKey = {
+ .oid = oid->Data,
+ .oidLength = oid->Length,
+ .key = keyData->Data,
+ .keyLength = keyData->Length,
+ };
+ if (params) {
+ derKey.parameters = params->Data;
+ derKey.parametersLength = params->Length;
+ }
+ publicKey = SecKeyCreateECPublicKey(kCFAllocatorDefault,
+ (const uint8_t *)&derKey, sizeof(derKey), kSecDERKeyEncoding);
+ } else {
+ secwarning("Unsupported algorithm oid");
+ }
+
+ return publicKey;
+}
+
+
+SecKeyRef SecKeyCreateFromSubjectPublicKeyInfoData(CFAllocatorRef allocator, CFDataRef subjectPublicKeyInfoData)
+{
+ DERReturn drtn;
+
+ DERItem subjectPublicKeyInfoDER = {
+ .data = (uint8_t *)CFDataGetBytePtr(subjectPublicKeyInfoData),
+ .length = (DERSize)CFDataGetLength(subjectPublicKeyInfoData),
+ };
+ DERSubjPubKeyInfo subjectPublicKeyInfo;
+ DERAlgorithmId algorithmId;
+ DERItem pubKeyBytes;
+
+ drtn = DERParseSequence(&subjectPublicKeyInfoDER,
+ DERNumSubjPubKeyInfoItemSpecs, DERSubjPubKeyInfoItemSpecs,
+ &subjectPublicKeyInfo, sizeof(subjectPublicKeyInfo));
+
+ require_noerr_quiet(drtn, out);
+
+ drtn = DERParseSequenceContent(&subjectPublicKeyInfo.algId,
+ DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
+ &algorithmId, sizeof(algorithmId));
+ require_noerr_quiet(drtn, out);
+
+ DERByte unusedBits;
+ drtn = DERParseBitString(&subjectPublicKeyInfo.pubKey, &pubKeyBytes, &unusedBits);
+ require_noerr_quiet(drtn, out);
+
+ /* Convert DERItem to SecAsn1Item : */
+ const SecAsn1Oid oid = { .Data = algorithmId.oid.data, .Length = algorithmId.oid.length };
+ const SecAsn1Item params = { .Data = algorithmId.params.data, .Length = algorithmId.params.length };
+ const SecAsn1Item pubKey = { .Data = pubKeyBytes.data, .Length = pubKeyBytes.length };
+
+ return SecKeyCreatePublicFromDER(allocator, &oid, ¶ms, &pubKey);
+
+out:
+
+ return NULL;
+
+}
+
+
+
+SecKeyRef SecKeyCreate(CFAllocatorRef allocator,
+ const SecKeyDescriptor *key_class, const uint8_t *keyData,
+ CFIndex keyDataLength, SecKeyEncoding encoding) {
+ if (!key_class) return NULL;
+ size_t size = sizeof(struct __SecKey) + key_class->extraBytes;
+ SecKeyRef result = (SecKeyRef)_CFRuntimeCreateInstance(allocator,
+ SecKeyGetTypeID(), size - sizeof(CFRuntimeBase), NULL);
+ if (result) {
+ memset((char*)result + sizeof(result->_base), 0, size - sizeof(result->_base));
+ result->key_class = key_class;
+ if (key_class->extraBytes) {
+ /* Make result->key point to the extraBytes we allocated. */
+ result->key = ((char*)result) + sizeof(*result);
+ }
+ if (key_class->init) {
+ OSStatus status;
+ status = key_class->init(result, keyData, keyDataLength, encoding);
+ if (status) {
+ secwarning("init %s key: %" PRIdOSStatus, key_class->name, status);
+ CFRelease(result);
+ result = NULL;
+ }
+ }
+ }
+ return result;
+}
+
+enum {
+ kSecKeyDigestInfoSign,
+ kSecKeyDigestInfoVerify
+};
+
+static OSStatus SecKeyDigestInfoSignVerify(
+ SecKeyRef key, /* Private key */
+ SecPadding padding, /* kSecPaddingPKCS1@@@ */
+ const uint8_t *dataToSign, /* signature over this data */
+ size_t dataToSignLen, /* length of dataToSign */
+ uint8_t *sig, /* signature, RETURNED */
+ size_t *sigLen, /* IN/OUT */
+ int mode) {
+ size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN;
+ uint8_t digestInfo[digestInfoLength];
+ const SecAsn1Oid *digestOid;
+ size_t digestLen;
+
+ switch (padding) {
+#if 0
+ case kSecPaddingPKCS1MD2:
+ digestLen = CC_MD2_DIGEST_LENGTH;
+ digestOid = &CSSMOID_MD2;
+ break;
+ case kSecPaddingPKCS1MD4:
+ digestLen = CC_MD4_DIGEST_LENGTH;
+ digestOid = &CSSMOID_MD4;
+ break;
+ case kSecPaddingPKCS1MD5:
+ digestLen = CC_MD5_DIGEST_LENGTH;
+ digestOid = &CSSMOID_MD5;
+ break;
+#endif
+ case kSecPaddingPKCS1SHA1:
+ digestLen = CC_SHA1_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA1;
+ break;
+ case kSecPaddingPKCS1SHA224:
+ digestLen = CC_SHA224_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA224;
+ break;
+ case kSecPaddingPKCS1SHA256:
+ digestLen = CC_SHA256_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA256;
+ break;
+ case kSecPaddingPKCS1SHA384:
+ digestLen = CC_SHA384_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA384;
+ break;
+ case kSecPaddingPKCS1SHA512:
+ digestLen = CC_SHA512_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA512;
+ break;
+ default:
+ return errSecUnsupportedPadding;
+ }
+
+ if (dataToSignLen != digestLen)
+ return errSecParam;
+
+ size_t offset = DEREncodeDigestInfoPrefix(digestOid, digestLen,
+ digestInfo, digestInfoLength);
+ if (!offset)
+ return errSecBufferTooSmall;
+
+ /* Append the digest to the digestInfo prefix and adjust the length. */
+ memcpy(&digestInfo[offset], dataToSign, digestLen);
+ digestInfoLength = offset + digestLen;
+
+ if (mode == kSecKeyDigestInfoSign) {
+ return key->key_class->rawSign(key, kSecPaddingPKCS1,
+ digestInfo, digestInfoLength, sig, sigLen);
+ } else {
+ return key->key_class->rawVerify(key, kSecPaddingPKCS1,
+ digestInfo, digestInfoLength, sig, *sigLen);
+ }
+
+ return errSecSuccess;
+}
+
+OSStatus SecKeyRawSign(
+ SecKeyRef key, /* Private key */
+ SecPadding padding, /* kSecPaddingNone or kSecPaddingPKCS1 */
+ const uint8_t *dataToSign, /* signature over this data */
+ size_t dataToSignLen, /* length of dataToSign */
+ uint8_t *sig, /* signature, RETURNED */
+ size_t *sigLen) { /* IN/OUT */
+ if (!key->key_class->rawSign)
+ return errSecUnsupportedOperation;
+
+ if (padding < kSecPaddingPKCS1MD2) {
+ return key->key_class->rawSign(key, padding, dataToSign, dataToSignLen,
+ sig, sigLen);
+ } else {
+ return SecKeyDigestInfoSignVerify(key, padding, dataToSign, dataToSignLen,
+ sig, sigLen, kSecKeyDigestInfoSign);
+ }
+}
+
+OSStatus SecKeyRawVerify(
+ SecKeyRef key, /* Public key */
+ SecPadding padding, /* kSecPaddingNone or kSecPaddingPKCS1 */
+ const uint8_t *signedData, /* signature over this data */
+ size_t signedDataLen, /* length of dataToSign */
+ const uint8_t *sig, /* signature */
+ size_t sigLen) { /* length of signature */
+ if (!key->key_class->rawVerify)
+ return errSecUnsupportedOperation;
+
+ if (padding < kSecPaddingPKCS1MD2) {
+ return key->key_class->rawVerify(key, padding, signedData, signedDataLen,
+ sig, sigLen);
+ } else {
+ /* Casting away the constness of sig is safe since
+ SecKeyDigestInfoSignVerify only modifies sig if
+ mode == kSecKeyDigestInfoSign. */
+ return SecKeyDigestInfoSignVerify(key, padding,
+ signedData, signedDataLen, (uint8_t *)sig, &sigLen,
+ kSecKeyDigestInfoVerify);
+ }
+}
+
+OSStatus SecKeyEncrypt(
+ SecKeyRef key, /* Public key */
+ SecPadding padding, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */
+ const uint8_t *plainText,
+ size_t plainTextLen, /* length of plainText */
+ uint8_t *cipherText,
+ size_t *cipherTextLen) { /* IN/OUT */
+ if (key->key_class->encrypt)
+ return key->key_class->encrypt(key, padding, plainText, plainTextLen,
+ cipherText, cipherTextLen);
+ return errSecUnsupportedOperation;
+}
+
+OSStatus SecKeyDecrypt(
+ SecKeyRef key, /* Private key */
+ SecPadding padding, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */
+ const uint8_t *cipherText,
+ size_t cipherTextLen, /* length of cipherText */
+ uint8_t *plainText,
+ size_t *plainTextLen) { /* IN/OUT */
+ if (key->key_class->decrypt)
+ return key->key_class->decrypt(key, padding, cipherText, cipherTextLen,
+ plainText, plainTextLen);
+ return errSecUnsupportedOperation;
+}
+
+size_t SecKeyGetBlockSize(SecKeyRef key) {
+ if (key->key_class->blockSize)
+ return key->key_class->blockSize(key);
+ return 0;
+}
+
+/* Private API functions. */
+
+CFDictionaryRef SecKeyCopyAttributeDictionary(SecKeyRef key) {
+ if (key->key_class->copyDictionary)
+ return key->key_class->copyDictionary(key);
+ return NULL;
+}
+
+SecKeyRef SecKeyCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
+ /* TODO: Support having an allocator in refAttributes. */
+ CFAllocatorRef allocator = NULL;
+ CFDataRef data = CFDictionaryGetValue(refAttributes, kSecValueData);
+ CFTypeRef ktype = CFDictionaryGetValue(refAttributes, kSecAttrKeyType);
+ SInt32 algorithm;
+ SecKeyRef ref;
+
+ /* First figure out the key type (algorithm). */
+ if (CFGetTypeID(ktype) == CFNumberGetTypeID()) {
+ CFNumberGetValue(ktype, kCFNumberSInt32Type, &algorithm);
+ } else if (isString(ktype)) {
+ algorithm = CFStringGetIntValue(ktype);
+ CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) algorithm);
+ if (!CFEqual(t, ktype)) {
+ secwarning("Unsupported key class: %@", ktype);
+ CFReleaseSafe(t);
+ return NULL;
+ }
+ CFReleaseSafe(t);
+ } else {
+ secwarning("Unsupported key type: %@", ktype);
+ return NULL;
+ }
+
+ /* TODO: The code below won't scale well, consider moving to something
+ table driven. */
+ SInt32 class;
+ CFTypeRef kclass = CFDictionaryGetValue(refAttributes, kSecAttrKeyClass);
+ if (CFGetTypeID(kclass) == CFNumberGetTypeID()) {
+ CFNumberGetValue(kclass, kCFNumberSInt32Type, &class);
+ } else if (isString(kclass)) {
+ class = CFStringGetIntValue(kclass);
+ CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) class);
+ if (!CFEqual(t, kclass)) {
+ CFReleaseSafe(t);
+ secwarning("Unsupported key class: %@", kclass);
+ return NULL;
+ }
+ CFReleaseSafe(t);
+ } else {
+ secwarning("Unsupported key class: %@", kclass);
+ return NULL;
+ }
+
+ switch (class) {
+ case 0: // kSecAttrKeyClassPublic
+ switch (algorithm) {
+ case 42: // kSecAlgorithmRSA
+ ref = SecKeyCreateRSAPublicKey(allocator,
+ CFDataGetBytePtr(data), CFDataGetLength(data),
+ kSecKeyEncodingBytes);
+ break;
+ case 43: // kSecAlgorithmECDSA
+ case 73: // kSecAlgorithmEC
+ ref = SecKeyCreateECPublicKey(allocator,
+ CFDataGetBytePtr(data), CFDataGetLength(data),
+ kSecKeyEncodingBytes);
+ break;
+ default:
+ secwarning("Unsupported public key type: %@", ktype);
+ ref = NULL;
+ break;
+ };
+ break;
+ case 1: // kSecAttrKeyClassPrivate
+ if (CFDictionaryGetValue(refAttributes, kSecAttrTokenID) != NULL) {
+ ref = SecKeyCreateCTKKey(allocator, refAttributes);
+ break;
+ }
+ switch (algorithm) {
+ case 42: // kSecAlgorithmRSA
+ ref = SecKeyCreateRSAPrivateKey(allocator,
+ CFDataGetBytePtr(data), CFDataGetLength(data),
+ kSecKeyEncodingBytes);
+ break;
+ case 43: // kSecAlgorithmECDSA
+ case 73: // kSecAlgorithmEC
+ ref = SecKeyCreateECPrivateKey(allocator,
+ CFDataGetBytePtr(data), CFDataGetLength(data),
+ kSecKeyEncodingBytes);
+ break;
+ default:
+ secwarning("Unsupported private key type: %@", ktype);
+ ref = NULL;
+ break;
+ };
+ break;
+ case 2: // kSecAttrKeyClassSymmetric
+ secwarning("Unsupported symmetric key type: %@", ktype);
+ ref = NULL;
+ break;
+ default:
+ secwarning("Unsupported key class: %@", kclass);
+ ref = NULL;
+ }
+
+ return ref;
+}
+
+/* TODO: This function should ensure that this keys algorithm matches the
+ signature algorithm. */
+static OSStatus SecKeyGetDigestInfo(SecKeyRef this, const SecAsn1AlgId *algId,
+ const uint8_t *data, size_t dataLen, bool digestData,
+ uint8_t *digestInfo, size_t *digestInfoLen /* IN/OUT */) {
+ unsigned char *(*digestFcn)(const void *, CC_LONG, unsigned char *);
+ CFIndex keyAlgID = kSecNullAlgorithmID;
+ const SecAsn1Oid *digestOid;
+ size_t digestLen;
+ size_t offset = 0;
+
+ /* Since these oids all have the same prefix, use switch. */
+ if ((algId->algorithm.Length == CSSMOID_RSA.Length) &&
+ !memcmp(algId->algorithm.Data, CSSMOID_RSA.Data,
+ algId->algorithm.Length - 1)) {
+ keyAlgID = kSecRSAAlgorithmID;
+ switch (algId->algorithm.Data[algId->algorithm.Length - 1]) {
+#if 0
+ case 2: /* oidMD2WithRSA */
+ digestFcn = CC_MD2;
+ digestLen = CC_MD2_DIGEST_LENGTH;
+ digestOid = &CSSMOID_MD2;
+ break;
+ case 3: /* oidMD4WithRSA */
+ digestFcn = CC_MD4;
+ digestLen = CC_MD4_DIGEST_LENGTH;
+ digestOid = &CSSMOID_MD4;
+ break;
+ case 4: /* oidMD5WithRSA */
+ digestFcn = CC_MD5;
+ digestLen = CC_MD5_DIGEST_LENGTH;
+ digestOid = &CSSMOID_MD5;
+ break;
+#endif /* 0 */
+ case 5: /* oidSHA1WithRSA */
+ digestFcn = CC_SHA1;
+ digestLen = CC_SHA1_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA1;
+ break;
+ case 11: /* oidSHA256WithRSA */
+ digestFcn = CC_SHA256;
+ digestLen = CC_SHA256_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA256;
+ break;
+ case 12: /* oidSHA384WithRSA */
+ /* pkcs1 12 */
+ digestFcn = CC_SHA384;
+ digestLen = CC_SHA384_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA384;
+ break;
+ case 13: /* oidSHA512WithRSA */
+ digestFcn = CC_SHA512;
+ digestLen = CC_SHA512_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA512;
+ break;
+ case 14: /* oidSHA224WithRSA */
+ digestFcn = CC_SHA224;
+ digestLen = CC_SHA224_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA224;
+ break;
+ default:
+ secdebug("key", "unsupported rsa signature algorithm");
+ return errSecUnsupportedAlgorithm;
+ }
+ } else if ((algId->algorithm.Length == CSSMOID_ECDSA_WithSHA224.Length) &&
+ !memcmp(algId->algorithm.Data, CSSMOID_ECDSA_WithSHA224.Data,
+ algId->algorithm.Length - 1)) {
+ keyAlgID = kSecECDSAAlgorithmID;
+ switch (algId->algorithm.Data[algId->algorithm.Length - 1]) {
+ case 1: /* oidSHA224WithECDSA */
+ digestFcn = CC_SHA224;
+ digestLen = CC_SHA224_DIGEST_LENGTH;
+ break;
+ case 2: /* oidSHA256WithECDSA */
+ digestFcn = CC_SHA256;
+ digestLen = CC_SHA256_DIGEST_LENGTH;
+ break;
+ case 3: /* oidSHA384WithECDSA */
+ /* pkcs1 12 */
+ digestFcn = CC_SHA384;
+ digestLen = CC_SHA384_DIGEST_LENGTH;
+ break;
+ case 4: /* oidSHA512WithECDSA */
+ digestFcn = CC_SHA512;
+ digestLen = CC_SHA512_DIGEST_LENGTH;
+ break;
+ default:
+ secdebug("key", "unsupported ecdsa signature algorithm");
+ return errSecUnsupportedAlgorithm;
+ }
+ } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_ECDSA_WithSHA1)) {
+ keyAlgID = kSecECDSAAlgorithmID;
+ digestFcn = CC_SHA1;
+ digestLen = CC_SHA1_DIGEST_LENGTH;
+ } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_SHA1)) {
+ digestFcn = CC_SHA1;
+ digestLen = CC_SHA1_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA1;
+ } else if ((algId->algorithm.Length == CSSMOID_SHA224.Length) &&
+ !memcmp(algId->algorithm.Data, CSSMOID_SHA224.Data, algId->algorithm.Length - 1))
+ {
+ switch (algId->algorithm.Data[algId->algorithm.Length - 1]) {
+ case 4: /* OID_SHA224 */
+ digestFcn = CC_SHA224;
+ digestLen = CC_SHA224_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA224;
+ break;
+ case 1: /* OID_SHA256 */
+ digestFcn = CC_SHA256;
+ digestLen = CC_SHA256_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA256;
+ break;
+ case 2: /* OID_SHA384 */
+ /* pkcs1 12 */
+ digestFcn = CC_SHA384;
+ digestLen = CC_SHA384_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA384;
+ break;
+ case 3: /* OID_SHA512 */
+ digestFcn = CC_SHA512;
+ digestLen = CC_SHA512_DIGEST_LENGTH;
+ digestOid = &CSSMOID_SHA512;
+ break;
+ default:
+ secdebug("key", "unsupported sha-2 signature algorithm");
+ return errSecUnsupportedAlgorithm;
+ }
+ } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_MD5)) {
+ digestFcn = CC_MD5;
+ digestLen = CC_MD5_DIGEST_LENGTH;
+ digestOid = &CSSMOID_MD5;
+ } else {
+ secdebug("key", "unsupported digesting algorithm");
+ return errSecUnsupportedAlgorithm;
+ }
+
+ /* check key is appropriate for signature (superfluous for digest only oid) */
+ if (keyAlgID == kSecNullAlgorithmID)
+ keyAlgID = SecKeyGetAlgorithmIdentifier(this);
+ else if (keyAlgID != SecKeyGetAlgorithmIdentifier(this))
+ return errSecUnsupportedAlgorithm;
+
+ switch(keyAlgID) {
+ case kSecRSAAlgorithmID:
+ offset = DEREncodeDigestInfoPrefix(digestOid, digestLen,
+ digestInfo, *digestInfoLen);
+ if (!offset)
+ return errSecBufferTooSmall;
+ break;
+ case kSecDSAAlgorithmID:
+ if (digestOid != &CSSMOID_SHA1)
+ return errSecUnsupportedAlgorithm;
+ break;
+ case kSecECDSAAlgorithmID:
+ break;
+ default:
+ secdebug("key", "unsupported signature algorithm");
+ return errSecUnsupportedAlgorithm;
+ }
+
+ if (digestData) {
+ if(dataLen>UINT32_MAX) /* Check for overflow with CC_LONG cast */
+ return errSecParam;
+ digestFcn(data, (CC_LONG)dataLen, &digestInfo[offset]);
+ *digestInfoLen = offset + digestLen;
+ } else {
+ if (dataLen != digestLen)
+ return errSecParam;
+ memcpy(&digestInfo[offset], data, dataLen);
+ *digestInfoLen = offset + dataLen;
+ }
+
+ return errSecSuccess;
+}
+
+OSStatus SecKeyDigestAndVerify(
+ SecKeyRef this, /* Private key */
+ const SecAsn1AlgId *algId, /* algorithm oid/params */
+ const uint8_t *dataToDigest, /* signature over this data */
+ size_t dataToDigestLen,/* length of dataToDigest */
+ const uint8_t *sig, /* signature to verify */
+ size_t sigLen) { /* length of sig */
+ size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN;
+ uint8_t digestInfo[digestInfoLength];
+ OSStatus status;
+
+ if (this == NULL)
+ return errSecParam;
+
+ status = SecKeyGetDigestInfo(this, algId, dataToDigest, dataToDigestLen, true,
+ digestInfo, &digestInfoLength);
+ if (status)
+ return status;
+ return SecKeyRawVerify(this, kSecPaddingPKCS1,
+ digestInfo, digestInfoLength, sig, sigLen);
+}
+
+OSStatus SecKeyDigestAndSign(
+ SecKeyRef this, /* Private key */
+ const SecAsn1AlgId *algId, /* algorithm oid/params */
+ const uint8_t *dataToDigest, /* signature over this data */
+ size_t dataToDigestLen,/* length of dataToDigest */
+ uint8_t *sig, /* signature, RETURNED */
+ size_t *sigLen) { /* IN/OUT */
+ size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN;
+ uint8_t digestInfo[digestInfoLength];
+ OSStatus status;
+
+ status = SecKeyGetDigestInfo(this, algId, dataToDigest, dataToDigestLen, true /* digest data */,
+ digestInfo, &digestInfoLength);
+ if (status)
+ return status;
+ return SecKeyRawSign(this, kSecPaddingPKCS1,
+ digestInfo, digestInfoLength, sig, sigLen);
+}
+
+OSStatus SecKeyVerifyDigest(
+ SecKeyRef this, /* Private key */
+ const SecAsn1AlgId *algId, /* algorithm oid/params */
+ const uint8_t *digestData, /* signature over this digest */
+ size_t digestDataLen,/* length of dataToDigest */
+ const uint8_t *sig, /* signature to verify */
+ size_t sigLen) { /* length of sig */
+ size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN;
+ uint8_t digestInfo[digestInfoLength];
+ OSStatus status;
+
+ status = SecKeyGetDigestInfo(this, algId, digestData, digestDataLen, false /* data is digest */,
+ digestInfo, &digestInfoLength);
+ if (status)
+ return status;
+ return SecKeyRawVerify(this, kSecPaddingPKCS1,
+ digestInfo, digestInfoLength, sig, sigLen);
+}
+
+OSStatus SecKeySignDigest(
+ SecKeyRef this, /* Private key */
+ const SecAsn1AlgId *algId, /* algorithm oid/params */
+ const uint8_t *digestData, /* signature over this digest */
+ size_t digestDataLen,/* length of digestData */
+ uint8_t *sig, /* signature, RETURNED */
+ size_t *sigLen) { /* IN/OUT */
+ size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN;
+ uint8_t digestInfo[digestInfoLength];
+ OSStatus status;
+
+ status = SecKeyGetDigestInfo(this, algId, digestData, digestDataLen, false,
+ digestInfo, &digestInfoLength);
+ if (status)
+ return status;
+ return SecKeyRawSign(this, kSecPaddingPKCS1,
+ digestInfo, digestInfoLength, sig, sigLen);
+}
+
+CFIndex SecKeyGetAlgorithmId(SecKeyRef key) {
+ return SecKeyGetAlgorithmIdentifier(key);
+}
+
+#if (TARGET_OS_MAC && !(TARGET_OS_EMBEDDED || TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR))
+/* On OS X, SecKeyGetAlgorithmID has a different function signature (two arguments,
+ with output in the second argument). Therefore, avoid implementing this function here
+ if compiling for OS X.
+ */
+#else
+CFIndex SecKeyGetAlgorithmID(SecKeyRef key) {
+ return SecKeyGetAlgorithmIdentifier(key);
+}
+#endif
+
+OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* serializedPublic) {
+ if (key->key_class->version > 1 && key->key_class->copyPublic)
+ return key->key_class->copyPublic(key, serializedPublic);
+ return errSecUnimplemented;
+}
+
+SecKeyRef SecKeyCreateFromPublicBytes(CFAllocatorRef allocator, CFIndex algorithmID, const uint8_t *keyData, CFIndex keyDataLength)
+{
+ switch (algorithmID)
+ {
+ case kSecRSAAlgorithmID:
+ return SecKeyCreateRSAPublicKey(allocator,
+ keyData, keyDataLength,
+ kSecKeyEncodingBytes);
+ case kSecECDSAAlgorithmID:
+ return SecKeyCreateECPublicKey(allocator,
+ keyData, keyDataLength,
+ kSecKeyEncodingBytes);
+ default:
+ return NULL;
+ }
+}
+
+SecKeyRef SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef serialized)
+{
+ return SecKeyCreateFromPublicBytes(allocator, algorithmID, CFDataGetBytePtr(serialized), CFDataGetLength(serialized));
+}
+
+// This is a bit icky hack to avoid changing the vtable for
+// SecKey.
+size_t SecKeyGetSize(SecKeyRef key, SecKeySize whichSize)
+{
+ size_t result = SecKeyGetBlockSize(key);
+
+ if (kSecECDSAAlgorithmID == SecKeyGetAlgorithmIdentifier(key)) {
+ switch (whichSize) {
+ case kSecKeyEncryptedDataSize:
+ result = 0;
+ break;
+ case kSecKeySignatureSize:
+ result = (result >= 66 ? 9 : 8) + 2 * result;
+ break;
+ case kSecKeyKeySizeInBits:
+ if (result >= 66)
+ return 521;
+ }
+ }
+
+ if (whichSize == kSecKeyKeySizeInBits)
+ result *= 8;
+
+ return result;
+
+}
+
+OSStatus SecKeyFindWithPersistentRef(CFDataRef persistentRef, SecKeyRef* lookedUpData)
+{
+ CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+ kSecReturnRef, kCFBooleanTrue,
+ kSecClass, kSecClassKey,
+ kSecValuePersistentRef, persistentRef,
+ NULL);
+ CFTypeRef foundRef = NULL;
+ OSStatus status = SecItemCopyMatching(query, &foundRef);
+
+ if (status == errSecSuccess) {
+ if (CFGetTypeID(foundRef) == SecKeyGetTypeID()) {
+ *lookedUpData = (SecKeyRef) foundRef;
+ foundRef = NULL;
+ status = errSecSuccess;
+ } else {
+ status = errSecItemNotFound;
+ }
+ }
+
+ CFReleaseSafe(foundRef);
+ CFReleaseSafe(query);
+
+ return status;
+}
+
+OSStatus SecKeyCopyPersistentRef(SecKeyRef key, CFDataRef* persistentRef)
+{
+ CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
+ kSecReturnPersistentRef, kCFBooleanTrue,
+ kSecValueRef, key,
+ kSecAttrSynchronizable, kSecAttrSynchronizableAny,
+ NULL);
+ CFTypeRef foundRef = NULL;
+ OSStatus status = SecItemCopyMatching(query, &foundRef);
+
+ if (status == errSecSuccess) {
+ if (CFGetTypeID(foundRef) == CFDataGetTypeID()) {
+ *persistentRef = foundRef;
+ foundRef = NULL;
+ } else {
+ status = errSecItemNotFound;
+ }
+ }
+
+ CFReleaseSafe(foundRef);
+ CFReleaseSafe(query);
+
+ return status;
+}
+
+/*
+ *
+ */
+
+#define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
+
+SEC_CONST_DECL(_kSecKeyWrapPGPSymAlg, "kSecKeyWrapPGPSymAlg");
+SEC_CONST_DECL(_kSecKeyWrapPGPFingerprint, "kSecKeyWrapPGPFingerprint");
+SEC_CONST_DECL(_kSecKeyWrapPGPWrapAlg, "kSecKeyWrapPGPWrapAlg");
+SEC_CONST_DECL(_kSecKeyWrapRFC6637Flags, "kSecKeyWrapPGPECFlags");
+SEC_CONST_DECL(_kSecKeyWrapRFC6637WrapDigestSHA256KekAES128, "kSecKeyWrapPGPECWrapDigestSHA256KekAES128");
+SEC_CONST_DECL(_kSecKeyWrapRFC6637WrapDigestSHA512KekAES256, "kSecKeyWrapPGPECWrapDigestSHA512KekAES256");
+
+#undef SEC_CONST_DECL
+
+CFDataRef
+_SecKeyCopyWrapKey(SecKeyRef key, SecKeyWrapType type, CFDataRef unwrappedKey, CFDictionaryRef parameters, CFDictionaryRef *outParam, CFErrorRef *error)
+{
+ if (error)
+ *error = NULL;
+ if (outParam)
+ *outParam = NULL;
+ if (key->key_class->version > 2 && key->key_class->copyWrapKey)
+ return key->key_class->copyWrapKey(key, type, unwrappedKey, parameters, outParam, error);
+ SecError(errSecUnsupportedOperation, error, CFSTR("No key wrap supported for key %@"), key);
+ return NULL;
+}
+
+CFDataRef
+_SecKeyCopyUnwrapKey(SecKeyRef key, SecKeyWrapType type, CFDataRef wrappedKey, CFDictionaryRef parameters, CFDictionaryRef *outParam, CFErrorRef *error)
+{
+ if (error)
+ *error = NULL;
+ if (outParam)
+ *outParam = NULL;
+ if (key->key_class->version > 2 && key->key_class->copyUnwrapKey)
+ return key->key_class->copyUnwrapKey(key, type, wrappedKey, parameters, outParam, error);
+
+ SecError(errSecUnsupportedOperation, error, CFSTR("No key unwrap for key %@"), key);
+ return NULL;
+}