]> git.saurik.com Git - apple/security.git/blobdiff - OSX/libsecurity_keychain/Security/SecKey.cpp
Security-57336.1.9.tar.gz
[apple/security.git] / OSX / libsecurity_keychain / Security / SecKey.cpp
diff --git a/OSX/libsecurity_keychain/Security/SecKey.cpp b/OSX/libsecurity_keychain/Security/SecKey.cpp
new file mode 100644 (file)
index 0000000..cd70368
--- /dev/null
@@ -0,0 +1,2288 @@
+/*
+ * Copyright (c) 2002-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@
+ */
+
+#include "SecKey.h"
+#include "SecKeyPriv.h"
+#include "SecItem.h"
+#include "SecItemPriv.h"
+#include <libDER/asn1Types.h>
+#include <libDER/DER_Encode.h>
+#include <libDER/DER_Decode.h>
+#include <libDER/DER_Keys.h>
+#include <Security/SecAsn1Types.h>
+#include <Security/SecAsn1Coder.h>
+#include <security_keychain/KeyItem.h>
+#include <CommonCrypto/CommonKeyDerivation.h>
+
+#include "SecBridge.h"
+
+#include <security_keychain/Access.h>
+#include <security_keychain/Keychains.h>
+#include <security_keychain/KeyItem.h>
+#include <string.h>
+#include <syslog.h>
+
+#include <security_cdsa_utils/cuCdsaUtils.h>
+#include <security_cdsa_client/wrapkey.h>
+
+#include "SecImportExportCrypto.h"
+
+/* Since there are currently two implementations of SecKey present,
+ * we need a specific function to return the registered type of the
+ * CFClass implementation, so we can determine which type we have.
+ */
+CFTypeID
+SecKeyGetCFClassTypeID(void)
+{
+       BEGIN_SECAPI
+
+       return gTypes().KeyItem.typeID;
+
+       END_SECAPI1(_kCFRuntimeNotATypeID)
+}
+
+CFTypeID
+SecKeyGetTypeID(void)
+{
+       return SecKeyGetCFClassTypeID();
+}
+
+static OSStatus SecKeyCreatePairInternal(
+       SecKeychainRef keychainRef,
+       CSSM_ALGORITHMS algorithm,
+       uint32 keySizeInBits,
+       CSSM_CC_HANDLE contextHandle,
+       CSSM_KEYUSE publicKeyUsage,
+       uint32 publicKeyAttr,
+       CSSM_KEYUSE privateKeyUsage,
+       uint32 privateKeyAttr,
+       SecAccessRef initialAccess,
+       SecKeyRef* publicKeyRef,
+       SecKeyRef* privateKeyRef)
+{
+       BEGIN_SECAPI
+
+       Keychain keychain = Keychain::optional(keychainRef);
+       SecPointer<Access> theAccess(initialAccess ? Access::required(initialAccess) : new Access("<key>"));
+       SecPointer<KeyItem> pubItem, privItem;
+
+    Mutex *keychainMutex = keychain->getKeychainMutex();
+    StLock<Mutex> _(*keychainMutex);
+
+       KeyItem::createPair(keychain,
+        algorithm,
+        keySizeInBits,
+        contextHandle,
+        publicKeyUsage,
+        publicKeyAttr,
+        privateKeyUsage,
+        privateKeyAttr,
+        theAccess,
+        pubItem,
+        privItem);
+
+       // Return the generated keys.
+       if (publicKeyRef)
+               *publicKeyRef = pubItem->handle();
+       if (privateKeyRef)
+               *privateKeyRef = privItem->handle();
+
+       END_SECAPI
+}
+
+OSStatus
+SecKeyCreatePair(
+       SecKeychainRef keychainRef,
+       CSSM_ALGORITHMS algorithm,
+       uint32 keySizeInBits,
+       CSSM_CC_HANDLE contextHandle,
+       CSSM_KEYUSE publicKeyUsage,
+       uint32 publicKeyAttr,
+       CSSM_KEYUSE privateKeyUsage,
+       uint32 privateKeyAttr,
+       SecAccessRef initialAccess,
+       SecKeyRef* publicKeyRef,
+       SecKeyRef* privateKeyRef)
+{
+    OSStatus result = SecKeyCreatePairInternal(keychainRef, algorithm, keySizeInBits, contextHandle, publicKeyUsage,
+                                               publicKeyAttr, privateKeyUsage, privateKeyAttr, initialAccess, publicKeyRef, privateKeyRef);
+
+    return result;
+}
+
+
+
+OSStatus
+SecKeyGetCSSMKey(SecKeyRef key, const CSSM_KEY **cssmKey)
+{
+       BEGIN_SECAPI
+
+       Required(cssmKey) = KeyItem::required(key)->key();
+
+       END_SECAPI
+}
+
+
+//
+// Private APIs
+//
+
+OSStatus
+SecKeyGetCSPHandle(SecKeyRef keyRef, CSSM_CSP_HANDLE *cspHandle)
+{
+    BEGIN_SECAPI
+
+       SecPointer<KeyItem> keyItem(KeyItem::required(keyRef));
+       Required(cspHandle) = keyItem->csp()->handle();
+
+       END_SECAPI
+}
+
+/* deprecated as of 10.8 */
+OSStatus
+SecKeyGetAlgorithmID(SecKeyRef keyRef, const CSSM_X509_ALGORITHM_IDENTIFIER **algid)
+{
+       BEGIN_SECAPI
+
+#if SECTRUST_OSX
+       if (!keyRef || (CFGetTypeID(keyRef) != SecKeyGetCFClassTypeID()))
+               return errSecParam;
+#endif
+       SecPointer<KeyItem> keyItem(KeyItem::required(keyRef));
+       Required(algid) = &keyItem->algorithmIdentifier();
+
+       END_SECAPI
+}
+
+/* new for 10.8 */
+CFIndex
+SecKeyGetAlgorithmId(SecKeyRef key)
+{
+#if SECTRUST_OSX
+       if (!key) {
+               return kSecNullAlgorithmID;
+       }
+       else if (CFGetTypeID(key) != SecKeyGetCFClassTypeID()) {
+               return SecKeyGetAlgorithmIdentifier(key);
+       }
+       // else fall through, we have a CSSM-based key
+#endif
+
+       const CSSM_KEY *cssmKey;
+
+       if (SecKeyGetCSSMKey(key, &cssmKey) != errSecSuccess)
+               return kSecNullAlgorithmID;
+
+       switch (cssmKey->KeyHeader.AlgorithmId) {
+               case CSSM_ALGID_RSA:
+                       return kSecRSAAlgorithmID;
+               case CSSM_ALGID_DSA:
+                       return kSecDSAAlgorithmID;
+               case CSSM_ALGID_ECDSA:
+                       return kSecECDSAAlgorithmID;
+               default:
+                       assert(0); /* other algorithms TBA */
+                       return kSecNullAlgorithmID;
+       }
+}
+
+OSStatus
+SecKeyGetStrengthInBits(SecKeyRef keyRef, const CSSM_X509_ALGORITHM_IDENTIFIER *algid, unsigned int *strength)
+{
+    BEGIN_SECAPI
+
+       SecPointer<KeyItem> keyItem(KeyItem::required(keyRef));
+       Required(strength) = keyItem->strengthInBits(algid);
+
+       END_SECAPI
+}
+
+OSStatus
+SecKeyGetCredentials(
+       SecKeyRef keyRef,
+       CSSM_ACL_AUTHORIZATION_TAG operation,
+       SecCredentialType credentialType,
+       const CSSM_ACCESS_CREDENTIALS **outCredentials)
+{
+       BEGIN_SECAPI
+
+       SecPointer<KeyItem> keyItem(KeyItem::required(keyRef));
+       Required(outCredentials) = keyItem->getCredentials(operation, credentialType);
+
+       END_SECAPI
+}
+
+OSStatus
+SecKeyImportPair(
+       SecKeychainRef keychainRef,
+       const CSSM_KEY *publicCssmKey,
+       const CSSM_KEY *privateCssmKey,
+       SecAccessRef initialAccess,
+       SecKeyRef* publicKey,
+       SecKeyRef* privateKey)
+{
+       BEGIN_SECAPI
+
+       Keychain keychain = Keychain::optional(keychainRef);
+       SecPointer<Access> theAccess(initialAccess ? Access::required(initialAccess) : new Access("<key>"));
+       SecPointer<KeyItem> pubItem, privItem;
+
+       KeyItem::importPair(keychain,
+               Required(publicCssmKey),
+               Required(privateCssmKey),
+        theAccess,
+        pubItem,
+        privItem);
+
+       // Return the generated keys.
+       if (publicKey)
+               *publicKey = pubItem->handle();
+       if (privateKey)
+               *privateKey = privItem->handle();
+
+       END_SECAPI
+}
+
+static OSStatus
+SecKeyGenerateWithAttributes(
+       SecKeychainAttributeList* attrList,
+       SecKeychainRef keychainRef,
+       CSSM_ALGORITHMS algorithm,
+       uint32 keySizeInBits,
+       CSSM_CC_HANDLE contextHandle,
+       CSSM_KEYUSE keyUsage,
+       uint32 keyAttr,
+       SecAccessRef initialAccess,
+       SecKeyRef* keyRef)
+{
+       BEGIN_SECAPI
+
+       Keychain keychain;
+       SecPointer<Access> theAccess;
+
+       if (keychainRef)
+               keychain = KeychainImpl::required(keychainRef);
+       if (initialAccess)
+               theAccess = Access::required(initialAccess);
+
+       SecPointer<KeyItem> item = KeyItem::generateWithAttributes(attrList,
+        keychain,
+        algorithm,
+        keySizeInBits,
+        contextHandle,
+        keyUsage,
+        keyAttr,
+        theAccess);
+
+       // Return the generated key.
+       if (keyRef)
+               *keyRef = item->handle();
+
+       END_SECAPI
+}
+
+OSStatus
+SecKeyGenerate(
+       SecKeychainRef keychainRef,
+       CSSM_ALGORITHMS algorithm,
+       uint32 keySizeInBits,
+       CSSM_CC_HANDLE contextHandle,
+       CSSM_KEYUSE keyUsage,
+       uint32 keyAttr,
+       SecAccessRef initialAccess,
+       SecKeyRef* keyRef)
+{
+       return SecKeyGenerateWithAttributes(NULL,
+               keychainRef, algorithm, keySizeInBits,
+               contextHandle, keyUsage, keyAttr,
+               initialAccess, keyRef);
+}
+
+
+/* new in 10.6 */
+/* Create a key from supplied data and parameters */
+SecKeyRef
+SecKeyCreate(CFAllocatorRef allocator,
+    const SecKeyDescriptor *keyClass,
+       const uint8_t *keyData,
+       CFIndex keyDataLength,
+       SecKeyEncoding encoding)
+{
+       SecKeyRef keyRef = NULL;
+    OSStatus __secapiresult;
+       try {
+               //FIXME: needs implementation
+
+               __secapiresult=errSecSuccess;
+       }
+       catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
+       catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
+       catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
+       catch (...) { __secapiresult=errSecInternalComponent; }
+       return keyRef;
+}
+
+/* new in 10.6 */
+/* Generate a floating key reference from a CSSM_KEY */
+OSStatus
+SecKeyCreateWithCSSMKey(const CSSM_KEY *cssmKey,
+    SecKeyRef *keyRef)
+{
+       BEGIN_SECAPI
+
+       Required(cssmKey);
+    if(cssmKey->KeyData.Length == 0){
+        MacOSError::throwMe(errSecInvalidAttributeKeyLength);
+    }
+    if(cssmKey->KeyData.Data == NULL){
+        MacOSError::throwMe(errSecInvalidPointer);
+    }
+       CssmClient::CSP csp(cssmKey->KeyHeader.CspId);
+       CssmClient::Key key(csp, *cssmKey);
+       KeyItem *item = new KeyItem(key);
+
+       // Return the generated key.
+       if (keyRef)
+               *keyRef = item->handle();
+
+       END_SECAPI
+}
+
+
+
+static u_int32_t ConvertCFStringToInteger(CFStringRef ref)
+{
+       if (ref == NULL)
+       {
+               return 0;
+       }
+
+       // figure out the size of the string
+       CFIndex numChars = CFStringGetMaximumSizeForEncoding(CFStringGetLength(ref), kCFStringEncodingUTF8);
+       char buffer[numChars];
+       if (!CFStringGetCString(ref, buffer, numChars, kCFStringEncodingUTF8))
+       {
+               MacOSError::throwMe(errSecParam);
+       }
+
+       return atoi(buffer);
+}
+
+
+
+static OSStatus CheckAlgorithmType(CFDictionaryRef parameters, CSSM_ALGORITHMS &algorithms)
+{
+       // figure out the algorithm to use
+       CFStringRef ktype = (CFStringRef) CFDictionaryGetValue(parameters, kSecAttrKeyType);
+       if (ktype == NULL)
+       {
+               return errSecParam;
+       }
+
+       if (CFEqual(ktype, kSecAttrKeyTypeRSA)) {
+               algorithms = CSSM_ALGID_RSA;
+               return errSecSuccess;
+       } else if(CFEqual(ktype, kSecAttrKeyTypeECDSA) ||
+                       CFEqual(ktype, kSecAttrKeyTypeEC)) {
+               algorithms = CSSM_ALGID_ECDSA;
+               return errSecSuccess;
+       } else if(CFEqual(ktype, kSecAttrKeyTypeAES)) {
+               algorithms = CSSM_ALGID_AES;
+               return errSecSuccess;
+       } else if(CFEqual(ktype, kSecAttrKeyType3DES)) {
+               algorithms = CSSM_ALGID_3DES;
+               return errSecSuccess;
+       } else {
+               return errSecUnsupportedAlgorithm;
+       }
+}
+
+
+
+static OSStatus GetKeySize(CFDictionaryRef parameters, CSSM_ALGORITHMS algorithms, uint32 &keySizeInBits)
+{
+
+    // get the key size and check it for validity
+    CFTypeRef ref = CFDictionaryGetValue(parameters, kSecAttrKeySizeInBits);
+
+    keySizeInBits = kSecDefaultKeySize;
+
+    CFTypeID bitSizeType = CFGetTypeID(ref);
+    if (bitSizeType == CFStringGetTypeID())
+        keySizeInBits = ConvertCFStringToInteger((CFStringRef) ref);
+    else if (bitSizeType == CFNumberGetTypeID())
+        CFNumberGetValue((CFNumberRef) ref, kCFNumberSInt32Type, &keySizeInBits);
+    else return errSecParam;
+
+
+    switch (algorithms) {
+    case CSSM_ALGID_ECDSA:
+        if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSecp256r1;
+        if(keySizeInBits == kSecp192r1 || keySizeInBits == kSecp256r1 || keySizeInBits == kSecp384r1 || keySizeInBits == kSecp521r1 ) return errSecSuccess;
+        break;
+    case CSSM_ALGID_RSA:
+                         if(keySizeInBits % 8) return errSecParam;
+        if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = 2048;
+        if(keySizeInBits >= kSecRSAMin && keySizeInBits <= kSecRSAMax) return errSecSuccess;
+        break;
+    case CSSM_ALGID_AES:
+        if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSecAES128;
+        if(keySizeInBits == kSecAES128 || keySizeInBits == kSecAES192 || keySizeInBits == kSecAES256) return errSecSuccess;
+        break;
+    case CSSM_ALGID_3DES:
+        if(keySizeInBits == kSecDefaultKeySize) keySizeInBits = kSec3DES192;
+        if(keySizeInBits == kSec3DES192) return errSecSuccess;
+        break;
+    default:
+        break;
+    }
+    return errSecParam;
+}
+
+
+
+enum AttributeType
+{
+       kStringType,
+       kBooleanType,
+       kIntegerType
+};
+
+
+
+struct ParameterAttribute
+{
+       const CFStringRef *name;
+       AttributeType type;
+};
+
+
+
+static ParameterAttribute gAttributes[] =
+{
+       {
+               &kSecAttrLabel,
+               kStringType
+       },
+       {
+               &kSecAttrIsPermanent,
+               kBooleanType
+       },
+       {
+               &kSecAttrApplicationTag,
+               kStringType
+       },
+       {
+               &kSecAttrEffectiveKeySize,
+               kBooleanType
+       },
+       {
+               &kSecAttrCanEncrypt,
+               kBooleanType
+       },
+       {
+               &kSecAttrCanDecrypt,
+               kBooleanType
+       },
+       {
+               &kSecAttrCanDerive,
+               kBooleanType
+       },
+       {
+               &kSecAttrCanSign,
+               kBooleanType
+       },
+       {
+               &kSecAttrCanVerify,
+               kBooleanType
+       },
+       {
+               &kSecAttrCanUnwrap,
+               kBooleanType
+       }
+};
+
+const int kNumberOfAttributes = sizeof(gAttributes) / sizeof(ParameterAttribute);
+
+static OSStatus ScanDictionaryForParameters(CFDictionaryRef parameters, void* attributePointers[])
+{
+       int i;
+       for (i = 0; i < kNumberOfAttributes; ++i)
+       {
+               // see if the corresponding tag exists in the dictionary
+               CFTypeRef value = CFDictionaryGetValue(parameters, *(gAttributes[i].name));
+               if (value != NULL)
+               {
+                       switch (gAttributes[i].type)
+                       {
+                               case kStringType:
+                                       // just return the value
+                                       *(CFTypeRef*) attributePointers[i] = value;
+                               break;
+
+                               case kBooleanType:
+                               {
+                                       CFBooleanRef bRef = (CFBooleanRef) value;
+                                       *(bool*) attributePointers[i] = CFBooleanGetValue(bRef);
+                               }
+                               break;
+
+                               case kIntegerType:
+                               {
+                                       CFNumberRef nRef = (CFNumberRef) value;
+                                       CFNumberGetValue(nRef, kCFNumberSInt32Type, attributePointers[i]);
+                               }
+                               break;
+                       }
+               }
+       }
+
+       return errSecSuccess;
+}
+
+
+
+static OSStatus GetKeyParameters(CFDictionaryRef parameters, int keySize, bool isPublic, CSSM_KEYUSE &keyUse, uint32 &attrs, CFTypeRef &labelRef, CFDataRef &applicationTagRef)
+{
+       // establish default values
+       labelRef = NULL;
+       bool isPermanent = false;
+       applicationTagRef = NULL;
+       CFTypeRef effectiveKeySize = NULL;
+       bool canDecrypt = isPublic ? false : true;
+       bool canEncrypt = !canDecrypt;
+       bool canDerive = true;
+       bool canSign = isPublic ? false : true;
+       bool canVerify = !canSign;
+       bool canUnwrap = isPublic ? false : true;
+       attrs = CSSM_KEYATTR_EXTRACTABLE;
+       keyUse = 0;
+
+       void* attributePointers[] = {&labelRef, &isPermanent, &applicationTagRef, &effectiveKeySize, &canEncrypt, &canDecrypt,
+                                                                &canDerive, &canSign, &canVerify, &canUnwrap};
+
+       // look for modifiers in the general dictionary
+       OSStatus result = ScanDictionaryForParameters(parameters, attributePointers);
+       if (result != errSecSuccess)
+       {
+               return result;
+       }
+
+       // see if we have anything which modifies the defaults
+       CFTypeRef key;
+       if (isPublic)
+       {
+               key = kSecPublicKeyAttrs;
+       }
+       else
+       {
+               key = kSecPrivateKeyAttrs;
+       }
+
+       CFTypeRef dType = CFDictionaryGetValue(parameters, key);
+       if (dType != NULL)
+       {
+               // this had better be a dictionary
+               if (CFGetTypeID(dType) != CFDictionaryGetTypeID())
+               {
+                       return errSecParam;
+               }
+
+               // pull any additional parameters out of this dictionary
+               result = ScanDictionaryForParameters((CFDictionaryRef)dType, attributePointers);
+               if (result != errSecSuccess)
+               {
+                       return result;
+               }
+       }
+
+       // figure out the key usage
+       keyUse = 0;
+       if (canDecrypt)
+       {
+               keyUse |= CSSM_KEYUSE_DECRYPT;
+       }
+
+       if (canEncrypt)
+       {
+               keyUse |= CSSM_KEYUSE_ENCRYPT;
+       }
+
+       if (canDerive)
+       {
+               keyUse |= CSSM_KEYUSE_DERIVE;
+       }
+
+       if (canSign)
+       {
+               keyUse |= CSSM_KEYUSE_SIGN;
+       }
+
+       if (canVerify)
+       {
+               keyUse |= CSSM_KEYUSE_VERIFY;
+       }
+
+       if (canUnwrap)
+       {
+               keyUse |= CSSM_KEYUSE_UNWRAP;
+       }
+
+       // public key is always extractable;
+       // private key is extractable by default unless explicitly set to false
+       CFTypeRef value = NULL;
+       if (!isPublic && CFDictionaryGetValueIfPresent(parameters, kSecAttrIsExtractable, (const void **)&value) && value)
+       {
+               Boolean keyIsExtractable = CFEqual(kCFBooleanTrue, value);
+               if (!keyIsExtractable)
+                       attrs = 0;
+       }
+
+       attrs |= CSSM_KEYATTR_PERMANENT;
+
+       return errSecSuccess;
+}
+
+
+
+static OSStatus MakeKeyGenParametersFromDictionary(CFDictionaryRef parameters,
+                                                                                                  CSSM_ALGORITHMS &algorithms,
+                                                                                                  uint32 &keySizeInBits,
+                                                                                                  CSSM_KEYUSE &publicKeyUse,
+                                                                                                  uint32 &publicKeyAttr,
+                                                                                                  CFTypeRef &publicKeyLabelRef,
+                                                                                                  CFDataRef &publicKeyAttributeTagRef,
+                                                                                                  CSSM_KEYUSE &privateKeyUse,
+                                                                                                  uint32 &privateKeyAttr,
+                                                                                                  CFTypeRef &privateKeyLabelRef,
+                                                                                                  CFDataRef &privateKeyAttributeTagRef,
+                                                                                                  SecAccessRef &initialAccess)
+{
+       OSStatus result;
+
+       result = CheckAlgorithmType(parameters, algorithms);
+       if (result != errSecSuccess)
+       {
+               return result;
+       }
+
+       result = GetKeySize(parameters, algorithms, keySizeInBits);
+       if (result != errSecSuccess)
+       {
+               return result;
+       }
+
+       result = GetKeyParameters(parameters, keySizeInBits, false, privateKeyUse, privateKeyAttr, privateKeyLabelRef, privateKeyAttributeTagRef);
+       if (result != errSecSuccess)
+       {
+               return result;
+       }
+
+       result = GetKeyParameters(parameters, keySizeInBits, true, publicKeyUse, publicKeyAttr, publicKeyLabelRef, publicKeyAttributeTagRef);
+       if (result != errSecSuccess)
+       {
+               return result;
+       }
+
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrAccess, (const void **)&initialAccess))
+       {
+               initialAccess = NULL;
+       }
+       else if (SecAccessGetTypeID() != CFGetTypeID(initialAccess))
+       {
+               return errSecParam;
+       }
+
+       return errSecSuccess;
+}
+
+
+
+static OSStatus SetKeyLabelAndTag(SecKeyRef keyRef, CFTypeRef label, CFDataRef tag)
+{
+       int numToModify = 0;
+       if (label != NULL)
+       {
+               numToModify += 1;
+       }
+
+       if (tag != NULL)
+       {
+               numToModify += 1;
+       }
+
+       if (numToModify == 0)
+       {
+               return errSecSuccess;
+       }
+
+       SecKeychainAttributeList attrList;
+       SecKeychainAttribute attributes[numToModify];
+
+       int i = 0;
+
+       if (label != NULL)
+       {
+               if (CFStringGetTypeID() == CFGetTypeID(label)) {
+                       CFStringRef label_string = static_cast<CFStringRef>(label);
+                       attributes[i].tag = kSecKeyPrintName;
+                       attributes[i].data = (void*) CFStringGetCStringPtr(label_string, kCFStringEncodingUTF8);
+                       if (NULL == attributes[i].data) {
+                               CFIndex buffer_length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(label_string), kCFStringEncodingUTF8);
+                               attributes[i].data = alloca((size_t)buffer_length);
+                               if (NULL == attributes[i].data) {
+                                       UnixError::throwMe(ENOMEM);
+                               }
+                               if (!CFStringGetCString(label_string, static_cast<char *>(attributes[i].data), buffer_length, kCFStringEncodingUTF8)) {
+                                       MacOSError::throwMe(errSecParam);
+                               }
+                       }
+                       attributes[i].length = (UInt32)strlen(static_cast<char *>(attributes[i].data));
+               } else if (CFDataGetTypeID() == CFGetTypeID(label)) {
+                       // 10.6 bug compatibility
+                       CFDataRef label_data = static_cast<CFDataRef>(label);
+                       attributes[i].tag = kSecKeyLabel;
+                       attributes[i].data = (void*) CFDataGetBytePtr(label_data);
+                       attributes[i].length = (UInt32)CFDataGetLength(label_data);
+               } else {
+                       MacOSError::throwMe(errSecParam);
+               }
+               i++;
+       }
+
+       if (tag != NULL)
+       {
+               attributes[i].tag = kSecKeyApplicationTag;
+               attributes[i].data = (void*) CFDataGetBytePtr(tag);
+               attributes[i].length = (UInt32)CFDataGetLength(tag);
+               i++;
+       }
+
+       attrList.count = numToModify;
+       attrList.attr = attributes;
+
+       return SecKeychainItemModifyAttributesAndData((SecKeychainItemRef) keyRef, &attrList, 0, NULL);
+}
+
+
+
+/* new in 10.6 */
+/* Generate a private/public keypair. */
+OSStatus
+SecKeyGeneratePair(
+       CFDictionaryRef parameters,
+       SecKeyRef *publicKey,
+       SecKeyRef *privateKey)
+{
+       BEGIN_SECAPI
+
+       Required(parameters);
+       Required(publicKey);
+       Required(privateKey);
+
+       CSSM_ALGORITHMS algorithms;
+       uint32 keySizeInBits;
+       CSSM_KEYUSE publicKeyUse;
+       uint32 publicKeyAttr;
+       CFTypeRef publicKeyLabelRef;
+       CFDataRef publicKeyAttributeTagRef;
+       CSSM_KEYUSE privateKeyUse;
+       uint32 privateKeyAttr;
+       CFTypeRef privateKeyLabelRef;
+       CFDataRef privateKeyAttributeTagRef;
+       SecAccessRef initialAccess;
+       SecKeychainRef keychain;
+
+       OSStatus result = MakeKeyGenParametersFromDictionary(parameters, algorithms, keySizeInBits, publicKeyUse, publicKeyAttr, publicKeyLabelRef,
+                                                                                                                publicKeyAttributeTagRef, privateKeyUse, privateKeyAttr, privateKeyLabelRef, privateKeyAttributeTagRef,
+                                                                                                                initialAccess);
+
+       if (result != errSecSuccess)
+       {
+               return result;
+       }
+
+       // verify keychain parameter
+       keychain = NULL;
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecUseKeychain, (const void **)&keychain))
+               keychain = NULL;
+       else if (SecKeychainGetTypeID() != CFGetTypeID(keychain))
+               keychain = NULL;
+
+       // do the key generation
+       result = SecKeyCreatePair(keychain, algorithms, keySizeInBits, 0, publicKeyUse, publicKeyAttr, privateKeyUse, privateKeyAttr, initialAccess, publicKey, privateKey);
+       if (result != errSecSuccess)
+       {
+               return result;
+       }
+
+       // set the label and print attributes on the keys
+       SetKeyLabelAndTag(*publicKey, publicKeyLabelRef, publicKeyAttributeTagRef);
+       SetKeyLabelAndTag(*privateKey, privateKeyLabelRef, privateKeyAttributeTagRef);
+       return result;
+
+       END_SECAPI
+}
+
+/* new in 10.6 */
+OSStatus
+SecKeyRawSign(
+    SecKeyRef           key,
+       SecPadding          padding,
+       const uint8_t       *dataToSign,
+       size_t              dataToSignLen,
+       uint8_t             *sig,
+       size_t              *sigLen)
+{
+       BEGIN_SECAPI
+
+       Required(key);
+       SecPointer<KeyItem> keyItem(KeyItem::required(key));
+       CSSM_DATA dataInput;
+
+       dataInput.Data = (uint8_t*) dataToSign;
+       dataInput.Length = dataToSignLen;
+
+       CSSM_DATA output;
+       output.Data = sig;
+       output.Length = *sigLen;
+
+       const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault);
+
+       keyItem->RawSign(padding, dataInput, credentials, output);
+       *sigLen = output.Length;
+
+       END_SECAPI
+}
+
+OSStatus SecKeyRawVerifyOSX(
+    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)
+{
+    return SecKeyRawVerify(key,padding,signedData,signedDataLen,sig,sigLen);
+}
+
+/* new in 10.6 */
+OSStatus
+SecKeyRawVerify(
+    SecKeyRef           key,
+       SecPadding          padding,
+       const uint8_t       *signedData,
+       size_t              signedDataLen,
+       const uint8_t       *sig,
+       size_t              sigLen)
+{
+       BEGIN_SECAPI
+
+       Required(key);
+
+       SecPointer<KeyItem> keyItem(KeyItem::required(key));
+       CSSM_DATA dataInput;
+
+       dataInput.Data = (uint8_t*) signedData;
+       dataInput.Length = signedDataLen;
+
+       CSSM_DATA signature;
+       signature.Data = (uint8_t*) sig;
+       signature.Length = sigLen;
+
+       const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_ANY, kSecCredentialTypeDefault);
+
+       keyItem->RawVerify(padding, dataInput, credentials, signature);
+
+       END_SECAPI
+}
+
+/* new in 10.6 */
+OSStatus
+SecKeyEncrypt(
+    SecKeyRef           key,
+       SecPadding          padding,
+       const uint8_t           *plainText,
+       size_t              plainTextLen,
+       uint8_t             *cipherText,
+       size_t              *cipherTextLen)
+{
+       BEGIN_SECAPI
+
+       SecPointer<KeyItem> keyItem(KeyItem::required(key));
+       CSSM_DATA inData, outData;
+       inData.Data = (uint8*) plainText;
+       inData.Length = plainTextLen;
+       outData.Data = cipherText;
+       outData.Length = *cipherTextLen;
+
+       const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_ENCRYPT, kSecCredentialTypeDefault);
+
+       keyItem->Encrypt(padding, inData, credentials, outData);
+       *cipherTextLen = outData.Length;
+
+       END_SECAPI
+}
+
+/* new in 10.6 */
+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 */
+{
+       BEGIN_SECAPI
+
+       SecPointer<KeyItem> keyItem(KeyItem::required(key));
+       CSSM_DATA inData, outData;
+       inData.Data = (uint8*) cipherText;
+       inData.Length = cipherTextLen;
+       outData.Data = plainText;
+       outData.Length = *plainTextLen;
+
+       const AccessCredentials* credentials = keyItem->getCredentials(CSSM_ACL_AUTHORIZATION_DECRYPT, kSecCredentialTypeDefault);
+
+       keyItem->Decrypt(padding, inData, credentials, outData);
+       *plainTextLen = outData.Length;
+
+       END_SECAPI
+}
+
+/* new in 10.6 */
+size_t
+SecKeyGetBlockSize(SecKeyRef key)
+{
+       size_t blockSize = 0;
+    OSStatus __secapiresult;
+       try {
+               CSSM_KEY cssmKey = KeyItem::required(key)->key();
+               switch(cssmKey.KeyHeader.AlgorithmId)
+               {
+                       case CSSM_ALGID_RSA:
+                       case CSSM_ALGID_DSA:
+                               blockSize = cssmKey.KeyHeader.LogicalKeySizeInBits / 8;
+                               break;
+                       case CSSM_ALGID_ECDSA:
+                       {
+                               /* Block size is up to 9 bytes of DER encoding for sequence of 2 integers,
+                                * plus both coordinates for the point used */
+                               #define ECDSA_KEY_SIZE_IN_BYTES(bits) (((bits) + 7) / 8)
+                               #define ECDSA_MAX_COORD_SIZE_IN_BYTES(n) (ECDSA_KEY_SIZE_IN_BYTES(n) + 1)
+                               size_t coordSize = ECDSA_MAX_COORD_SIZE_IN_BYTES(cssmKey.KeyHeader.LogicalKeySizeInBits);
+                               assert(coordSize < 256); /* size must fit in a byte for DER */
+                               size_t coordDERLen = (coordSize > 127) ? 2 : 1;
+                               size_t coordLen = 1 + coordDERLen + coordSize;
+
+                               size_t pointSize = 2 * coordLen;
+                               assert(pointSize < 256); /* size must fit in a byte for DER */
+                               size_t pointDERLen = (pointSize > 127) ? 2 : 1;
+                               size_t pointLen = 1 + pointDERLen + pointSize;
+
+                               blockSize = pointLen;
+                       }
+                       break;
+                       case CSSM_ALGID_AES:
+                               blockSize = 16; /* all AES keys use 128-bit blocks */
+                               break;
+                       case CSSM_ALGID_DES:
+                       case CSSM_ALGID_3DES_3KEY:
+                               blockSize = 8; /* all DES keys use 64-bit blocks */
+                               break;
+                       default:
+                               assert(0); /* some other key algorithm */
+                               blockSize = 16; /* FIXME: revisit this */
+                               break;
+               }
+               __secapiresult=errSecSuccess;
+       }
+       catch (const MacOSError &err) { __secapiresult=err.osStatus(); }
+       catch (const CommonError &err) { __secapiresult=SecKeychainErrFromOSStatus(err.osStatus()); }
+       catch (const std::bad_alloc &) { __secapiresult=errSecAllocate; }
+       catch (...) { __secapiresult=errSecInternalComponent; }
+       return blockSize;
+}
+
+
+/*
+    M4 Additions
+*/
+
+static CFTypeRef
+utilGetStringFromCFDict(CFDictionaryRef parameters, CFTypeRef key, CFTypeRef defaultValue)
+{
+               CFTypeRef value = CFDictionaryGetValue(parameters, key);
+        if (value != NULL) return value;
+        return defaultValue;
+}
+
+static uint32_t
+utilGetNumberFromCFDict(CFDictionaryRef parameters, CFTypeRef key, uint32_t defaultValue)
+{
+        uint32_t integerValue;
+               CFTypeRef value = CFDictionaryGetValue(parameters, key);
+        if (value != NULL) {
+            CFNumberRef nRef = (CFNumberRef) value;
+            CFNumberGetValue(nRef, kCFNumberSInt32Type, &integerValue);
+            return integerValue;
+        }
+        return defaultValue;
+ }
+
+static uint32_t
+utilGetMaskValFromCFDict(CFDictionaryRef parameters, CFTypeRef key, uint32_t maskValue)
+{
+               CFTypeRef value = CFDictionaryGetValue(parameters, key);
+        if (value != NULL) {
+            CFBooleanRef bRef = (CFBooleanRef) value;
+            if(CFBooleanGetValue(bRef)) return maskValue;
+        }
+        return 0;
+}
+
+static void
+utilGetKeyParametersFromCFDict(CFDictionaryRef parameters, CSSM_ALGORITHMS *algorithm, uint32 *keySizeInBits, CSSM_KEYUSE *keyUsage, CSSM_KEYCLASS *keyClass)
+{
+    CFTypeRef algorithmDictValue = utilGetStringFromCFDict(parameters, kSecAttrKeyType, kSecAttrKeyTypeAES);
+    CFTypeRef keyClassDictValue = utilGetStringFromCFDict(parameters, kSecAttrKeyClass, kSecAttrKeyClassSymmetric);
+
+    if(CFEqual(algorithmDictValue, kSecAttrKeyTypeAES)) {
+        *algorithm = CSSM_ALGID_AES;
+        *keySizeInBits = 128;
+        *keyClass = CSSM_KEYCLASS_SESSION_KEY;
+    } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeDES)) {
+        *algorithm = CSSM_ALGID_DES;
+        *keySizeInBits = 128;
+         *keyClass = CSSM_KEYCLASS_SESSION_KEY;
+    } else if(CFEqual(algorithmDictValue, kSecAttrKeyType3DES)) {
+        *algorithm = CSSM_ALGID_3DES_3KEY_EDE;
+        *keySizeInBits = 128;
+        *keyClass = CSSM_KEYCLASS_SESSION_KEY;
+    } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRC4)) {
+        *algorithm = CSSM_ALGID_RC4;
+        *keySizeInBits = 128;
+        *keyClass = CSSM_KEYCLASS_SESSION_KEY;
+    } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRC2)) {
+        *algorithm = CSSM_ALGID_RC2;
+        *keySizeInBits = 128;
+         *keyClass = CSSM_KEYCLASS_SESSION_KEY;
+    } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeCAST)) {
+        *algorithm = CSSM_ALGID_CAST;
+        *keySizeInBits = 128;
+         *keyClass = CSSM_KEYCLASS_SESSION_KEY;
+    } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeRSA)) {
+        *algorithm = CSSM_ALGID_RSA;
+        *keySizeInBits = 128;
+         *keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
+    } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeDSA)) {
+        *algorithm = CSSM_ALGID_DSA;
+        *keySizeInBits = 128;
+         *keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
+    } else if(CFEqual(algorithmDictValue, kSecAttrKeyTypeECDSA) ||
+            CFEqual(algorithmDictValue, kSecAttrKeyTypeEC)) {
+        *algorithm = CSSM_ALGID_ECDSA;
+        *keySizeInBits = 128;
+        *keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
+    } else {
+        *algorithm = CSSM_ALGID_AES;
+        *keySizeInBits = 128;
+        *keyClass = CSSM_KEYCLASS_SESSION_KEY;
+    }
+
+    if(CFEqual(keyClassDictValue, kSecAttrKeyClassPublic)) {
+        *keyClass = CSSM_KEYCLASS_PUBLIC_KEY;
+    } else if(CFEqual(keyClassDictValue, kSecAttrKeyClassPrivate)) {
+        *keyClass = CSSM_KEYCLASS_PRIVATE_KEY;
+    } else if(CFEqual(keyClassDictValue, kSecAttrKeyClassSymmetric)) {
+         *keyClass = CSSM_KEYCLASS_SESSION_KEY;
+    }
+
+    *keySizeInBits = utilGetNumberFromCFDict(parameters, kSecAttrKeySizeInBits, *keySizeInBits);
+    *keyUsage =  utilGetMaskValFromCFDict(parameters, kSecAttrCanEncrypt, CSSM_KEYUSE_ENCRYPT) |
+                utilGetMaskValFromCFDict(parameters, kSecAttrCanDecrypt, CSSM_KEYUSE_DECRYPT) |
+                utilGetMaskValFromCFDict(parameters, kSecAttrCanWrap, CSSM_KEYUSE_WRAP) |
+                utilGetMaskValFromCFDict(parameters, kSecAttrCanUnwrap, CSSM_KEYUSE_UNWRAP);
+
+
+    if(*keyClass == CSSM_KEYCLASS_PRIVATE_KEY || *keyClass == CSSM_KEYCLASS_PUBLIC_KEY) {
+               *keyUsage |=  utilGetMaskValFromCFDict(parameters, kSecAttrCanSign, CSSM_KEYUSE_SIGN) |
+                                       utilGetMaskValFromCFDict(parameters, kSecAttrCanVerify, CSSM_KEYUSE_VERIFY);
+    }
+
+    if(*keyUsage == 0) {
+               switch (*keyClass) {
+                       case CSSM_KEYCLASS_PRIVATE_KEY:
+                               *keyUsage = CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_SIGN;
+                               break;
+                       case CSSM_KEYCLASS_PUBLIC_KEY:
+                               *keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_WRAP;
+                               break;
+                       default:
+                               *keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_WRAP | CSSM_KEYUSE_UNWRAP | CSSM_KEYUSE_SIGN | CSSM_KEYUSE_VERIFY;
+                               break;
+               }
+       }
+}
+
+static CFStringRef
+utilCopyDefaultKeyLabel(void)
+{
+       // generate a default label from the current date
+       CFDateRef dateNow = CFDateCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent());
+       CFStringRef defaultLabel = CFCopyDescription(dateNow);
+       CFRelease(dateNow);
+
+       return defaultLabel;
+}
+
+SecKeyRef
+SecKeyGenerateSymmetric(CFDictionaryRef parameters, CFErrorRef *error)
+{
+       OSStatus result = errSecParam; // default result for an early exit
+       SecKeyRef key = NULL;
+       SecKeychainRef keychain = NULL;
+       SecAccessRef access;
+       CFStringRef label;
+       CFStringRef appLabel;
+       CFStringRef appTag;
+       CFStringRef dateLabel = NULL;
+
+       CSSM_ALGORITHMS algorithm;
+       uint32 keySizeInBits;
+       CSSM_KEYUSE keyUsage;
+       uint32 keyAttr = CSSM_KEYATTR_RETURN_DEFAULT;
+       CSSM_KEYCLASS keyClass;
+       CFTypeRef value;
+       Boolean isPermanent;
+       Boolean isExtractable;
+
+       // verify keychain parameter
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecUseKeychain, (const void **)&keychain))
+               keychain = NULL;
+       else if (SecKeychainGetTypeID() != CFGetTypeID(keychain)) {
+               keychain = NULL;
+               goto errorExit;
+       }
+       else
+               CFRetain(keychain);
+
+       // verify permanent parameter
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrIsPermanent, (const void **)&value))
+               isPermanent = false;
+       else if (!value || (CFBooleanGetTypeID() != CFGetTypeID(value)))
+               goto errorExit;
+       else
+               isPermanent = CFEqual(kCFBooleanTrue, value);
+       if (isPermanent) {
+               if (keychain == NULL) {
+                       // no keychain was specified, so use the default keychain
+                       result = SecKeychainCopyDefault(&keychain);
+               }
+               keyAttr |= CSSM_KEYATTR_PERMANENT;
+       }
+
+       // verify extractable parameter
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrIsExtractable, (const void **)&value))
+               isExtractable = true; // default to extractable if value not specified
+       else if (!value || (CFBooleanGetTypeID() != CFGetTypeID(value)))
+               goto errorExit;
+       else
+               isExtractable = CFEqual(kCFBooleanTrue, value);
+       if (isExtractable)
+               keyAttr |= CSSM_KEYATTR_EXTRACTABLE;
+
+       // verify access parameter
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrAccess, (const void **)&access))
+               access = NULL;
+       else if (SecAccessGetTypeID() != CFGetTypeID(access))
+               goto errorExit;
+
+       // verify label parameter
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrLabel, (const void **)&label))
+               label = (dateLabel = utilCopyDefaultKeyLabel()); // no label provided, so use default
+       else if (CFStringGetTypeID() != CFGetTypeID(label))
+               goto errorExit;
+
+       // verify application label parameter
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrApplicationLabel, (const void **)&appLabel))
+               appLabel = (dateLabel) ? dateLabel : (dateLabel = utilCopyDefaultKeyLabel());
+       else if (CFStringGetTypeID() != CFGetTypeID(appLabel))
+               goto errorExit;
+
+       // verify application tag parameter
+       if (!CFDictionaryGetValueIfPresent(parameters, kSecAttrApplicationTag, (const void **)&appTag))
+               appTag = NULL;
+       else if (CFStringGetTypeID() != CFGetTypeID(appTag))
+               goto errorExit;
+
+    utilGetKeyParametersFromCFDict(parameters, &algorithm, &keySizeInBits, &keyUsage, &keyClass);
+
+       if (!keychain) {
+               // the generated key will not be stored in any keychain
+               result = SecKeyGenerate(keychain, algorithm, keySizeInBits, 0, keyUsage, keyAttr, access, &key);
+       }
+       else {
+               // we can set the label attributes on the generated key if it's a keychain item
+               size_t labelBufLen = (label) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(label), kCFStringEncodingUTF8) + 1 : 0;
+               char *labelBuf = (char *)malloc(labelBufLen);
+               size_t appLabelBufLen = (appLabel) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(appLabel), kCFStringEncodingUTF8) + 1 : 0;
+               char *appLabelBuf = (char *)malloc(appLabelBufLen);
+               size_t appTagBufLen = (appTag) ? (size_t)CFStringGetMaximumSizeForEncoding(CFStringGetLength(appTag), kCFStringEncodingUTF8) + 1 : 0;
+               char *appTagBuf = (char *)malloc(appTagBufLen);
+
+               if (label && !CFStringGetCString(label, labelBuf, labelBufLen-1, kCFStringEncodingUTF8))
+                       labelBuf[0]=0;
+               if (appLabel && !CFStringGetCString(appLabel, appLabelBuf, appLabelBufLen-1, kCFStringEncodingUTF8))
+                       appLabelBuf[0]=0;
+               if (appTag && !CFStringGetCString(appTag, appTagBuf, appTagBufLen-1, kCFStringEncodingUTF8))
+                       appTagBuf[0]=0;
+
+               SecKeychainAttribute attrs[] = {
+                       { kSecKeyPrintName, (UInt32)strlen(labelBuf), (char *)labelBuf },
+                       { kSecKeyLabel, (UInt32)strlen(appLabelBuf), (char *)appLabelBuf },
+                       { kSecKeyApplicationTag, (UInt32)strlen(appTagBuf), (char *)appTagBuf } };
+               SecKeychainAttributeList attributes = { sizeof(attrs) / sizeof(attrs[0]), attrs };
+               if (!appTag) --attributes.count;
+
+               result = SecKeyGenerateWithAttributes(&attributes,
+                       keychain, algorithm, keySizeInBits, 0,
+                       keyUsage, keyAttr, access, &key);
+
+               free(labelBuf);
+               free(appLabelBuf);
+               free(appTagBuf);
+       }
+
+errorExit:
+       if (result && error) {
+               *error = CFErrorCreate(kCFAllocatorDefault, kCFErrorDomainOSStatus, result, NULL);
+       }
+       if (dateLabel)
+               CFRelease(dateLabel);
+       if (keychain)
+               CFRelease(keychain);
+
+    return key;
+}
+
+
+
+SecKeyRef
+SecKeyCreateFromData(CFDictionaryRef parameters, CFDataRef keyData, CFErrorRef *error)
+{
+       CSSM_ALGORITHMS         algorithm;
+    uint32                             keySizeInBits;
+    CSSM_KEYUSE                        keyUsage;
+    CSSM_KEYCLASS              keyClass;
+    CSSM_RETURN                        crtn;
+
+    if(keyData == NULL || CFDataGetLength(keyData) == 0){
+        MacOSError::throwMe(errSecUnsupportedKeySize);
+    }
+
+    utilGetKeyParametersFromCFDict(parameters, &algorithm, &keySizeInBits, &keyUsage, &keyClass);
+
+       CSSM_CSP_HANDLE cspHandle = cuCspStartup(CSSM_FALSE); // TRUE => CSP, FALSE => CSPDL
+
+       SecKeyImportExportParameters iparam;
+       memset(&iparam, 0, sizeof(iparam));
+       iparam.keyUsage = keyUsage;
+
+       SecExternalItemType itype;
+       switch (keyClass) {
+               case CSSM_KEYCLASS_PRIVATE_KEY:
+                       itype = kSecItemTypePrivateKey;
+                       break;
+               case CSSM_KEYCLASS_PUBLIC_KEY:
+                       itype = kSecItemTypePublicKey;
+                       break;
+               case CSSM_KEYCLASS_SESSION_KEY:
+                       itype = kSecItemTypeSessionKey;
+                       break;
+               default:
+                       itype = kSecItemTypeUnknown;
+                       break;
+       }
+
+       CFMutableArrayRef ka = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
+       // NOTE: if we had a way to specify values other then kSecFormatUnknown we might be more useful.
+       crtn = impExpImportRawKey(keyData, kSecFormatUnknown, itype, algorithm, NULL, cspHandle, 0, NULL, NULL, ka);
+       if (crtn == CSSM_OK && CFArrayGetCount((CFArrayRef)ka)) {
+               SecKeyRef sk = (SecKeyRef)CFArrayGetValueAtIndex((CFArrayRef)ka, 0);
+               CFRetain(sk);
+               CFRelease(ka);
+               return sk;
+       } else {
+               if (error) {
+                       *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, crtn ? crtn : CSSM_ERRCODE_INTERNAL_ERROR, NULL);
+               }
+               return NULL;
+       }
+}
+
+
+void
+SecKeyGeneratePairAsync(CFDictionaryRef parametersWhichMightBeMutiable, dispatch_queue_t deliveryQueue,
+                                               SecKeyGeneratePairBlock result)
+{
+       CFDictionaryRef parameters = CFDictionaryCreateCopy(NULL, parametersWhichMightBeMutiable);
+       dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+               SecKeyRef publicKey = NULL;
+               SecKeyRef privateKey = NULL;
+               OSStatus status = SecKeyGeneratePair(parameters, &publicKey, &privateKey);
+               dispatch_async(deliveryQueue, ^{
+                       CFErrorRef error = NULL;
+                       if (errSecSuccess != status) {
+                               error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, status, NULL);
+                       }
+                       result(publicKey, privateKey, error);
+                       if (error) {
+                               CFRelease(error);
+                       }
+                       if (publicKey) {
+                               CFRelease(publicKey);
+                       }
+                       if (privateKey) {
+                               CFRelease(privateKey);
+                       }
+                       CFRelease(parameters);
+               });
+       });
+}
+
+static inline void utilClearAndFree(void *p, size_t len) {
+    if(p) {
+        if(len) bzero(p, len);
+        free(p);
+    }
+}
+
+SecKeyRef
+SecKeyDeriveFromPassword(CFStringRef password, CFDictionaryRef parameters, CFErrorRef *error)
+{
+    CCPBKDFAlgorithm algorithm;
+    CFIndex passwordLen = 0;
+    CFDataRef keyData = NULL;
+    char *thePassword = NULL;
+    uint8_t *salt = NULL;
+    uint8_t *derivedKey = NULL;
+    size_t  saltLen = 0, derivedKeyLen = 0;
+    uint rounds;
+    CFDataRef saltDictValue, algorithmDictValue;
+    SecKeyRef retval = NULL;
+
+    /* Pick Values from parameters */
+
+    if((saltDictValue = (CFDataRef) CFDictionaryGetValue(parameters, kSecAttrSalt)) == NULL) {
+        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecMissingAlgorithmParms, NULL);
+        goto errOut;
+    }
+
+    derivedKeyLen = utilGetNumberFromCFDict(parameters, kSecAttrKeySizeInBits, 128);
+       // This value come in bits but the rest of the code treats it as bytes
+       derivedKeyLen /= 8;
+
+    algorithmDictValue = (CFDataRef) utilGetStringFromCFDict(parameters, kSecAttrPRF, kSecAttrPRFHmacAlgSHA256);
+
+    rounds = utilGetNumberFromCFDict(parameters, kSecAttrRounds, 0);
+
+    /* Convert any remaining parameters and get the password bytes */
+
+    saltLen = CFDataGetLength(saltDictValue);
+    if((salt = (uint8_t *) malloc(saltLen)) == NULL) {
+        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL);
+        goto errOut;
+    }
+
+    CFDataGetBytes(saltDictValue, CFRangeMake(0, saltLen), (UInt8 *) salt);
+
+    passwordLen = CFStringGetMaximumSizeForEncoding(CFStringGetLength(password), kCFStringEncodingUTF8) + 1;
+    if((thePassword = (char *) malloc(passwordLen)) == NULL) {
+        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL);
+        goto errOut;
+    }
+    CFStringGetBytes(password, CFRangeMake(0, CFStringGetLength(password)), kCFStringEncodingUTF8, '?', FALSE, (UInt8*)thePassword, passwordLen, &passwordLen);
+
+    if((derivedKey = (uint8_t *) malloc(derivedKeyLen)) == NULL) {
+        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecAllocate, NULL);
+        goto errOut;
+    }
+
+    if(algorithmDictValue == NULL) {
+        algorithm = kCCPRFHmacAlgSHA1; /* default */
+    } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA1)) {
+        algorithm = kCCPRFHmacAlgSHA1;
+    } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA224)) {
+        algorithm = kCCPRFHmacAlgSHA224;
+    } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA256)) {
+        algorithm = kCCPRFHmacAlgSHA256;
+    } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA384)) {
+        algorithm = kCCPRFHmacAlgSHA384;
+    } else if(CFEqual(algorithmDictValue, kSecAttrPRFHmacAlgSHA512)) {
+        algorithm = kCCPRFHmacAlgSHA512;
+    } else {
+        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInvalidAlgorithmParms, NULL);
+        goto errOut;
+    }
+
+    if(rounds == 0) {
+        rounds = 33333; // we need to pass back a consistent value since there's no way to record the round count.
+    }
+
+    if(CCKeyDerivationPBKDF(kCCPBKDF2, thePassword, passwordLen, salt, saltLen, algorithm, rounds, derivedKey, derivedKeyLen)) {
+        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInternalError, NULL);
+        goto errOut;
+    }
+
+    if((keyData = CFDataCreate(NULL, derivedKey, derivedKeyLen)) != NULL) {
+        retval =  SecKeyCreateFromData(parameters, keyData, error);
+        CFRelease(keyData);
+    } else {
+        *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecInternalError, NULL);
+    }
+
+errOut:
+    utilClearAndFree(salt, saltLen);
+    utilClearAndFree(thePassword, passwordLen);
+    utilClearAndFree(derivedKey, derivedKeyLen);
+    return retval;
+}
+
+CFDataRef
+SecKeyWrapSymmetric(SecKeyRef keyToWrap, SecKeyRef wrappingKey, CFDictionaryRef parameters, CFErrorRef *error)
+{
+    *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecUnimplemented, NULL);
+    return NULL;
+}
+
+SecKeyRef
+SecKeyUnwrapSymmetric(CFDataRef *keyToUnwrap, SecKeyRef unwrappingKey, CFDictionaryRef parameters, CFErrorRef *error)
+{
+    *error = CFErrorCreate(NULL, kCFErrorDomainOSStatus, errSecUnimplemented, NULL);
+    return NULL;
+}
+
+
+/* iOS SecKey shim 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 OSStatus SecKeyGetDigestInfo(SecKeyRef key, 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) */
+    {
+        CFIndex supportedKeyAlgID = kSecNullAlgorithmID;
+    #if TARGET_OS_EMBEDDED
+        supportedKeyAlgID = SecKeyGetAlgorithmID(key);
+    #else
+        const CSSM_KEY* temporaryKey;
+        SecKeyGetCSSMKey(key, &temporaryKey);
+        CSSM_ALGORITHMS tempAlgorithm = temporaryKey->KeyHeader.AlgorithmId;
+        if (CSSM_ALGID_RSA == tempAlgorithm) {
+            supportedKeyAlgID = kSecRSAAlgorithmID;
+        } else if (CSSM_ALGID_ECDSA == tempAlgorithm) {
+            supportedKeyAlgID = kSecECDSAAlgorithmID;
+        }
+    #endif
+
+        if (keyAlgID == kSecNullAlgorithmID) {
+            keyAlgID = supportedKeyAlgID;
+        }
+        else if (keyAlgID != supportedKeyAlgID) {
+            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 SecKeyVerifyDigest(
+    SecKeyRef           key,            /* 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(key, algId, digestData, digestDataLen, false /* data is digest */,
+                                 digestInfo, &digestInfoLength);
+    if (status)
+        return status;
+    return SecKeyRawVerify(key, kSecPaddingPKCS1,
+                           digestInfo, digestInfoLength, sig, sigLen);
+}
+
+OSStatus SecKeySignDigest(
+    SecKeyRef           key,            /* 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(key, algId, digestData, digestDataLen, false,
+                                 digestInfo, &digestInfoLength);
+    if (status)
+        return status;
+    return SecKeyRawSign(key, kSecPaddingPKCS1,
+                         digestInfo, digestInfoLength, sig, sigLen);
+}
+
+/* It's debatable whether this belongs here or in the ssl code since the
+ curve values come from a tls related rfc4492. */
+SecECNamedCurve SecECKeyGetNamedCurve(SecKeyRef key)
+{
+    try {
+        SecPointer<KeyItem> keyItem(KeyItem::required(key));
+        switch (keyItem->key().header().LogicalKeySizeInBits) {
+#if 0
+            case 192:
+                return kSecECCurveSecp192r1;
+            case 224:
+                return kSecECCurveSecp224r1;
+#endif
+            case 256:
+                return kSecECCurveSecp256r1;
+            case 384:
+                return kSecECCurveSecp384r1;
+            case 521:
+                return kSecECCurveSecp521r1;
+        }
+    }
+    catch (...) {}
+    return kSecECCurveNone;
+}
+
+static inline CFDataRef _CFDataCreateReferenceFromRange(CFAllocatorRef allocator, CFDataRef sourceData, CFRange range)
+{
+    return CFDataCreateWithBytesNoCopy(allocator,
+                                       CFDataGetBytePtr(sourceData) + range.location, range.length,
+                                       kCFAllocatorNull);
+}
+
+static inline CFDataRef _CFDataCreateCopyFromRange(CFAllocatorRef allocator, CFDataRef sourceData, CFRange range)
+{
+    return CFDataCreate(allocator, CFDataGetBytePtr(sourceData) + range.location, range.length);
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+static inline bool _CFDataEquals(CFDataRef left, CFDataRef right)
+{
+    return (left != NULL) &&
+    (right != NULL) &&
+    (CFDataGetLength(left) == CFDataGetLength(right)) &&
+    (0 == memcmp(CFDataGetBytePtr(left), CFDataGetBytePtr(right), (size_t)CFDataGetLength(left)));
+}
+#pragma clang diagnostic pop
+
+#if ECDSA_DEBUG
+void secdump(const unsigned char *data, unsigned long len)
+{
+       unsigned long i;
+       char s[128];
+       char t[32];
+       s[0]=0;
+       for(i=0;i<len;i++)
+       {
+               if((i&0xf)==0) {
+                       sprintf(t, "%04lx :", i);
+                       strcat(s, t);
+               }
+               sprintf(t, " %02x", data[i]);
+               strcat(s, t);
+               if((i&0xf)==0xf) {
+                       strcat(s, "\n");
+                       syslog(LOG_NOTICE, s);
+                       s[0]=0;
+               }
+       }
+       strcat(s, "\n");
+       syslog(LOG_NOTICE, s);
+}
+#endif
+
+OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* publicBytes)
+{
+       CFIndex keyAlgId;
+#if TARGET_OS_EMBEDDED
+       keyAlgId = SecKeyGetAlgorithmID(key);
+#else
+       keyAlgId = SecKeyGetAlgorithmId(key);
+#endif
+
+       OSStatus ecStatus = errSecParam;
+       CFDataRef tempPublicData = NULL;
+       CFDataRef headerlessPublicData = NULL;
+       CFIndex headerLength = 0;
+    const UInt8* pData_Ptr = NULL;
+
+       if (kSecRSAAlgorithmID == keyAlgId)
+       {
+               return SecItemExport(key, kSecFormatBSAFE, 0, NULL, publicBytes);
+       }
+
+       if (kSecECDSAAlgorithmID == keyAlgId)
+       {
+               // First export the key so there is access to the underlying key material
+               ecStatus = SecItemExport(key, kSecFormatOpenSSL, 0, NULL, &tempPublicData);
+               if(ecStatus != errSecSuccess)
+               {
+                       secdebug("key", "SecKeyCopyPublicBytes: SecItemExport error (%d) for ECDSA public key %p",
+                                       ecStatus, (uintptr_t)key);
+
+               return ecStatus;
+        }
+
+
+        // Get a pointer to the first byte of the exported data
+        pData_Ptr = CFDataGetBytePtr(tempPublicData);
+
+        // the first byte should be a sequence 0x30
+        if (*pData_Ptr != 0x30)
+        {
+            secdebug("key", "SecKeyCopyPublicBytes: exported data is invalid");
+             if (NULL != tempPublicData)
+                   CFRelease(tempPublicData);
+
+                       ecStatus = errSecParam;
+               return ecStatus;
+        }
+
+        // move past the sequence byte
+        pData_Ptr++;
+
+        // Check to see if the high bit is set which
+        // indicates that the length will be at least
+        // two bytes.  If the high bit is set then
+        // The lower seven bits specifies the number of
+        // bytes used for the length.  The additonal 1
+        // is for the current byte. Otherwise just move past the
+        // single length byte
+        pData_Ptr += (*pData_Ptr & 0x80) ? ((*pData_Ptr & 0x7F) + 1) : 1;
+
+        // The current byte should be a sequence 0x30
+        if (*pData_Ptr != 0x30)
+        {
+            secdebug("key", "SecKeyCopyPublicBytes: Could not find the key sequence");
+            if (NULL != tempPublicData) {
+                CFRelease(tempPublicData);
+            }
+            ecStatus = errSecParam;
+            return ecStatus;
+        }
+
+        // The next bytes will always be the same
+        // 0x30 = SEQUENCE
+        // XX Length Byte
+        // 0x06 OBJECT ID
+        // 0x07 Length Byte
+        // ECDSA public KEY OID value 0x2a,0x86,0x48,0xce,0x3d,0x02,0x01
+        // 0x06 OBJECT ID
+        // This is a total of 12 bytes
+        pData_Ptr += 12;
+
+        // Next byte is the length of the ECDSA curve OID
+        // Move past the length byte and the curve OID
+        pData_Ptr += (((int)*pData_Ptr) + 1);
+
+        // Should be at a BINARY String which is specifed by a 0x3
+        if (*pData_Ptr != 0x03)
+        {
+            secdebug("key", "SecKeyCopyPublicBytes: Invalid key structure");
+            if (NULL != tempPublicData) {
+                CFRelease(tempPublicData);
+            }
+            ecStatus = errSecParam;
+            return ecStatus;
+        }
+
+        // Move past the BINARY String specifier 0x03
+        pData_Ptr++;
+
+
+        // Check to see if the high bit is set which
+        // indicates that the length will be at least
+        // two bytes.  If the high bit is set then
+        // The lower seven bits specifies the number of
+        // bytes used for the length.  The additonal 1
+        // is for the current byte. Otherwise just move past the
+        // single length byte
+        pData_Ptr += (*pData_Ptr & 0x80) ? ((*pData_Ptr & 0x7F) + 1) : 1;
+
+        // Move past the beginning marker for the BINARY String 0x00
+        pData_Ptr++;
+
+        // pData_Ptr now points to the first bytes of the key material
+        headerLength = (CFIndex)(((intptr_t)pData_Ptr) -  ((intptr_t)CFDataGetBytePtr(tempPublicData)));
+
+        headerlessPublicData = _CFDataCreateCopyFromRange(kCFAllocatorDefault,
+            tempPublicData, CFRangeMake(headerLength, CFDataGetLength(tempPublicData) - headerLength));
+
+        if (!headerlessPublicData)
+        {
+                       printf("SecKeyCopyPublicBytes: headerlessPublicData is nil (1)\n");
+                if (NULL != tempPublicData)
+                   CFRelease(tempPublicData);
+
+                       ecStatus = errSecParam;
+
+               return ecStatus;
+               }
+
+        if (publicBytes)
+        {
+               *publicBytes = headerlessPublicData;
+        }
+
+        ecStatus = errSecSuccess;
+
+        if (NULL != tempPublicData)
+            CFRelease(tempPublicData);
+
+        return ecStatus;
+      }
+
+  return errSecParam;
+}
+
+
+CFDataRef SecECKeyCopyPublicBits(SecKeyRef key)
+{
+    CFDataRef exportedKey;
+    if(SecKeyCopyPublicBytes(key, &exportedKey) != errSecSuccess) {
+        exportedKey = NULL;
+    }
+    return exportedKey;
+}
+
+SecKeyRef SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef publicBytes)
+{
+    SecExternalFormat externalFormat = kSecFormatOpenSSL;
+    SecExternalItemType externalItemType = kSecItemTypePublicKey;
+    CFDataRef workingData = NULL;
+    CFArrayRef outArray = NULL;
+    SecKeyRef retVal = NULL;
+
+    if (kSecRSAAlgorithmID == algorithmID) {
+               /*
+                * kSecFormatBSAFE uses the original PKCS#1 definition:
+                *     RSAPublicKey ::= SEQUENCE {
+                *        modulus           INTEGER,  -- n
+                *        publicExponent    INTEGER   -- e
+                *     }
+                * kSecFormatOpenSSL uses different ASN.1 encoding.
+                */
+               externalFormat = kSecFormatBSAFE;
+        workingData = _CFDataCreateReferenceFromRange(kCFAllocatorDefault, publicBytes, CFRangeMake(0, CFDataGetLength(publicBytes)));
+    } else if (kSecECDSAAlgorithmID == algorithmID) {
+        CFMutableDataRef tempData;
+        uint8 requiredFirstDERByte [] = {0x04};
+        uint8 placeholder[1];
+        uint8 headerBytes[] = { 0x30,0x59,0x30,0x13,0x06,0x07,0x2a,0x86,
+                                0x48,0xce,0x3d,0x02,0x01,0x06,0x08,0x2a,
+                                0x86,0x48,0xce,0x3d,0x03,0x01,0x07,0x03,
+                                0x42,0x00 };
+
+        /* FIXME: this code only handles one specific curve type; need to expand this */
+        if(CFDataGetLength(publicBytes) != 65)
+            goto cleanup;
+
+        CFDataGetBytes(publicBytes, CFRangeMake(0, 1), placeholder);
+
+        if(requiredFirstDERByte[0] != placeholder[0])
+            goto cleanup;
+
+
+        tempData = CFDataCreateMutable(kCFAllocatorDefault, 0);
+        CFDataAppendBytes(tempData, headerBytes, sizeof(headerBytes));
+        CFDataAppendBytes(tempData, CFDataGetBytePtr(publicBytes), CFDataGetLength(publicBytes));
+
+        workingData = tempData;
+    }
+    if(SecItemImport(workingData, NULL, &externalFormat, &externalItemType, 0, NULL, NULL, &outArray) != errSecSuccess) {
+               goto cleanup;
+    }
+       if(!outArray || CFArrayGetCount(outArray) == 0) {
+               goto cleanup;
+       }
+    retVal = (SecKeyRef)CFArrayGetValueAtIndex(outArray, 0);
+    CFRetain(retVal);
+
+cleanup:
+    if(workingData) CFRelease(workingData);
+    if(outArray) CFRelease(outArray);
+    return retVal;
+}
+
+SecKeyRef SecKeyCreateRSAPublicKey(CFAllocatorRef allocator,
+    const uint8_t *keyData, CFIndex keyDataLength,
+    SecKeyEncoding encoding)
+{
+       CFDataRef pubKeyData = NULL;
+    if(kSecKeyEncodingPkcs1 == encoding) {
+        /* DER-encoded according to PKCS1. */
+               pubKeyData = CFDataCreate(allocator, keyData, keyDataLength);
+
+    } else if(kSecKeyEncodingApplePkcs1 == encoding) {
+        /* DER-encoded according to PKCS1 with Apple Extensions. */
+               /* FIXME: need to actually handle extensions */
+               return NULL;
+
+    } else if(kSecKeyEncodingRSAPublicParams == encoding) {
+        /* SecRSAPublicKeyParams format; we must encode as PKCS1. */
+               SecRSAPublicKeyParams *params = (SecRSAPublicKeyParams *)keyData;
+               DERSize m_size = params->modulusLength;
+               DERSize e_size = params->exponentLength;
+               const DERSize seq_size = DERLengthOfItem(ASN1_INTEGER, m_size) +
+                       DERLengthOfItem(ASN1_INTEGER, e_size);
+               const DERSize result_size = DERLengthOfItem(ASN1_SEQUENCE, seq_size);
+               DERSize r_size, remaining_size = result_size;
+               DERReturn drtn;
+
+               CFMutableDataRef pkcs1 = CFDataCreateMutable(allocator, result_size);
+               if (pkcs1 == NULL) {
+                       return NULL;
+               }
+               CFDataSetLength(pkcs1, result_size);
+               uint8_t *bytes = CFDataGetMutableBytePtr(pkcs1);
+
+               *bytes++ = ASN1_CONSTR_SEQUENCE;
+               remaining_size--;
+               r_size = 4;
+               drtn = DEREncodeLength(seq_size, bytes, &r_size);
+               if (r_size <= remaining_size) {
+                       bytes += r_size;
+                       remaining_size -= r_size;
+               }
+               r_size = remaining_size;
+               drtn = DEREncodeItem(ASN1_INTEGER, m_size, (const DERByte *)params->modulus, (DERByte *)bytes, &r_size);
+               if (r_size <= remaining_size) {
+                       bytes += r_size;
+                       remaining_size -= r_size;
+               }
+               r_size = remaining_size;
+               drtn = DEREncodeItem(ASN1_INTEGER, e_size, (const DERByte *)params->exponent, (DERByte *)bytes, &r_size);
+
+               pubKeyData = pkcs1;
+
+    } else {
+        /* unsupported encoding */
+        return NULL;
+    }
+    SecKeyRef publicKey = SecKeyCreateFromPublicData(allocator, kSecRSAAlgorithmID, pubKeyData);
+    CFRelease(pubKeyData);
+    return publicKey;
+}
+
+#if !TARGET_OS_EMBEDDED
+//
+// Given a CSSM public key, copy its modulus and/or exponent data.
+// Caller is responsible for releasing the returned CFDataRefs.
+//
+static
+OSStatus _SecKeyCopyRSAPublicModulusAndExponent(SecKeyRef key, CFDataRef *modulus, CFDataRef *exponent)
+{
+       const CSSM_KEY          *pubKey;
+       const CSSM_KEYHEADER    *hdr;
+       CSSM_DATA               pubKeyBlob;
+       OSStatus                result;
+
+    result = SecKeyGetCSSMKey(key, &pubKey);
+       if(result != errSecSuccess) {
+               return result;
+       }
+       hdr = &pubKey->KeyHeader;
+       if(hdr->KeyClass != CSSM_KEYCLASS_PUBLIC_KEY) {
+               return errSSLInternal;
+       }
+       if(hdr->AlgorithmId != CSSM_ALGID_RSA) {
+               return errSSLInternal;
+       }
+       switch(hdr->BlobType) {
+               case CSSM_KEYBLOB_RAW:
+                       pubKeyBlob.Length = pubKey->KeyData.Length;
+                       pubKeyBlob.Data = pubKey->KeyData.Data;
+                       break;
+               case CSSM_KEYBLOB_REFERENCE:
+                       // FIXME: currently SSL only uses raw public keys, obtained from the CL
+               default:
+                       return errSSLInternal;
+       }
+       assert(hdr->BlobType == CSSM_KEYBLOB_RAW);
+       // at this point we should have a PKCS1-encoded blob
+
+    DERItem keyItem = {(DERByte *)pubKeyBlob.Data, pubKeyBlob.Length};
+    DERRSAPubKeyPKCS1 decodedKey;
+    if(DERParseSequence(&keyItem, DERNumRSAPubKeyPKCS1ItemSpecs,
+                        DERRSAPubKeyPKCS1ItemSpecs,
+                        &decodedKey, sizeof(decodedKey)) != DR_Success) {
+        return errSecDecode;
+    }
+    if(modulus) {
+        *modulus = CFDataCreate(kCFAllocatorDefault, decodedKey.modulus.data, decodedKey.modulus.length);
+        if(*modulus == NULL) {
+            return errSecDecode;
+        }
+    }
+    if(exponent) {
+        *exponent = CFDataCreate(kCFAllocatorDefault, decodedKey.pubExponent.data, decodedKey.pubExponent.length);
+        if(*exponent == NULL) {
+            return errSecDecode;
+        }
+    }
+
+    return errSecSuccess;
+}
+#endif /* !TARGET_OS_EMBEDDED */
+
+CFDataRef SecKeyCopyModulus(SecKeyRef key)
+{
+#if TARGET_OS_EMBEDDED
+    ccrsa_pub_ctx_t pubkey;
+    pubkey.pub = key->key;
+
+    size_t m_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey));
+
+       CFAllocatorRef allocator = CFGetAllocator(key);
+       CFMutableDataRef modulusData = CFDataCreateMutable(allocator, m_size);
+
+    if (modulusData == NULL)
+        return NULL;
+
+       CFDataSetLength(modulusData, m_size);
+
+    ccn_write_uint(ccrsa_ctx_n(pubkey), ccrsa_ctx_m(pubkey), m_size, CFDataGetMutableBytePtr(modulusData));
+#else
+    CFDataRef modulusData;
+    OSStatus status = _SecKeyCopyRSAPublicModulusAndExponent(key, &modulusData, NULL);
+    if(status != errSecSuccess) {
+        modulusData = NULL;
+    }
+#endif
+
+    return modulusData;
+}
+
+CFDataRef SecKeyCopyExponent(SecKeyRef key)
+{
+#if TARGET_OS_EMBEDDED
+    ccrsa_pub_ctx_t pubkey;
+    pubkey.pub = key->key;
+
+    size_t e_size = ccn_write_uint_size(ccrsa_ctx_n(pubkey), ccrsa_ctx_e(pubkey));
+
+       CFAllocatorRef allocator = CFGetAllocator(key);
+       CFMutableDataRef exponentData = CFDataCreateMutable(allocator, e_size);
+
+    if (exponentData == NULL)
+        return NULL;
+
+       CFDataSetLength(exponentData, e_size);
+
+    ccn_write_uint(ccrsa_ctx_n(pubkey), ccrsa_ctx_e(pubkey), e_size, CFDataGetMutableBytePtr(exponentData));
+#else
+    CFDataRef exponentData;
+    OSStatus status = _SecKeyCopyRSAPublicModulusAndExponent(key, NULL, &exponentData);
+    if(status != errSecSuccess) {
+        exponentData = NULL;
+    }
+#endif
+
+    return exponentData;
+}
+
+SecKeyRef SecKeyCreatePublicFromPrivate(SecKeyRef privateKey) {
+    OSStatus status = errSecParam;
+
+    CFDataRef serializedPublic = NULL;
+
+    status = SecKeyCopyPublicBytes(privateKey, &serializedPublic);
+    if ((status == errSecSuccess) && (serializedPublic != NULL)) {
+        SecKeyRef publicKeyRef = SecKeyCreateFromPublicData(kCFAllocatorDefault, SecKeyGetAlgorithmId(privateKey), serializedPublic);
+        CFRelease(serializedPublic);
+        if (publicKeyRef != NULL) {
+            return publicKeyRef;
+        }
+    }
+
+    const void *keys[]      = { kSecClass, kSecValueRef, kSecReturnAttributes };
+    const void *values[]    = { kSecClassKey, privateKey,  kCFBooleanTrue };
+    CFDictionaryRef query= CFDictionaryCreate(NULL, keys, values,
+                                              (sizeof(values) / sizeof(*values)),
+                                              &kCFTypeDictionaryKeyCallBacks,
+                                              &kCFTypeDictionaryValueCallBacks);
+    CFTypeRef foundItem = NULL;
+    status = SecItemCopyMatching(query, &foundItem);
+
+    if (status == errSecSuccess) {
+        if (CFGetTypeID(foundItem) == CFDictionaryGetTypeID()) {
+            CFMutableDictionaryRef query2 = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+            CFDictionaryAddValue(query2, kSecClass, kSecClassKey);
+            CFDictionaryAddValue(query2, kSecAttrKeyClass, kSecAttrKeyClassPublic);
+            CFDictionaryAddValue(query2, kSecAttrApplicationLabel, CFDictionaryGetValue((CFDictionaryRef)foundItem, kSecAttrApplicationLabel));
+            CFDictionaryAddValue(query2, kSecReturnRef, kCFBooleanTrue);
+
+            CFTypeRef foundKey = NULL;
+            status = SecItemCopyMatching(query2, &foundKey);
+            if (status == errSecSuccess) {
+                if (CFGetTypeID(foundKey) == SecKeyGetTypeID()) {
+                    CFRelease(query);
+                    CFRelease(query2);
+                    CFRelease(foundItem);
+                    return (SecKeyRef)foundKey;
+                } else {
+                    status = errSecItemNotFound;
+                }
+            }
+            CFRelease(query2);
+
+        } else {
+            status = errSecItemNotFound;
+        }
+        CFRelease(foundItem);
+    }
+
+    CFRelease(query);
+    return NULL;
+}
+