]> git.saurik.com Git - apple/security.git/blobdiff - sec/Security/SecKey.c
Security-55163.44.tar.gz
[apple/security.git] / sec / Security / SecKey.c
diff --git a/sec/Security/SecKey.c b/sec/Security/SecKey.c
new file mode 100644 (file)
index 0000000..f09fadf
--- /dev/null
@@ -0,0 +1,946 @@
+/*
+ * Copyright (c) 2006-2011 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 "SecRSAKeyPriv.h"
+#include "SecECKey.h"
+#include "SecBasePriv.h"
+
+#include <CoreFoundation/CFNumber.h>
+#include <CoreFoundation/CFString.h>
+#include <MacErrors.h>
+#include <pthread.h>
+#include <string.h>
+#include <AssertMacros.h>
+#include <security_utilities/debugging.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>
+
+static pthread_once_t kSecKeyRegisterClass = PTHREAD_ONCE_INIT;
+static CFTypeID kSecKeyTypeID = _kCFRuntimeNotATypeID;
+
+/* Forward declartions of static functions. */
+static CFStringRef SecKeyDescribe(CFTypeRef cf);
+static void SecKeyDestroy(CFTypeRef cf);
+
+/* 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;
+}
+
+static struct ccrng_system_state ccrng_system_state_seckey;
+
+static void register_algs(void) {
+    ccrng_seckey = (struct ccrng_state *)&ccrng_system_state_seckey;
+    ccrng_system_init(&ccrng_system_state_seckey);
+}
+
+
+/*
+ */
+static CFDictionaryRef SecKeyGenerateAttributeDictionaryFor(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 SecKeyGenerateAttributeDictionaryFor(key, keyType, privateBlob);
+}
+
+CFDictionaryRef SecKeyGeneratePublicAttributeDictionary(SecKeyRef key, CFTypeRef keyType)
+{
+    return SecKeyGenerateAttributeDictionaryFor(key, keyType, NULL);
+}
+
+/*
+ */
+
+static CFStringRef SecKeyDescribe(CFTypeRef cf) {
+    SecKeyRef key = (SecKeyRef)cf;
+    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;
+}
+
+static void SecKeyRegisterClass(void) {
+       static const CFRuntimeClass kSecKeyClass = {
+               0,                                                                                                      /* version */
+               "SecKey",                                           /* class name */
+               NULL,                                                                                           /* init */
+               NULL,                                                                                           /* copy */
+               SecKeyDestroy,                                      /* dealloc */
+               SecKeyEqual,                                                                            /* equal */
+               NULL,                                                                                           /* hash */
+               NULL,                                                                                           /* copyFormattingDesc */
+               SecKeyDescribe                                      /* copyDebugDesc */
+       };
+
+    kSecKeyTypeID = _CFRuntimeRegisterClass(&kSecKeyClass);
+    register_algs();
+}
+
+/* Public API functions. */
+CFTypeID SecKeyGetTypeID(void) {
+    pthread_once(&kSecKeyRegisterClass, SecKeyRegisterClass);
+    return kSecKeyTypeID;
+}
+
+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 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;
+}
+
+/* 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);
+
+    require(ktype, errOut);
+
+    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(result = add_ref(pubKey, pubParams), errOut);
+    }
+    if (getBoolForKey(privParams, kSecAttrIsPermanent, false)) {
+        require_noerr(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 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 SecKeyCreate(CFAllocatorRef allocator,
+    const SecKeyDescriptor *key_class, const uint8_t *keyData,
+       CFIndex keyDataLength, SecKeyEncoding encoding) {
+       check(key_class);
+
+    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: %d", 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;
+#if 0
+    case kSecPaddingPKCS1SHA224:
+        digestLen = CC_SHA224_DIGEST_LENGTH;
+        digestOid = &CSSMOID_SHA224;
+        break;
+    case kSecPaddingPKCS1SHA256:
+        digestLen = CCSHA256_OUTPUT_SIZE;
+        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;
+#endif
+    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 noErr;
+}
+
+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 {
+               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 {
+               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
+                    ref = SecKeyCreateECPublicKey(allocator,
+                                                  CFDataGetBytePtr(data), CFDataGetLength(data),
+                                                  kSecKeyEncodingBytes);
+                    break;
+                default:
+                    secwarning("Unsupported public key type: %@", ktype);
+                    ref = NULL;
+                    break;
+            };
+            break;
+        case 1: // kSecAttrKeyClassPrivate
+            switch (algorithm) {
+                case 42: // kSecAlgorithmRSA
+                    ref = SecKeyCreateRSAPrivateKey(allocator,
+                                                    CFDataGetBytePtr(data), CFDataGetLength(data),
+                                                    kSecKeyEncodingBytes);
+                    break;
+                case 43: // kSecAlgorithmECDSA
+                    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 = SecKeyGetAlgorithmID(this);
+    else if (keyAlgID != SecKeyGetAlgorithmID(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 paramErr;
+        digestFcn(data, (CC_LONG)dataLen, &digestInfo[offset]);
+        *digestInfoLen = offset + digestLen;
+    } else {
+        if (dataLen != digestLen)
+            return paramErr;
+        memcpy(&digestInfo[offset], data, dataLen);
+        *digestInfoLen = offset + dataLen;
+    }
+
+    return noErr;
+}
+
+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;
+
+    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) {
+    /* 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;
+}
+
+
+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 == SecKeyGetAlgorithmID(key)) {
+        switch (whichSize) {
+            case kSecKeyEncryptedDataSize:
+                result = 0;
+                break;
+            case kSecKeySignatureSize:
+                result = 2 * result + 6;
+                if (result >= 66)
+                    result += 2;
+                break;
+            case kSecKeyKeySizeInBits:
+                if (result >= 66)
+                    return 521;
+        }
+    }
+            
+    if (whichSize == kSecKeyKeySizeInBits)
+            result *= 8;
+        
+    return result;
+
+}
+