X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/5dd5f9ec28f304ca377c42fd7f711d6cf12b90e1..5c19dc3ae3bd8e40a9c028b0deddd50ff337692c:/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 index 00000000..cd703686 --- /dev/null +++ b/OSX/libsecurity_keychain/Security/SecKey.cpp @@ -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 +#include +#include +#include +#include +#include +#include +#include + +#include "SecBridge.h" + +#include +#include +#include +#include +#include + +#include +#include + +#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 theAccess(initialAccess ? Access::required(initialAccess) : new Access("")); + SecPointer pubItem, privItem; + + Mutex *keychainMutex = keychain->getKeychainMutex(); + StLock _(*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::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::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::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::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 theAccess(initialAccess ? Access::required(initialAccess) : new Access("")); + SecPointer 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 theAccess; + + if (keychainRef) + keychain = KeychainImpl::required(keychainRef); + if (initialAccess) + theAccess = Access::required(initialAccess); + + SecPointer 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(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(attributes[i].data), buffer_length, kCFStringEncodingUTF8)) { + MacOSError::throwMe(errSecParam); + } + } + attributes[i].length = (UInt32)strlen(static_cast(attributes[i].data)); + } else if (CFDataGetTypeID() == CFGetTypeID(label)) { + // 10.6 bug compatibility + CFDataRef label_data = static_cast(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::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::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::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::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::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;imodulusLength; + 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; +} +