X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/72a12576750f52947eb043106ba5c12c0d07decf..b1ab9ed8d0e0f1c3b66d7daa8fd5564444c56195:/sec/Security/SecKey.c diff --git a/sec/Security/SecKey.c b/sec/Security/SecKey.c new file mode 100644 index 00000000..f09fadfe --- /dev/null +++ b/sec/Security/SecKey.c @@ -0,0 +1,946 @@ +/* + * Copyright (c) 2006-2011 Apple Inc. All Rights Reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +/* + * SecKey.c - CoreFoundation based key object + */ + + +#include +#include +#include +#include + +#include "SecRSAKeyPriv.h" +#include "SecECKey.h" +#include "SecBasePriv.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static pthread_once_t kSecKeyRegisterClass = PTHREAD_ONCE_INIT; +static CFTypeID kSecKeyTypeID = _kCFRuntimeNotATypeID; + +/* Forward declartions of static functions. */ +static CFStringRef SecKeyDescribe(CFTypeRef cf); +static void SecKeyDestroy(CFTypeRef cf); + +/* Static functions. */ +#define MAX_DIGEST_LEN (CC_SHA512_DIGEST_LENGTH) + +/* Currently length of SHA512 oid + 1 */ +#define MAX_OID_LEN (10) + +#define DER_MAX_DIGEST_INFO_LEN (10 + MAX_DIGEST_LEN + MAX_OID_LEN) + +/* Encode the digestInfo header into digestInfo and return the offset from + digestInfo at which to put the actual digest. Returns 0 if digestInfo + won't fit within digestInfoLength bytes. + + 0x30, topLen, + 0x30, algIdLen, + 0x06, oid.Len, oid.Data, + 0x05, 0x00 + 0x04, digestLen + digestData +*/ +static size_t DEREncodeDigestInfoPrefix(const SecAsn1Oid *oid, + size_t digestLength, uint8_t *digestInfo, size_t digestInfoLength) { + size_t algIdLen = oid->Length + 4; + size_t topLen = algIdLen + digestLength + 4; + size_t totalLen = topLen + 2; + + if (totalLen > digestInfoLength) { + return 0; + } + + size_t ix = 0; + digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED); + digestInfo[ix++] = topLen; + digestInfo[ix++] = (SEC_ASN1_SEQUENCE | SEC_ASN1_CONSTRUCTED); + digestInfo[ix++] = algIdLen; + digestInfo[ix++] = SEC_ASN1_OBJECT_ID; + digestInfo[ix++] = oid->Length; + memcpy(&digestInfo[ix], oid->Data, oid->Length); + ix += oid->Length; + digestInfo[ix++] = SEC_ASN1_NULL; + digestInfo[ix++] = 0; + digestInfo[ix++] = SEC_ASN1_OCTET_STRING; + digestInfo[ix++] = digestLength; + + return ix; +} + +static struct ccrng_system_state ccrng_system_state_seckey; + +static void register_algs(void) { + ccrng_seckey = (struct ccrng_state *)&ccrng_system_state_seckey; + ccrng_system_init(&ccrng_system_state_seckey); +} + + +/* + */ +static CFDictionaryRef SecKeyGenerateAttributeDictionaryFor(SecKeyRef key, + CFTypeRef keyType, + CFDataRef privateBlob) +{ + CFAllocatorRef allocator = CFGetAllocator(key); + DICT_DECLARE(25); + CFDataRef pubKeyDigest = NULL, pubKeyBlob = NULL; + CFDictionaryRef dict = NULL; + + size_t sizeValue = SecKeyGetSize(key, kSecKeyKeySizeInBits); + CFNumberRef sizeInBits = CFNumberCreate(allocator, kCFNumberLongType, &sizeValue); + + /* encode the public key. */ + require_noerr(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut); + require(pubKeyBlob, errOut); + + /* Calculate the digest of the public key. */ + require(pubKeyDigest = SecSHA1DigestCreate(allocator, + CFDataGetBytePtr(pubKeyBlob), CFDataGetLength(pubKeyBlob)), + errOut); + + DICT_ADDPAIR(kSecClass, kSecClassKey); + DICT_ADDPAIR(kSecAttrKeyClass, privateBlob ? kSecAttrKeyClassPrivate : kSecAttrKeyClassPublic); + DICT_ADDPAIR(kSecAttrApplicationLabel, pubKeyDigest); + DICT_ADDPAIR(kSecAttrIsPermanent, kCFBooleanTrue); + DICT_ADDPAIR(kSecAttrIsPrivate, kCFBooleanTrue); + DICT_ADDPAIR(kSecAttrIsModifiable, kCFBooleanTrue); + DICT_ADDPAIR(kSecAttrKeyType, keyType); + DICT_ADDPAIR(kSecAttrKeySizeInBits, sizeInBits); + DICT_ADDPAIR(kSecAttrEffectiveKeySize, sizeInBits); + DICT_ADDPAIR(kSecAttrIsSensitive, kCFBooleanFalse); + DICT_ADDPAIR(kSecAttrWasAlwaysSensitive, kCFBooleanFalse); + DICT_ADDPAIR(kSecAttrIsExtractable, kCFBooleanTrue); + DICT_ADDPAIR(kSecAttrWasNeverExtractable, kCFBooleanFalse); + DICT_ADDPAIR(kSecAttrCanEncrypt, kCFBooleanFalse); + DICT_ADDPAIR(kSecAttrCanDecrypt, kCFBooleanTrue); + DICT_ADDPAIR(kSecAttrCanDerive, kCFBooleanTrue); + DICT_ADDPAIR(kSecAttrCanSign, kCFBooleanTrue); + DICT_ADDPAIR(kSecAttrCanVerify, kCFBooleanFalse); + DICT_ADDPAIR(kSecAttrCanSignRecover, kCFBooleanFalse); + DICT_ADDPAIR(kSecAttrCanVerifyRecover, kCFBooleanFalse); + DICT_ADDPAIR(kSecAttrCanWrap, kCFBooleanFalse); + DICT_ADDPAIR(kSecAttrCanUnwrap, kCFBooleanTrue); + DICT_ADDPAIR(kSecValueData, privateBlob ? privateBlob : pubKeyBlob); + dict = DICT_CREATE(allocator); + +errOut: + // @@@ Zero out key material. + CFReleaseSafe(pubKeyDigest); + CFReleaseSafe(pubKeyBlob); + CFReleaseSafe(sizeInBits); + + return dict; +} + +CFDictionaryRef SecKeyGeneratePrivateAttributeDictionary(SecKeyRef key, + CFTypeRef keyType, + CFDataRef privateBlob) +{ + return SecKeyGenerateAttributeDictionaryFor(key, keyType, privateBlob); +} + +CFDictionaryRef SecKeyGeneratePublicAttributeDictionary(SecKeyRef key, CFTypeRef keyType) +{ + return SecKeyGenerateAttributeDictionaryFor(key, keyType, NULL); +} + +/* + */ + +static CFStringRef SecKeyDescribe(CFTypeRef cf) { + SecKeyRef key = (SecKeyRef)cf; + return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR(""), key); +} + +static void SecKeyDestroy(CFTypeRef cf) { + SecKeyRef key = (SecKeyRef)cf; + if (key->key_class->destroy) + key->key_class->destroy(key); +} + +static Boolean SecKeyEqual(CFTypeRef cf1, CFTypeRef cf2) { + SecKeyRef key1 = (SecKeyRef)cf1; + SecKeyRef key2 = (SecKeyRef)cf2; + if (key1 == key2) + return true; + if (!key2 || key1->key_class != key2->key_class) + return false; + if (key1->key_class->extraBytes) + return !memcmp(key1->key, key2->key, key1->key_class->extraBytes); + + /* TODO: Won't work when we get reference keys. */ + CFDictionaryRef d1, d2; + d1 = SecKeyCopyAttributeDictionary(key1); + d2 = SecKeyCopyAttributeDictionary(key2); + Boolean result = CFEqual(d1, d2); + CFReleaseSafe(d1); + CFReleaseSafe(d2); + return result; +} + +static void SecKeyRegisterClass(void) { + static const CFRuntimeClass kSecKeyClass = { + 0, /* version */ + "SecKey", /* class name */ + NULL, /* init */ + NULL, /* copy */ + SecKeyDestroy, /* dealloc */ + SecKeyEqual, /* equal */ + NULL, /* hash */ + NULL, /* copyFormattingDesc */ + SecKeyDescribe /* copyDebugDesc */ + }; + + kSecKeyTypeID = _CFRuntimeRegisterClass(&kSecKeyClass); + register_algs(); +} + +/* Public API functions. */ +CFTypeID SecKeyGetTypeID(void) { + pthread_once(&kSecKeyRegisterClass, SecKeyRegisterClass); + return kSecKeyTypeID; +} + +static bool getBoolForKey(CFDictionaryRef dict, CFStringRef key, bool default_value) { + CFTypeRef value = CFDictionaryGetValue(dict, key); + if (value) { + if (CFGetTypeID(value) == CFBooleanGetTypeID()) { + return CFBooleanGetValue(value); + } else { + secwarning("Value %@ for key %@ is not bool", value, key); + } + } + + return default_value; +} + +static OSStatus add_ref(CFTypeRef item, CFMutableDictionaryRef dict) { + CFDictionarySetValue(dict, kSecValueRef, item); + return SecItemAdd(dict, NULL); +} + +static void merge_params_applier(const void *key, const void *value, + void *context) { + CFMutableDictionaryRef result = (CFMutableDictionaryRef)context; + CFDictionaryAddValue(result, key, value); +} + +/* Create a mutable dictionary that is based on the subdictionary for key + with any attributes from the top level dict merged in. */ +static CFMutableDictionaryRef merge_params(CFDictionaryRef dict, + CFStringRef key) { + CFDictionaryRef subdict = CFDictionaryGetValue(dict, key); + CFMutableDictionaryRef result; + + if (subdict) { + result = CFDictionaryCreateMutableCopy(NULL, 0, subdict); + /* Add everything in dict not already in result to result. */ + CFDictionaryApplyFunction(dict, merge_params_applier, result); + } else { + result = CFDictionaryCreateMutableCopy(NULL, 0, dict); + } + + /* Remove values that only belong in the top level dict. */ + CFDictionaryRemoveValue(result, kSecPublicKeyAttrs); + CFDictionaryRemoveValue(result, kSecPrivateKeyAttrs); + CFDictionaryRemoveValue(result, kSecAttrKeyType); + CFDictionaryRemoveValue(result, kSecAttrKeySizeInBits); + + return result; +} + +/* Generate a private/public keypair. */ +OSStatus SecKeyGeneratePair(CFDictionaryRef parameters, + SecKeyRef *publicKey, SecKeyRef *privateKey) { + OSStatus result = errSecUnsupportedAlgorithm; + SecKeyRef privKey = NULL; + SecKeyRef pubKey = NULL; + CFMutableDictionaryRef pubParams = merge_params(parameters, kSecPublicKeyAttrs), + privParams = merge_params(parameters, kSecPrivateKeyAttrs); + CFStringRef ktype = CFDictionaryGetValue(parameters, kSecAttrKeyType); + + require(ktype, errOut); + + if (CFEqual(ktype, kSecAttrKeyTypeEC)) { + result = SecECKeyGeneratePair(parameters, &pubKey, &privKey); + } else if (CFEqual(ktype, kSecAttrKeyTypeRSA)) { + result = SecRSAKeyGeneratePair(parameters, &pubKey, &privKey); + } + + require_noerr(result, errOut); + + /* Store the keys in the keychain if they are marked as permanent. */ + if (getBoolForKey(pubParams, kSecAttrIsPermanent, false)) { + require_noerr(result = add_ref(pubKey, pubParams), errOut); + } + if (getBoolForKey(privParams, kSecAttrIsPermanent, false)) { + require_noerr(result = add_ref(privKey, privParams), errOut); + } + + if (publicKey) { + *publicKey = pubKey; + pubKey = NULL; + } + if (privateKey) { + *privateKey = privKey; + privKey = NULL; + } + +errOut: + CFReleaseSafe(pubParams); + CFReleaseSafe(privParams); + CFReleaseSafe(pubKey); + CFReleaseSafe(privKey); + + return result; +} + +SecKeyRef SecKeyCreatePublicFromDER(CFAllocatorRef allocator, + const SecAsn1Oid *oid, const SecAsn1Item *params, + const SecAsn1Item *keyData) { + SecKeyRef publicKey = NULL; + if (SecAsn1OidCompare(oid, &CSSMOID_RSA)) { + /* pkcs1 1 */ + publicKey = SecKeyCreateRSAPublicKey(kCFAllocatorDefault, + keyData->Data, keyData->Length, kSecKeyEncodingPkcs1); + } else if (SecAsn1OidCompare(oid, &CSSMOID_ecPublicKey)) { + SecDERKey derKey = { + .oid = oid->Data, + .oidLength = oid->Length, + .key = keyData->Data, + .keyLength = keyData->Length, + }; + if (params) { + derKey.parameters = params->Data; + derKey.parametersLength = params->Length; + } + publicKey = SecKeyCreateECPublicKey(kCFAllocatorDefault, + (const uint8_t *)&derKey, sizeof(derKey), kSecDERKeyEncoding); + } else { + secwarning("Unsupported algorithm oid"); + } + + return publicKey; +} + +SecKeyRef SecKeyCreate(CFAllocatorRef allocator, + const SecKeyDescriptor *key_class, const uint8_t *keyData, + CFIndex keyDataLength, SecKeyEncoding encoding) { + check(key_class); + + size_t size = sizeof(struct __SecKey) + key_class->extraBytes; + SecKeyRef result = (SecKeyRef)_CFRuntimeCreateInstance(allocator, + SecKeyGetTypeID(), size - sizeof(CFRuntimeBase), NULL); + if (result) { + memset((char*)result + sizeof(result->_base), 0, size - sizeof(result->_base)); + result->key_class = key_class; + if (key_class->extraBytes) { + /* Make result->key point to the extraBytes we allocated. */ + result->key = ((char*)result) + sizeof(*result); + } + if (key_class->init) { + OSStatus status; + status = key_class->init(result, keyData, keyDataLength, encoding); + if (status) { + secwarning("init %s key: %d", key_class->name, status); + CFRelease(result); + result = NULL; + } + } + } + return result; +} + +enum { + kSecKeyDigestInfoSign, + kSecKeyDigestInfoVerify +}; + +static OSStatus SecKeyDigestInfoSignVerify( + SecKeyRef key, /* Private key */ + SecPadding padding, /* kSecPaddingPKCS1@@@ */ + const uint8_t *dataToSign, /* signature over this data */ + size_t dataToSignLen, /* length of dataToSign */ + uint8_t *sig, /* signature, RETURNED */ + size_t *sigLen, /* IN/OUT */ + int mode) { + size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; + uint8_t digestInfo[digestInfoLength]; + const SecAsn1Oid *digestOid; + size_t digestLen; + + switch (padding) { +#if 0 + case kSecPaddingPKCS1MD2: + digestLen = CC_MD2_DIGEST_LENGTH; + digestOid = &CSSMOID_MD2; + break; + case kSecPaddingPKCS1MD4: + digestLen = CC_MD4_DIGEST_LENGTH; + digestOid = &CSSMOID_MD4; + break; + case kSecPaddingPKCS1MD5: + digestLen = CC_MD5_DIGEST_LENGTH; + digestOid = &CSSMOID_MD5; + break; +#endif + case kSecPaddingPKCS1SHA1: + digestLen = CC_SHA1_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA1; + break; +#if 0 + case kSecPaddingPKCS1SHA224: + digestLen = CC_SHA224_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA224; + break; + case kSecPaddingPKCS1SHA256: + digestLen = CCSHA256_OUTPUT_SIZE; + digestOid = &CSSMOID_SHA256; + break; + case kSecPaddingPKCS1SHA384: + digestLen = CC_SHA384_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA384; + break; + case kSecPaddingPKCS1SHA512: + digestLen = CC_SHA512_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA512; + break; +#endif + default: + return errSecUnsupportedPadding; + } + + if (dataToSignLen != digestLen) + return errSecParam; + + size_t offset = DEREncodeDigestInfoPrefix(digestOid, digestLen, + digestInfo, digestInfoLength); + if (!offset) + return errSecBufferTooSmall; + + /* Append the digest to the digestInfo prefix and adjust the length. */ + memcpy(&digestInfo[offset], dataToSign, digestLen); + digestInfoLength = offset + digestLen; + + if (mode == kSecKeyDigestInfoSign) { + return key->key_class->rawSign(key, kSecPaddingPKCS1, + digestInfo, digestInfoLength, sig, sigLen); + } else { + return key->key_class->rawVerify(key, kSecPaddingPKCS1, + digestInfo, digestInfoLength, sig, *sigLen); + } + + return noErr; +} + +OSStatus SecKeyRawSign( + SecKeyRef key, /* Private key */ + SecPadding padding, /* kSecPaddingNone or kSecPaddingPKCS1 */ + const uint8_t *dataToSign, /* signature over this data */ + size_t dataToSignLen, /* length of dataToSign */ + uint8_t *sig, /* signature, RETURNED */ + size_t *sigLen) { /* IN/OUT */ + if (!key->key_class->rawSign) + return errSecUnsupportedOperation; + + if (padding < kSecPaddingPKCS1MD2) { + return key->key_class->rawSign(key, padding, dataToSign, dataToSignLen, + sig, sigLen); + } else { + return SecKeyDigestInfoSignVerify(key, padding, dataToSign, dataToSignLen, + sig, sigLen, kSecKeyDigestInfoSign); + } +} + +OSStatus SecKeyRawVerify( + SecKeyRef key, /* Public key */ + SecPadding padding, /* kSecPaddingNone or kSecPaddingPKCS1 */ + const uint8_t *signedData, /* signature over this data */ + size_t signedDataLen, /* length of dataToSign */ + const uint8_t *sig, /* signature */ + size_t sigLen) { /* length of signature */ + if (!key->key_class->rawVerify) + return errSecUnsupportedOperation; + + if (padding < kSecPaddingPKCS1MD2) { + return key->key_class->rawVerify(key, padding, signedData, signedDataLen, + sig, sigLen); + } else { + /* Casting away the constness of sig is safe since + SecKeyDigestInfoSignVerify only modifies sig if + mode == kSecKeyDigestInfoSign. */ + return SecKeyDigestInfoSignVerify(key, padding, + signedData, signedDataLen, (uint8_t *)sig, &sigLen, + kSecKeyDigestInfoVerify); + } +} + +OSStatus SecKeyEncrypt( + SecKeyRef key, /* Public key */ + SecPadding padding, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */ + const uint8_t *plainText, + size_t plainTextLen, /* length of plainText */ + uint8_t *cipherText, + size_t *cipherTextLen) { /* IN/OUT */ + if (key->key_class->encrypt) + return key->key_class->encrypt(key, padding, plainText, plainTextLen, + cipherText, cipherTextLen); + return errSecUnsupportedOperation; +} + +OSStatus SecKeyDecrypt( + SecKeyRef key, /* Private key */ + SecPadding padding, /* kSecPaddingNone, kSecPaddingPKCS1, kSecPaddingOAEP */ + const uint8_t *cipherText, + size_t cipherTextLen, /* length of cipherText */ + uint8_t *plainText, + size_t *plainTextLen) { /* IN/OUT */ + if (key->key_class->decrypt) + return key->key_class->decrypt(key, padding, cipherText, cipherTextLen, + plainText, plainTextLen); + return errSecUnsupportedOperation; +} + +size_t SecKeyGetBlockSize(SecKeyRef key) { + if (key->key_class->blockSize) + return key->key_class->blockSize(key); + return 0; +} + +/* Private API functions. */ + +CFDictionaryRef SecKeyCopyAttributeDictionary(SecKeyRef key) { + if (key->key_class->copyDictionary) + return key->key_class->copyDictionary(key); + return NULL; +} + +SecKeyRef SecKeyCreateFromAttributeDictionary(CFDictionaryRef refAttributes) { + /* TODO: Support having an allocator in refAttributes. */ + CFAllocatorRef allocator = NULL; + CFDataRef data = CFDictionaryGetValue(refAttributes, kSecValueData); + CFTypeRef ktype = CFDictionaryGetValue(refAttributes, kSecAttrKeyType); + SInt32 algorithm; + SecKeyRef ref; + + /* First figure out the key type (algorithm). */ + if (CFGetTypeID(ktype) == CFNumberGetTypeID()) { + CFNumberGetValue(ktype, kCFNumberSInt32Type, &algorithm); + } else { + secwarning("Unsupported key type: %@", ktype); + return NULL; + } + + /* TODO: The code below won't scale well, consider moving to something + table driven. */ + SInt32 class; + CFTypeRef kclass = CFDictionaryGetValue(refAttributes, kSecAttrKeyClass); + if (CFGetTypeID(kclass) == CFNumberGetTypeID()) { + CFNumberGetValue(kclass, kCFNumberSInt32Type, &class); + } else { + secwarning("Unsupported key class: %@", kclass); + return NULL; + } + + switch (class) { + case 0: // kSecAttrKeyClassPublic + switch (algorithm) { + case 42: // kSecAlgorithmRSA + ref = SecKeyCreateRSAPublicKey(allocator, + CFDataGetBytePtr(data), CFDataGetLength(data), + kSecKeyEncodingBytes); + break; + case 43: // kSecAlgorithmECDSA + ref = SecKeyCreateECPublicKey(allocator, + CFDataGetBytePtr(data), CFDataGetLength(data), + kSecKeyEncodingBytes); + break; + default: + secwarning("Unsupported public key type: %@", ktype); + ref = NULL; + break; + }; + break; + case 1: // kSecAttrKeyClassPrivate + switch (algorithm) { + case 42: // kSecAlgorithmRSA + ref = SecKeyCreateRSAPrivateKey(allocator, + CFDataGetBytePtr(data), CFDataGetLength(data), + kSecKeyEncodingBytes); + break; + case 43: // kSecAlgorithmECDSA + ref = SecKeyCreateECPrivateKey(allocator, + CFDataGetBytePtr(data), CFDataGetLength(data), + kSecKeyEncodingBytes); + break; + default: + secwarning("Unsupported private key type: %@", ktype); + ref = NULL; + break; + }; + break; + case 2: // kSecAttrKeyClassSymmetric + secwarning("Unsupported symmetric key type: %@", ktype); + ref = NULL; + break; + default: + secwarning("Unsupported key class: %@", kclass); + ref = NULL; + } + + return ref; +} + +/* TODO: This function should ensure that this keys algorithm matches the + signature algorithm. */ +static OSStatus SecKeyGetDigestInfo(SecKeyRef this, const SecAsn1AlgId *algId, + const uint8_t *data, size_t dataLen, bool digestData, + uint8_t *digestInfo, size_t *digestInfoLen /* IN/OUT */) { + unsigned char *(*digestFcn)(const void *, CC_LONG, unsigned char *); + CFIndex keyAlgID = kSecNullAlgorithmID; + const SecAsn1Oid *digestOid; + size_t digestLen; + size_t offset = 0; + + /* Since these oids all have the same prefix, use switch. */ + if ((algId->algorithm.Length == CSSMOID_RSA.Length) && + !memcmp(algId->algorithm.Data, CSSMOID_RSA.Data, + algId->algorithm.Length - 1)) { + keyAlgID = kSecRSAAlgorithmID; + switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { +#if 0 + case 2: /* oidMD2WithRSA */ + digestFcn = CC_MD2; + digestLen = CC_MD2_DIGEST_LENGTH; + digestOid = &CSSMOID_MD2; + break; + case 3: /* oidMD4WithRSA */ + digestFcn = CC_MD4; + digestLen = CC_MD4_DIGEST_LENGTH; + digestOid = &CSSMOID_MD4; + break; + case 4: /* oidMD5WithRSA */ + digestFcn = CC_MD5; + digestLen = CC_MD5_DIGEST_LENGTH; + digestOid = &CSSMOID_MD5; + break; +#endif /* 0 */ + case 5: /* oidSHA1WithRSA */ + digestFcn = CC_SHA1; + digestLen = CC_SHA1_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA1; + break; + case 11: /* oidSHA256WithRSA */ + digestFcn = CC_SHA256; + digestLen = CC_SHA256_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA256; + break; + case 12: /* oidSHA384WithRSA */ + /* pkcs1 12 */ + digestFcn = CC_SHA384; + digestLen = CC_SHA384_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA384; + break; + case 13: /* oidSHA512WithRSA */ + digestFcn = CC_SHA512; + digestLen = CC_SHA512_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA512; + break; + case 14: /* oidSHA224WithRSA */ + digestFcn = CC_SHA224; + digestLen = CC_SHA224_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA224; + break; + default: + secdebug("key", "unsupported rsa signature algorithm"); + return errSecUnsupportedAlgorithm; + } + } else if ((algId->algorithm.Length == CSSMOID_ECDSA_WithSHA224.Length) && + !memcmp(algId->algorithm.Data, CSSMOID_ECDSA_WithSHA224.Data, + algId->algorithm.Length - 1)) { + keyAlgID = kSecECDSAAlgorithmID; + switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { + case 1: /* oidSHA224WithECDSA */ + digestFcn = CC_SHA224; + digestLen = CC_SHA224_DIGEST_LENGTH; + break; + case 2: /* oidSHA256WithECDSA */ + digestFcn = CC_SHA256; + digestLen = CC_SHA256_DIGEST_LENGTH; + break; + case 3: /* oidSHA384WithECDSA */ + /* pkcs1 12 */ + digestFcn = CC_SHA384; + digestLen = CC_SHA384_DIGEST_LENGTH; + break; + case 4: /* oidSHA512WithECDSA */ + digestFcn = CC_SHA512; + digestLen = CC_SHA512_DIGEST_LENGTH; + break; + default: + secdebug("key", "unsupported ecdsa signature algorithm"); + return errSecUnsupportedAlgorithm; + } + } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_ECDSA_WithSHA1)) { + keyAlgID = kSecECDSAAlgorithmID; + digestFcn = CC_SHA1; + digestLen = CC_SHA1_DIGEST_LENGTH; + } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_SHA1)) { + digestFcn = CC_SHA1; + digestLen = CC_SHA1_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA1; + } else if ((algId->algorithm.Length == CSSMOID_SHA224.Length) && + !memcmp(algId->algorithm.Data, CSSMOID_SHA224.Data, algId->algorithm.Length - 1)) + { + switch (algId->algorithm.Data[algId->algorithm.Length - 1]) { + case 4: /* OID_SHA224 */ + digestFcn = CC_SHA224; + digestLen = CC_SHA224_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA224; + break; + case 1: /* OID_SHA256 */ + digestFcn = CC_SHA256; + digestLen = CC_SHA256_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA256; + break; + case 2: /* OID_SHA384 */ + /* pkcs1 12 */ + digestFcn = CC_SHA384; + digestLen = CC_SHA384_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA384; + break; + case 3: /* OID_SHA512 */ + digestFcn = CC_SHA512; + digestLen = CC_SHA512_DIGEST_LENGTH; + digestOid = &CSSMOID_SHA512; + break; + default: + secdebug("key", "unsupported sha-2 signature algorithm"); + return errSecUnsupportedAlgorithm; + } + } else if (SecAsn1OidCompare(&algId->algorithm, &CSSMOID_MD5)) { + digestFcn = CC_MD5; + digestLen = CC_MD5_DIGEST_LENGTH; + digestOid = &CSSMOID_MD5; + } else { + secdebug("key", "unsupported digesting algorithm"); + return errSecUnsupportedAlgorithm; + } + + /* check key is appropriate for signature (superfluous for digest only oid) */ + if (keyAlgID == kSecNullAlgorithmID) + keyAlgID = SecKeyGetAlgorithmID(this); + else if (keyAlgID != SecKeyGetAlgorithmID(this)) + return errSecUnsupportedAlgorithm; + + switch(keyAlgID) { + case kSecRSAAlgorithmID: + offset = DEREncodeDigestInfoPrefix(digestOid, digestLen, + digestInfo, *digestInfoLen); + if (!offset) + return errSecBufferTooSmall; + break; + case kSecDSAAlgorithmID: + if (digestOid != &CSSMOID_SHA1) + return errSecUnsupportedAlgorithm; + break; + case kSecECDSAAlgorithmID: + break; + default: + secdebug("key", "unsupported signature algorithm"); + return errSecUnsupportedAlgorithm; + } + + if (digestData) { + if(dataLen>UINT32_MAX) /* Check for overflow with CC_LONG cast */ + return paramErr; + digestFcn(data, (CC_LONG)dataLen, &digestInfo[offset]); + *digestInfoLen = offset + digestLen; + } else { + if (dataLen != digestLen) + return paramErr; + memcpy(&digestInfo[offset], data, dataLen); + *digestInfoLen = offset + dataLen; + } + + return noErr; +} + +OSStatus SecKeyDigestAndVerify( + SecKeyRef this, /* Private key */ + const SecAsn1AlgId *algId, /* algorithm oid/params */ + const uint8_t *dataToDigest, /* signature over this data */ + size_t dataToDigestLen,/* length of dataToDigest */ + const uint8_t *sig, /* signature to verify */ + size_t sigLen) { /* length of sig */ + size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; + uint8_t digestInfo[digestInfoLength]; + OSStatus status; + + status = SecKeyGetDigestInfo(this, algId, dataToDigest, dataToDigestLen, true, + digestInfo, &digestInfoLength); + if (status) + return status; + return SecKeyRawVerify(this, kSecPaddingPKCS1, + digestInfo, digestInfoLength, sig, sigLen); +} + +OSStatus SecKeyDigestAndSign( + SecKeyRef this, /* Private key */ + const SecAsn1AlgId *algId, /* algorithm oid/params */ + const uint8_t *dataToDigest, /* signature over this data */ + size_t dataToDigestLen,/* length of dataToDigest */ + uint8_t *sig, /* signature, RETURNED */ + size_t *sigLen) { /* IN/OUT */ + size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; + uint8_t digestInfo[digestInfoLength]; + OSStatus status; + + status = SecKeyGetDigestInfo(this, algId, dataToDigest, dataToDigestLen, true /* digest data */, + digestInfo, &digestInfoLength); + if (status) + return status; + return SecKeyRawSign(this, kSecPaddingPKCS1, + digestInfo, digestInfoLength, sig, sigLen); +} + +OSStatus SecKeyVerifyDigest( + SecKeyRef this, /* Private key */ + const SecAsn1AlgId *algId, /* algorithm oid/params */ + const uint8_t *digestData, /* signature over this digest */ + size_t digestDataLen,/* length of dataToDigest */ + const uint8_t *sig, /* signature to verify */ + size_t sigLen) { /* length of sig */ + size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; + uint8_t digestInfo[digestInfoLength]; + OSStatus status; + + status = SecKeyGetDigestInfo(this, algId, digestData, digestDataLen, false /* data is digest */, + digestInfo, &digestInfoLength); + if (status) + return status; + return SecKeyRawVerify(this, kSecPaddingPKCS1, + digestInfo, digestInfoLength, sig, sigLen); +} + +OSStatus SecKeySignDigest( + SecKeyRef this, /* Private key */ + const SecAsn1AlgId *algId, /* algorithm oid/params */ + const uint8_t *digestData, /* signature over this digest */ + size_t digestDataLen,/* length of digestData */ + uint8_t *sig, /* signature, RETURNED */ + size_t *sigLen) { /* IN/OUT */ + size_t digestInfoLength = DER_MAX_DIGEST_INFO_LEN; + uint8_t digestInfo[digestInfoLength]; + OSStatus status; + + status = SecKeyGetDigestInfo(this, algId, digestData, digestDataLen, false, + digestInfo, &digestInfoLength); + if (status) + return status; + return SecKeyRawSign(this, kSecPaddingPKCS1, + digestInfo, digestInfoLength, sig, sigLen); +} + +CFIndex SecKeyGetAlgorithmID(SecKeyRef key) { + /* This method was added to version 1 keys. */ + if (key->key_class->version > 0 && key->key_class->getAlgorithmID) + return key->key_class->getAlgorithmID(key); + /* All version 0 key were RSA. */ + return kSecRSAAlgorithmID; +} + + +OSStatus SecKeyCopyPublicBytes(SecKeyRef key, CFDataRef* serializedPublic) { + if (key->key_class->version > 1 && key->key_class->copyPublic) + return key->key_class->copyPublic(key, serializedPublic); + return errSecUnimplemented; +} + +SecKeyRef SecKeyCreateFromPublicBytes(CFAllocatorRef allocator, CFIndex algorithmID, const uint8_t *keyData, CFIndex keyDataLength) +{ + switch (algorithmID) + { + case kSecRSAAlgorithmID: + return SecKeyCreateRSAPublicKey(allocator, + keyData, keyDataLength, + kSecKeyEncodingBytes); + case kSecECDSAAlgorithmID: + return SecKeyCreateECPublicKey(allocator, + keyData, keyDataLength, + kSecKeyEncodingBytes); + default: + return NULL; + } +} + +SecKeyRef SecKeyCreateFromPublicData(CFAllocatorRef allocator, CFIndex algorithmID, CFDataRef serialized) +{ + return SecKeyCreateFromPublicBytes(allocator, algorithmID, CFDataGetBytePtr(serialized), CFDataGetLength(serialized)); +} + +// This is a bit icky hack to avoid changing the vtable for +// SecKey. +size_t SecKeyGetSize(SecKeyRef key, SecKeySize whichSize) +{ + size_t result = SecKeyGetBlockSize(key); + + if (kSecECDSAAlgorithmID == SecKeyGetAlgorithmID(key)) { + switch (whichSize) { + case kSecKeyEncryptedDataSize: + result = 0; + break; + case kSecKeySignatureSize: + result = 2 * result + 6; + if (result >= 66) + result += 2; + break; + case kSecKeyKeySizeInBits: + if (result >= 66) + return 521; + } + } + + if (whichSize == kSecKeyKeySizeInBits) + result *= 8; + + return result; + +} +