-/*
- * 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 <Security/SecKeyInternal.h>
-#include <Security/SecItem.h>
-#include <Security/SecItemPriv.h>
-#include <Security/SecFramework.h>
-
-#include <utilities/SecIOFormat.h>
-
-#include <utilities/SecCFWrappers.h>
-
-#include "SecRSAKeyPriv.h"
-#include "SecECKey.h"
-#include "SecBasePriv.h"
-
-#include <CoreFoundation/CFNumber.h>
-#include <CoreFoundation/CFString.h>
-#include <Security/SecBase.h>
-#include <pthread.h>
-#include <string.h>
-#include <AssertMacros.h>
-#include <utilities/debugging.h>
-#include <utilities/SecCFError.h>
-#include <CommonCrypto/CommonDigest.h>
-#include <Security/SecAsn1Coder.h>
-#include <Security/oidsalg.h>
-#include <Security/SecInternal.h>
-#include <Security/SecRandom.h>
-#include <corecrypto/ccrng_system.h>
-#include <asl.h>
-#include <stdlib.h>
-
-static pthread_once_t kSecKeyRegisterClass = PTHREAD_ONCE_INIT;
-static CFTypeID kSecKeyTypeID = _kCFRuntimeNotATypeID;
-
-/* Forward declartions of static functions. */
-static CFStringRef SecKeyCopyDescription(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 CFDataRef SecKeyCopyPublicKeyHash(SecKeyRef key)
-{
- CFDataRef pubKeyDigest = NULL, pubKeyBlob = NULL;
-
- /* encode the public key. */
- require_noerr(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut);
- require(pubKeyBlob, errOut);
-
- /* Calculate the digest of the public key. */
- require(pubKeyDigest = SecSHA1DigestCreate(CFGetAllocator(key),
- CFDataGetBytePtr(pubKeyBlob), CFDataGetLength(pubKeyBlob)),
- errOut);
-errOut:
- CFReleaseNull(pubKeyBlob);
- return pubKeyDigest;
-}
-
-
-/*
- */
-static CF_RETURNS_RETAINED 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 SecKeyCopyDescription(CFTypeRef cf) {
- SecKeyRef key = (SecKeyRef)cf;
-
- if(key->key_class->describe)
- return key->key_class->describe(key);
- else
- return CFStringCreateWithFormat(kCFAllocatorDefault,NULL,CFSTR("<SecKeyRef: %p>"), 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 */
- SecKeyCopyDescription /* 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_quiet(result = add_ref(pubKey, pubParams), errOut);
- }
- if (getBoolForKey(privParams, kSecAttrIsPermanent, false)) {
- require_noerr_quiet(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 SecKeyCreatePublicFromPrivate(SecKeyRef privateKey) {
- CFDataRef serializedPublic = NULL;
- SecKeyRef result = NULL;
-
- require_noerr_quiet(SecKeyCopyPublicBytes(privateKey, &serializedPublic), fail);
- require_quiet(serializedPublic, fail);
-
- result = SecKeyCreateFromPublicData(kCFAllocatorDefault, SecKeyGetAlgorithmID(privateKey), serializedPublic);
-
-fail:
- CFReleaseSafe(serializedPublic);
-
- return result;
-}
-
-static CFDictionaryRef CreatePrivateKeyMatchingQuery(SecKeyRef publicKey, bool returnPersistentRef)
-{
- CFDataRef public_key_hash = SecKeyCopyPublicKeyHash(publicKey);
-
- CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
- kSecClass, kSecClassKey,
- kSecAttrKeyClass, kSecAttrKeyClassPrivate,
- kSecAttrSynchronizable, kSecAttrSynchronizableAny,
- kSecAttrApplicationLabel, public_key_hash,
- kSecReturnPersistentRef, kCFBooleanTrue,
- NULL);
- CFReleaseNull(public_key_hash);
-
- return query;
-}
-
-CFDataRef SecKeyCreatePersistentRefToMatchingPrivateKey(SecKeyRef publicKey, CFErrorRef *error) {
- CFTypeRef persistentRef = NULL;
- CFDictionaryRef query = CreatePrivateKeyMatchingQuery(publicKey, true);
-
- require_quiet(SecError(SecItemCopyMatching(query, &persistentRef),error ,
- CFSTR("Error finding persistent ref to key from public: %@"), publicKey), fail);
-fail:
- CFReleaseNull(query);
- return (CFDataRef)persistentRef;
-}
-
-SecKeyRef SecKeyCopyMatchingPrivateKey(SecKeyRef publicKey, CFErrorRef *error) {
- CFTypeRef private_key = NULL;
-
- CFDictionaryRef query = CreatePrivateKeyMatchingQuery(publicKey, false);
-
- require_quiet(SecError(SecItemCopyMatching(query, &private_key), error,
- CFSTR("Error finding private key from public: %@"), publicKey), fail);
-fail:
- CFReleaseNull(query);
- return (SecKeyRef)private_key;
-}
-
-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: %" PRIdOSStatus, 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;
- case kSecPaddingPKCS1SHA224:
- digestLen = CC_SHA224_DIGEST_LENGTH;
- digestOid = &CSSMOID_SHA224;
- break;
- case kSecPaddingPKCS1SHA256:
- digestLen = CC_SHA256_DIGEST_LENGTH;
- 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;
- 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 errSecSuccess;
-}
-
-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 if (isString(ktype)) {
- algorithm = CFStringGetIntValue(ktype);
- CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) algorithm);
- if (!CFEqual(t, ktype)) {
- secwarning("Unsupported key class: %@", ktype);
- CFReleaseSafe(t);
- return NULL;
- }
- CFReleaseSafe(t);
- } 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 if (isString(kclass)) {
- class = CFStringGetIntValue(kclass);
- CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) class);
- if (!CFEqual(t, kclass)) {
- CFReleaseSafe(t);
- secwarning("Unsupported key class: %@", kclass);
- return NULL;
- }
- CFReleaseSafe(t);
- } 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
- case 73: // kSecAlgorithmEC
- 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
- case 73: // kSecAlgorithmEC
- 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 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 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;
-
- if (this == NULL)
- return errSecParam;
-
- 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 = (result >= 66 ? 9 : 8) + 2 * result;
- break;
- case kSecKeyKeySizeInBits:
- if (result >= 66)
- return 521;
- }
- }
-
- if (whichSize == kSecKeyKeySizeInBits)
- result *= 8;
-
- return result;
-
-}
-
-OSStatus SecKeyFindWithPersistentRef(CFDataRef persistentRef, SecKeyRef* lookedUpData)
-{
- CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
- kSecReturnRef, kCFBooleanTrue,
- kSecClass, kSecClassKey,
- kSecValuePersistentRef, persistentRef,
- NULL);
- CFTypeRef foundRef = NULL;
- OSStatus status = SecItemCopyMatching(query, &foundRef);
-
- if (status == errSecSuccess) {
- if (CFGetTypeID(foundRef) == SecKeyGetTypeID()) {
- *lookedUpData = (SecKeyRef) foundRef;
- foundRef = NULL;
- status = errSecSuccess;
- } else {
- status = errSecItemNotFound;
- }
- }
-
- CFReleaseSafe(foundRef);
- CFReleaseSafe(query);
-
- return status;
-}
-
-OSStatus SecKeyCopyPersistentRef(SecKeyRef key, CFDataRef* persistentRef)
-{
- CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
- kSecReturnPersistentRef, kCFBooleanTrue,
- kSecValueRef, key,
- kSecAttrSynchronizable, kSecAttrSynchronizableAny,
- NULL);
- CFTypeRef foundRef = NULL;
- OSStatus status = SecItemCopyMatching(query, &foundRef);
-
- if (status == errSecSuccess) {
- if (CFGetTypeID(foundRef) == CFDataGetTypeID()) {
- *persistentRef = foundRef;
- foundRef = NULL;
- } else {
- status = errSecItemNotFound;
- }
- }
-
- CFReleaseSafe(foundRef);
- CFReleaseSafe(query);
-
- return status;
-}