--- /dev/null
+/*
+ * 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;
+}
+