/* * Copyright (c) 2002-2014 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" CFTypeID SecKeyGetTypeID(void) { BEGIN_SECAPI return gTypes().KeyItem.typeID; END_SECAPI1(_kCFRuntimeNotATypeID) } 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 SecPointer keyItem(KeyItem::required(keyRef)); Required(algid) = &keyItem->algorithmIdentifier(); END_SECAPI } /* new for 10.8 */ CFIndex SecKeyGetAlgorithmId(SecKeyRef key) { 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 CFTypeRef *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_m(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; }