#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;
static CFStringRef SecKeyCopyDescription(CFTypeRef cf);
static void SecKeyDestroy(CFTypeRef cf);
#define MAX_DIGEST_LEN (CC_SHA512_DIGEST_LENGTH)
#define MAX_OID_LEN (10)
#define DER_MAX_DIGEST_INFO_LEN (10 + MAX_DIGEST_LEN + MAX_OID_LEN)
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;
require_noerr(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut);
require(pubKeyBlob, errOut);
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);
require_noerr(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut);
require(pubKeyBlob, errOut);
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:
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);
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,
"SecKey",
NULL,
NULL,
SecKeyDestroy,
SecKeyEqual,
NULL,
NULL,
SecKeyCopyDescription
};
kSecKeyTypeID = _CFRuntimeRegisterClass(&kSecKeyClass);
register_algs();
}
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);
}
static CFMutableDictionaryRef merge_params(CFDictionaryRef dict,
CFStringRef key) {
CFDictionaryRef subdict = CFDictionaryGetValue(dict, key);
CFMutableDictionaryRef result;
if (subdict) {
result = CFDictionaryCreateMutableCopy(NULL, 0, subdict);
CFDictionaryApplyFunction(dict, merge_params_applier, result);
} else {
result = CFDictionaryCreateMutableCopy(NULL, 0, dict);
}
CFDictionaryRemoveValue(result, kSecPublicKeyAttrs);
CFDictionaryRemoveValue(result, kSecPrivateKeyAttrs);
CFDictionaryRemoveValue(result, kSecAttrKeyType);
CFDictionaryRemoveValue(result, kSecAttrKeySizeInBits);
return result;
}
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);
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)) {
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) {
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,
SecPadding padding,
const uint8_t *dataToSign,
size_t dataToSignLen,
uint8_t *sig,
size_t *sigLen,
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;
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,
SecPadding padding,
const uint8_t *dataToSign,
size_t dataToSignLen,
uint8_t *sig,
size_t *sigLen) {
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,
SecPadding padding,
const uint8_t *signedData,
size_t signedDataLen,
const uint8_t *sig,
size_t sigLen) {
if (!key->key_class->rawVerify)
return errSecUnsupportedOperation;
if (padding < kSecPaddingPKCS1MD2) {
return key->key_class->rawVerify(key, padding, signedData, signedDataLen,
sig, sigLen);
} else {
return SecKeyDigestInfoSignVerify(key, padding,
signedData, signedDataLen, (uint8_t *)sig, &sigLen,
kSecKeyDigestInfoVerify);
}
}
OSStatus SecKeyEncrypt(
SecKeyRef key,
SecPadding padding,
const uint8_t *plainText,
size_t plainTextLen,
uint8_t *cipherText,
size_t *cipherTextLen) {
if (key->key_class->encrypt)
return key->key_class->encrypt(key, padding, plainText, plainTextLen,
cipherText, cipherTextLen);
return errSecUnsupportedOperation;
}
OSStatus SecKeyDecrypt(
SecKeyRef key,
SecPadding padding,
const uint8_t *cipherText,
size_t cipherTextLen,
uint8_t *plainText,
size_t *plainTextLen) {
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;
}
CFDictionaryRef SecKeyCopyAttributeDictionary(SecKeyRef key) {
if (key->key_class->copyDictionary)
return key->key_class->copyDictionary(key);
return NULL;
}
SecKeyRef SecKeyCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
CFAllocatorRef allocator = NULL;
CFDataRef data = CFDictionaryGetValue(refAttributes, kSecValueData);
CFTypeRef ktype = CFDictionaryGetValue(refAttributes, kSecAttrKeyType);
SInt32 algorithm;
SecKeyRef ref;
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;
}
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: switch (algorithm) {
case 42: ref = SecKeyCreateRSAPublicKey(allocator,
CFDataGetBytePtr(data), CFDataGetLength(data),
kSecKeyEncodingBytes);
break;
case 43: case 73: ref = SecKeyCreateECPublicKey(allocator,
CFDataGetBytePtr(data), CFDataGetLength(data),
kSecKeyEncodingBytes);
break;
default:
secwarning("Unsupported public key type: %@", ktype);
ref = NULL;
break;
};
break;
case 1: switch (algorithm) {
case 42: ref = SecKeyCreateRSAPrivateKey(allocator,
CFDataGetBytePtr(data), CFDataGetLength(data),
kSecKeyEncodingBytes);
break;
case 43: case 73: ref = SecKeyCreateECPrivateKey(allocator,
CFDataGetBytePtr(data), CFDataGetLength(data),
kSecKeyEncodingBytes);
break;
default:
secwarning("Unsupported private key type: %@", ktype);
ref = NULL;
break;
};
break;
case 2: secwarning("Unsupported symmetric key type: %@", ktype);
ref = NULL;
break;
default:
secwarning("Unsupported key class: %@", kclass);
ref = NULL;
}
return ref;
}
static OSStatus SecKeyGetDigestInfo(SecKeyRef this, const SecAsn1AlgId *algId,
const uint8_t *data, size_t dataLen, bool digestData,
uint8_t *digestInfo, size_t *digestInfoLen ) {
unsigned char *(*digestFcn)(const void *, CC_LONG, unsigned char *);
CFIndex keyAlgID = kSecNullAlgorithmID;
const SecAsn1Oid *digestOid;
size_t digestLen;
size_t offset = 0;
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:
digestFcn = CC_MD2;
digestLen = CC_MD2_DIGEST_LENGTH;
digestOid = &CSSMOID_MD2;
break;
case 3:
digestFcn = CC_MD4;
digestLen = CC_MD4_DIGEST_LENGTH;
digestOid = &CSSMOID_MD4;
break;
case 4:
digestFcn = CC_MD5;
digestLen = CC_MD5_DIGEST_LENGTH;
digestOid = &CSSMOID_MD5;
break;
#endif
case 5:
digestFcn = CC_SHA1;
digestLen = CC_SHA1_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA1;
break;
case 11:
digestFcn = CC_SHA256;
digestLen = CC_SHA256_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA256;
break;
case 12:
digestFcn = CC_SHA384;
digestLen = CC_SHA384_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA384;
break;
case 13:
digestFcn = CC_SHA512;
digestLen = CC_SHA512_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA512;
break;
case 14:
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:
digestFcn = CC_SHA224;
digestLen = CC_SHA224_DIGEST_LENGTH;
break;
case 2:
digestFcn = CC_SHA256;
digestLen = CC_SHA256_DIGEST_LENGTH;
break;
case 3:
digestFcn = CC_SHA384;
digestLen = CC_SHA384_DIGEST_LENGTH;
break;
case 4:
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:
digestFcn = CC_SHA224;
digestLen = CC_SHA224_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA224;
break;
case 1:
digestFcn = CC_SHA256;
digestLen = CC_SHA256_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA256;
break;
case 2:
digestFcn = CC_SHA384;
digestLen = CC_SHA384_DIGEST_LENGTH;
digestOid = &CSSMOID_SHA384;
break;
case 3:
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;
}
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)
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,
const SecAsn1AlgId *algId,
const uint8_t *dataToDigest,
size_t dataToDigestLen,
const uint8_t *sig,
size_t sigLen) {
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,
const SecAsn1AlgId *algId,
const uint8_t *dataToDigest,
size_t dataToDigestLen,
uint8_t *sig,
size_t *sigLen) {
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 SecKeyRawSign(this, kSecPaddingPKCS1,
digestInfo, digestInfoLength, sig, sigLen);
}
OSStatus SecKeyVerifyDigest(
SecKeyRef this,
const SecAsn1AlgId *algId,
const uint8_t *digestData,
size_t digestDataLen,
const uint8_t *sig,
size_t sigLen) {
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 SecKeyRawVerify(this, kSecPaddingPKCS1,
digestInfo, digestInfoLength, sig, sigLen);
}
OSStatus SecKeySignDigest(
SecKeyRef this,
const SecAsn1AlgId *algId,
const uint8_t *digestData,
size_t digestDataLen,
uint8_t *sig,
size_t *sigLen) {
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) {
if (key->key_class->version > 0 && key->key_class->getAlgorithmID)
return key->key_class->getAlgorithmID(key);
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));
}
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;
}