#include <Security/SecBase.h>
#include <Security/SecKeyInternal.h>
#include <Security/SecItem.h>
#include <Security/SecItemPriv.h>
#include <Security/SecItemShim.h>
#include <Security/SecFramework.h>
#include <Security/SecCertificate.h>
#include <utilities/SecIOFormat.h>
#include <utilities/SecCFWrappers.h>
#include <utilities/array_size.h>
#include "SecKeyPriv.h"
#include "SecRSAKeyPriv.h"
#include "SecECKeyPriv.h"
#include "SecCTKKeyPriv.h"
#include "SecBasePriv.h"
#include <CoreFoundation/CFNumber.h>
#include <CoreFoundation/CFString.h>
#include <CoreFoundation/CFPriv.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 <Security/SecureTransport.h>
#include <corecrypto/ccrng_system.h>
#include <asl.h>
#include <stdlib.h>
#include <syslog.h>
#include <libDER/asn1Types.h>
#include <libDER/DER_Keys.h>
#include <libDER/DER_Encode.h>
CFDataRef SecKeyCopyPublicKeyHash(SecKeyRef key)
{
CFDataRef pubKeyDigest = NULL, pubKeyBlob = NULL;
require_noerr_quiet(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut);
require_quiet(pubKeyBlob, errOut);
require_quiet(pubKeyDigest = SecSHA1DigestCreate(CFGetAllocator(key),
CFDataGetBytePtr(pubKeyBlob), CFDataGetLength(pubKeyBlob)),
errOut);
errOut:
CFReleaseNull(pubKeyBlob);
return pubKeyDigest;
}
static CFDictionaryRef SecKeyCopyAttributeDictionaryWithLocalKey(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_quiet(SecKeyCopyPublicBytes(key, &pubKeyBlob), errOut);
require_quiet(pubKeyBlob, errOut);
require_quiet(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, privateBlob ? kCFBooleanFalse : kCFBooleanTrue);
DICT_ADDPAIR(kSecAttrCanDecrypt, privateBlob ? kCFBooleanTrue : kCFBooleanFalse);
DICT_ADDPAIR(kSecAttrCanDerive, kCFBooleanTrue);
DICT_ADDPAIR(kSecAttrCanSign, privateBlob ? kCFBooleanTrue : kCFBooleanFalse);
DICT_ADDPAIR(kSecAttrCanVerify, privateBlob ? kCFBooleanFalse : kCFBooleanTrue);
DICT_ADDPAIR(kSecAttrCanSignRecover, kCFBooleanFalse);
DICT_ADDPAIR(kSecAttrCanVerifyRecover, kCFBooleanFalse);
DICT_ADDPAIR(kSecAttrCanWrap, privateBlob ? kCFBooleanFalse : kCFBooleanTrue);
DICT_ADDPAIR(kSecAttrCanUnwrap, privateBlob ? kCFBooleanTrue : kCFBooleanFalse);
DICT_ADDPAIR(kSecValueData, privateBlob ? privateBlob : pubKeyBlob);
dict = CFDictionaryCreate(allocator, keys, values, numValues, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
errOut:
CFReleaseSafe(pubKeyDigest);
CFReleaseSafe(pubKeyBlob);
CFReleaseSafe(sizeInBits);
return dict;
}
CFDictionaryRef SecKeyGeneratePrivateAttributeDictionary(SecKeyRef key,
CFTypeRef keyType,
CFDataRef privateBlob)
{
return SecKeyCopyAttributeDictionaryWithLocalKey(key, keyType, privateBlob);
}
CFDictionaryRef SecKeyGeneratePublicAttributeDictionary(SecKeyRef key, CFTypeRef keyType)
{
return SecKeyCopyAttributeDictionaryWithLocalKey(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 TARGET_OS_OSX
CFReleaseNull(key->cdsaKey);
#endif
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->version >= 4 && key1->key_class->isEqual)
return key1->key_class->isEqual(key1, key2);
if (key1->key_class->extraBytes)
return !memcmp(key1->key, key2->key, key1->key_class->extraBytes);
CFDictionaryRef d1, d2;
d1 = SecKeyCopyAttributeDictionary(key1);
d2 = SecKeyCopyAttributeDictionary(key2);
if(!d1 || !d2) {
CFReleaseSafe(d1);
CFReleaseSafe(d2);
return false;
}
Boolean result = CFEqual(d1, d2);
CFReleaseSafe(d1);
CFReleaseSafe(d2);
return result;
}
struct ccrng_state *ccrng_seckey;
CFGiblisWithFunctions(SecKey, NULL, NULL, SecKeyDestroy, SecKeyEqual, NULL, NULL, SecKeyCopyDescription, NULL, NULL, ^{
static struct ccrng_system_state ccrng_system_state_seckey;
ccrng_seckey = (struct ccrng_state *)&ccrng_system_state_seckey;
ccrng_system_init(&ccrng_system_state_seckey);
})
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 CF_RETURNS_RETAINED 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;
}
CFIndex SecKeyGetAlgorithmId(SecKeyRef key) {
if (!key || !key->key_class) {
return kSecNullAlgorithmID;
}
if (key->key_class->version > 0 && key->key_class->getAlgorithmID) {
return key->key_class->getAlgorithmID(key);
}
return kSecRSAAlgorithmID;
}
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);
CFStringRef tokenID = CFDictionaryGetValue(parameters, kSecAttrTokenID);
require_quiet(ktype, errOut);
if (tokenID != NULL) {
result = SecCTKKeyGeneratePair(parameters, &pubKey, &privKey);
} else if (CFEqual(ktype, kSecAttrKeyTypeECSECPrimeRandom)) {
result = SecECKeyGeneratePair(parameters, &pubKey, &privKey);
} else if (CFEqual(ktype, kSecAttrKeyTypeRSA)) {
result = SecRSAKeyGeneratePair(parameters, &pubKey, &privKey);
}
require_noerr_quiet(result, errOut);
if (getBoolForKey(pubParams, kSecAttrIsPermanent, false)) {
CFDictionaryRemoveValue(pubParams, kSecAttrTokenID);
require_noerr_quiet(result = add_ref(pubKey, pubParams), errOut);
}
if (getBoolForKey(privParams, kSecAttrIsPermanent, CFDictionaryContainsKey(privParams, kSecAttrTokenID))) {
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) {
return SecKeyCopyPublicKey(privateKey);
}
CFDictionaryRef CreatePrivateKeyMatchingQuery(SecKeyRef publicKey, bool returnPersistentRef)
{
const CFTypeRef refType = (returnPersistentRef) ? kSecReturnPersistentRef: kSecReturnRef;
CFDataRef public_key_hash = SecKeyCopyPublicKeyHash(publicKey);
CFDictionaryRef query = CFDictionaryCreateForCFTypes(kCFAllocatorDefault,
kSecClass, kSecClassKey,
kSecAttrKeyClass, kSecAttrKeyClassPrivate,
kSecAttrSynchronizable, kSecAttrSynchronizableAny,
kSecAttrApplicationLabel, public_key_hash,
refType, 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) {
SecKeyRef privateKey = NULL;
CFTypeRef queryResult = NULL;
CFDictionaryRef query = NULL;
require_action_quiet(publicKey != NULL, errOut, SecError(errSecParam, error, CFSTR("Null Public Key")));
query = CreatePrivateKeyMatchingQuery(publicKey, false);
require_quiet(SecError(SecItemCopyMatching(query, &queryResult), error,
CFSTR("Error finding private key from public: %@"), publicKey), errOut);
if (CFGetTypeID(queryResult) == SecKeyGetTypeID()) {
privateKey = (SecKeyRef) queryResult;
queryResult = NULL;
}
errOut:
CFReleaseNull(query);
CFReleaseNull(queryResult);
return privateKey;
}
OSStatus SecKeyGetMatchingPrivateKeyStatus(SecKeyRef publicKey, CFErrorRef *error) {
OSStatus retval = errSecParam;
CFTypeRef private_key = NULL;
CFDictionaryRef query = NULL;
require_action_quiet(publicKey != NULL, errOut, SecError(errSecParam, error, NULL, CFSTR("Null Public Key")));
query = CreatePrivateKeyMatchingQuery(publicKey, false);
retval = SecItemCopyMatching(query, &private_key);
if (!retval && CFGetTypeID(private_key) != SecKeyGetTypeID()) {
retval = errSecInternalComponent;
}
errOut:
CFReleaseNull(query);
CFReleaseNull(private_key);
return retval;
}
SecKeyRef SecKeyCreatePublicFromDER(CFAllocatorRef allocator,
const SecAsn1Oid *oid, const SecAsn1Item *params,
const SecAsn1Item *keyData) {
SecKeyRef publicKey = NULL;
if (SecAsn1OidCompare(oid, &CSSMOID_RSA)) {
publicKey = SecKeyCreateRSAPublicKey_ios(allocator,
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(allocator,
(const uint8_t *)&derKey, sizeof(derKey), kSecDERKeyEncoding);
} else {
secwarning("Unsupported algorithm oid");
}
return publicKey;
}
SecKeyRef SecKeyCreateFromSubjectPublicKeyInfoData(CFAllocatorRef allocator, CFDataRef subjectPublicKeyInfoData)
{
DERReturn drtn;
DERItem subjectPublicKeyInfoDER = {
.data = (uint8_t *)CFDataGetBytePtr(subjectPublicKeyInfoData),
.length = (DERSize)CFDataGetLength(subjectPublicKeyInfoData),
};
DERSubjPubKeyInfo subjectPublicKeyInfo;
DERAlgorithmId algorithmId;
DERItem pubKeyBytes;
drtn = DERParseSequence(&subjectPublicKeyInfoDER,
DERNumSubjPubKeyInfoItemSpecs, DERSubjPubKeyInfoItemSpecs,
&subjectPublicKeyInfo, sizeof(subjectPublicKeyInfo));
require_noerr_quiet(drtn, out);
drtn = DERParseSequenceContent(&subjectPublicKeyInfo.algId,
DERNumAlgorithmIdItemSpecs, DERAlgorithmIdItemSpecs,
&algorithmId, sizeof(algorithmId));
require_noerr_quiet(drtn, out);
DERByte unusedBits;
drtn = DERParseBitString(&subjectPublicKeyInfo.pubKey, &pubKeyBytes, &unusedBits);
require_noerr_quiet(drtn, out);
const SecAsn1Oid oid = { .Data = algorithmId.oid.data, .Length = algorithmId.oid.length };
const SecAsn1Item params = { .Data = algorithmId.params.data, .Length = algorithmId.params.length };
const SecAsn1Item pubKey = { .Data = pubKeyBytes.data, .Length = pubKeyBytes.length };
return SecKeyCreatePublicFromDER(allocator, &oid, ¶ms, &pubKey);
out:
return NULL;
}
static const DERByte oidRSA[] = {
0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00,
};
static const DERByte oidECsecp256[] = {
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x08, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07,
};
static const DERByte oidECsecp384[] = {
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x22,
};
static const DERByte oidECsecp521[] = {
0x06, 0x07, 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, 0x06, 0x05, 0x2b, 0x81, 0x04, 0x00, 0x23,
};
CFDataRef SecKeyCopySubjectPublicKeyInfo(SecKeyRef key)
{
CFMutableDataRef data = NULL;
CFDataRef publicKey = NULL;
CFDataRef dataret = NULL;
DERSubjPubKeyInfo spki;
DERReturn drtn;
size_t zeroPad = 0;
memset(&spki, 0, sizeof(spki));
require_noerr_quiet(SecKeyCopyPublicBytes(key, &publicKey), errOut);
require_quiet(publicKey, errOut);
require_quiet(CFDataGetLength(publicKey) != 0, errOut);
if (((uint8_t *)CFDataGetBytePtr(publicKey))[0] & 0x80)
zeroPad = 1;
CFMutableDataRef paddedKey = CFDataCreateMutable(NULL, 0);
CFDataAppendBytes(paddedKey, (const UInt8 *)"\x00", 1);
if (zeroPad)
CFDataAppendBytes(paddedKey, (const UInt8 *)"\x00", 1);
CFDataAppendBytes(paddedKey, CFDataGetBytePtr(publicKey), CFDataGetLength(publicKey));
CFTransferRetained(publicKey, paddedKey);
spki.pubKey.data = (DERByte *)CFDataGetBytePtr(publicKey);
spki.pubKey.length = CFDataGetLength(publicKey);
CFIndex algorithm = SecKeyGetAlgorithmId(key);
if (algorithm == kSecRSAAlgorithmID) {
spki.algId.data = (DERByte *)oidRSA;
spki.algId.length = sizeof(oidRSA);
} else if (algorithm == kSecECDSAAlgorithmID) {
SecECNamedCurve curve = SecECKeyGetNamedCurve(key);
switch(curve) {
case kSecECCurveSecp256r1:
spki.algId.data = (DERByte *)oidECsecp256;
spki.algId.length = sizeof(oidECsecp256);
break;
case kSecECCurveSecp384r1:
spki.algId.data = (DERByte *)oidECsecp384;
spki.algId.length = sizeof(oidECsecp384);
break;
case kSecECCurveSecp521r1:
spki.algId.data = (DERByte *)oidECsecp521;
spki.algId.length = sizeof(oidECsecp521);
break;
default:
goto errOut;
}
} else {
goto errOut;
}
DERSize size = DERLengthOfEncodedSequence(ASN1_CONSTR_SEQUENCE, &spki,
DERNumSubjPubKeyInfoItemSpecs, DERSubjPubKeyInfoItemSpecs);
data = CFDataCreateMutable(kCFAllocatorDefault, size);
CFDataSetLength(data, size);
drtn = DEREncodeSequence(ASN1_CONSTR_SEQUENCE, &spki,
DERNumSubjPubKeyInfoItemSpecs,
DERSubjPubKeyInfoItemSpecs,
CFDataGetMutableBytePtr(data), &size);
require_quiet(drtn == DR_Success, errOut);
CFDataSetLength(data, size);
dataret = CFRetain(data);
errOut:
CFReleaseNull(data);
CFReleaseNull(publicKey);
return dataret;
}
SecKeyRef SecKeyCreate(CFAllocatorRef allocator,
const SecKeyDescriptor *key_class, const uint8_t *keyData,
CFIndex keyDataLength, SecKeyEncoding encoding) {
if (!key_class) return NULL;
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;
}
static SecKeyAlgorithm SecKeyGetSignatureAlgorithmForPadding(SecKeyRef key, SecPadding padding) {
switch (SecKeyGetAlgorithmId(key)) {
case kSecRSAAlgorithmID: {
#if TARGET_OS_OSX
if (!_CFMZEnabled()) {
switch (padding) {
case kSecPaddingNone:
return kSecKeyAlgorithmRSASignatureRaw;
case kSecPaddingPKCS1:
return kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw;
case kSecPaddingPKCS1SHA1:
return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1;
case kSecPaddingPKCS1SHA224:
return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA224;
case kSecPaddingPKCS1SHA256:
return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256;
case kSecPaddingPKCS1SHA384:
return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384;
case kSecPaddingPKCS1SHA512:
return kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512;
default:
return NULL;
}
} else
#endif
{
switch (padding) {
case kSecPaddingNone:
return kSecKeyAlgorithmRSASignatureRaw;
case kSecPaddingPKCS1:
return kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw;
case kSecPaddingPKCS1SHA1:
return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1;
case kSecPaddingPKCS1SHA224:
return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA224;
case kSecPaddingPKCS1SHA256:
return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256;
case kSecPaddingPKCS1SHA384:
return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384;
case kSecPaddingPKCS1SHA512:
return kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512;
default:
return NULL;
}
}
}
case kSecECDSAAlgorithmID:
switch (padding) {
case kSecPaddingSigRaw:
return kSecKeyAlgorithmECDSASignatureRFC4754;
default:
return kSecKeyAlgorithmECDSASignatureDigestX962;
}
default:
return NULL;
}
}
static OSStatus SecKeyPerformLegacyOperation(SecKeyRef key,
const uint8_t *in1Ptr, size_t in1Len,
const uint8_t *in2Ptr, size_t in2Len,
uint8_t *outPtr, size_t *outLen,
CFTypeRef (^operation)(CFDataRef in1, CFDataRef in2, CFRange *resultRange, CFErrorRef *error)) {
CFErrorRef error = NULL;
OSStatus status = errSecSuccess;
CFDataRef in1 = CFDataCreateWithBytesNoCopy(NULL, in1Ptr, in1Len, kCFAllocatorNull);
CFDataRef in2 = CFDataCreateWithBytesNoCopy(NULL, in2Ptr, in2Len, kCFAllocatorNull);
CFRange range = { 0, -1 };
CFTypeRef output = operation(in1, in2, &range, &error);
require_quiet(output, out);
if (CFGetTypeID(output) == CFDataGetTypeID() && outLen != NULL) {
if (range.length == -1) {
range.length = CFDataGetLength(output);
}
require_action_quiet((size_t)range.length <= *outLen, out,
SecError(errSecParam, &error, CFSTR("buffer too small")));
*outLen = range.length;
CFDataGetBytes(output, range, outPtr);
}
out:
CFReleaseSafe(in1);
CFReleaseSafe(in2);
CFReleaseSafe(output);
if (error != NULL) {
status = SecErrorGetOSStatus(error);
if (status == errSecVerifyFailed) {
status = errSSLCrypto;
}
CFRelease(error);
}
return status;
}
OSStatus SecKeyRawSign(
SecKeyRef key,
SecPadding padding,
const uint8_t *dataToSign,
size_t dataToSignLen,
uint8_t *sig,
size_t *sigLen) {
SecKeyAlgorithm algorithm = SecKeyGetSignatureAlgorithmForPadding(key, padding);
if (algorithm == NULL) {
return errSecParam;
}
return SecKeyPerformLegacyOperation(key, dataToSign, dataToSignLen, NULL, 0, sig, sigLen,
^CFTypeRef(CFDataRef in1, CFDataRef in2, CFRange *range, CFErrorRef *error) {
return SecKeyCreateSignature(key, algorithm, in1, error);
});
}
OSStatus SecKeyRawVerify(
SecKeyRef key,
SecPadding padding,
const uint8_t *signedData,
size_t signedDataLen,
const uint8_t *sig,
size_t sigLen) {
SecKeyAlgorithm algorithm = SecKeyGetSignatureAlgorithmForPadding(key, padding);
if (algorithm == NULL) {
return errSecParam;
}
OSStatus status = SecKeyPerformLegacyOperation(key, signedData, signedDataLen, sig, sigLen, NULL, NULL,
^CFTypeRef(CFDataRef in1, CFDataRef in2, CFRange *range, CFErrorRef *error) {
return SecKeyVerifySignature(key, algorithm, in1, in2, error)
? kCFBooleanTrue : NULL;
});
return status;
}
static SecKeyAlgorithm SecKeyGetEncryptionAlgorithmForPadding(SecKeyRef key, SecPadding padding) {
switch (SecKeyGetAlgorithmId(key)) {
case kSecRSAAlgorithmID:
switch (padding) {
case kSecPaddingNone:
return kSecKeyAlgorithmRSAEncryptionRaw;
case kSecPaddingPKCS1:
return kSecKeyAlgorithmRSAEncryptionPKCS1;
case kSecPaddingOAEP:
return kSecKeyAlgorithmRSAEncryptionOAEPSHA1;
default:
return NULL;
}
default:
return NULL;
}
}
OSStatus SecKeyEncrypt(
SecKeyRef key,
SecPadding padding,
const uint8_t *plainText,
size_t plainTextLen,
uint8_t *cipherText,
size_t *cipherTextLen) {
SecKeyAlgorithm algorithm = SecKeyGetEncryptionAlgorithmForPadding(key, padding);
if (algorithm == NULL) {
return errSecParam;
}
return SecKeyPerformLegacyOperation(key, plainText, plainTextLen, NULL, 0, cipherText, cipherTextLen,
^CFTypeRef(CFDataRef in1, CFDataRef in2, CFRange *range, CFErrorRef *error) {
return SecKeyCreateEncryptedData(key, algorithm, in1, error);
});
}
OSStatus SecKeyDecrypt(
SecKeyRef key,
SecPadding padding,
const uint8_t *cipherText,
size_t cipherTextLen,
uint8_t *plainText,
size_t *plainTextLen) {
SecKeyAlgorithm algorithm = SecKeyGetEncryptionAlgorithmForPadding(key, padding);
if (algorithm == NULL) {
return errSecParam;
}
return SecKeyPerformLegacyOperation(key, cipherText, cipherTextLen, NULL, 0, plainText, plainTextLen,
^CFTypeRef(CFDataRef in1, CFDataRef in2, CFRange *range, CFErrorRef *error) {
CFDataRef decrypted = SecKeyCreateDecryptedData(key, algorithm, in1, error);
const UInt8 *data;
if (decrypted != NULL && algorithm == kSecKeyAlgorithmRSAEncryptionRaw &&
*(data = CFDataGetBytePtr(decrypted)) == 0x00) {
range->length = CFDataGetLength(decrypted);
while (*data == 0x00 && range->length > 0) {
range->location++;
range->length--;
data++;
}
}
return decrypted;
});
}
size_t SecKeyGetBlockSize(SecKeyRef key) {
if (key->key_class->blockSize)
return key->key_class->blockSize(key);
return 0;
}
CFDictionaryRef SecKeyCopyAttributeDictionary(SecKeyRef key) {
return SecKeyCopyAttributes(key);
}
SecKeyRef SecKeyCreateFromAttributeDictionary(CFDictionaryRef refAttributes) {
CFErrorRef error = NULL;
SecKeyRef key = SecKeyCreateWithData(CFDictionaryGetValue(refAttributes, kSecValueData), refAttributes, &error);
if (key == NULL) {
CFStringRef description = CFErrorCopyDescription(error);
secwarning("%@", description);
CFRelease(description);
CFRelease(error);
}
return key;
}
static SecKeyAlgorithm SecKeyGetAlgorithmForSecAsn1AlgId(SecKeyRef key, const SecAsn1AlgId *algId, bool digestData) {
static const struct TableItem {
const SecAsn1Oid *oid1, *oid2;
const SecKeyAlgorithm *algorithms[2];
} translationTableRSA[] = {
{ &CSSMOID_SHA1WithRSA, &CSSMOID_SHA1, {
[false] = &kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1,
[true] = &kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA1,
} },
{ &CSSMOID_SHA224WithRSA, &CSSMOID_SHA224, {
[false] = &kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA224,
[true] = &kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA224,
} },
{ &CSSMOID_SHA256WithRSA, &CSSMOID_SHA256, {
[false] = &kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256,
[true] = &kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA256,
} },
{ &CSSMOID_SHA384WithRSA, &CSSMOID_SHA384, {
[false] = &kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384,
[true] = &kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA384,
} },
{ &CSSMOID_SHA512WithRSA, &CSSMOID_SHA512, {
[false] = &kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512,
[true] = &kSecKeyAlgorithmRSASignatureMessagePKCS1v15SHA512,
} },
{ &CSSMOID_MD5, NULL, {
[false] = &kSecKeyAlgorithmRSASignatureDigestPKCS1v15MD5,
[true] = &kSecKeyAlgorithmRSASignatureMessagePKCS1v15MD5,
} },
{ &CSSMOID_MD5WithRSA, NULL, {
[false] = &kSecKeyAlgorithmRSASignatureDigestPKCS1v15MD5,
[true] = &kSecKeyAlgorithmRSASignatureMessagePKCS1v15MD5,
} },
{ NULL },
}, translationTableECDSA[] = {
{ &CSSMOID_ECDSA_WithSHA1, &CSSMOID_SHA1, {
[false] = &kSecKeyAlgorithmECDSASignatureDigestX962,
[true] = &kSecKeyAlgorithmECDSASignatureMessageX962SHA1,
} },
{ &CSSMOID_ECDSA_WithSHA224, &CSSMOID_SHA224, {
[false] = &kSecKeyAlgorithmECDSASignatureDigestX962,
[true] = &kSecKeyAlgorithmECDSASignatureMessageX962SHA224,
} },
{ &CSSMOID_ECDSA_WithSHA256, &CSSMOID_SHA256, {
[false] = &kSecKeyAlgorithmECDSASignatureDigestX962,
[true] = &kSecKeyAlgorithmECDSASignatureMessageX962SHA256,
} },
{ &CSSMOID_ECDSA_WithSHA384, &CSSMOID_SHA384, {
[false] = &kSecKeyAlgorithmECDSASignatureDigestX962,
[true] = &kSecKeyAlgorithmECDSASignatureMessageX962SHA384,
} },
{ &CSSMOID_ECDSA_WithSHA512, &CSSMOID_SHA512, {
[false] = &kSecKeyAlgorithmECDSASignatureDigestX962,
[true] = &kSecKeyAlgorithmECDSASignatureMessageX962SHA512,
} },
{ NULL },
};
const struct TableItem *table;
switch (SecKeyGetAlgorithmId(key)) {
case kSecRSAAlgorithmID:
table = translationTableRSA;
break;
case kSecECDSAAlgorithmID:
table = translationTableECDSA;
break;
default:
return NULL;
}
for (; table->oid1 != NULL; table++) {
if (SecAsn1OidCompare(table->oid1, &algId->algorithm) ||
(table->oid2 != NULL && SecAsn1OidCompare(table->oid2, &algId->algorithm))) {
return *table->algorithms[digestData];
}
}
return NULL;
}
OSStatus SecKeyDigestAndVerify(
SecKeyRef key,
const SecAsn1AlgId *algId,
const uint8_t *dataToDigest,
size_t dataToDigestLen,
const uint8_t *sig,
size_t sigLen) {
SecKeyAlgorithm algorithm = SecKeyGetAlgorithmForSecAsn1AlgId(key, algId, true);
if (algorithm == NULL) {
return errSecUnimplemented;
}
return SecKeyPerformLegacyOperation(key, dataToDigest, dataToDigestLen, sig, sigLen, NULL, NULL,
^CFTypeRef(CFDataRef in1, CFDataRef in2, CFRange *range, CFErrorRef *error) {
return SecKeyVerifySignature(key, algorithm, in1, in2, error) ?
kCFBooleanTrue : NULL;
});
}
OSStatus SecKeyDigestAndSign(
SecKeyRef key,
const SecAsn1AlgId *algId,
const uint8_t *dataToDigest,
size_t dataToDigestLen,
uint8_t *sig,
size_t *sigLen) {
SecKeyAlgorithm algorithm = SecKeyGetAlgorithmForSecAsn1AlgId(key, algId, true);
if (algorithm == NULL) {
return errSecUnimplemented;
}
return SecKeyPerformLegacyOperation(key, dataToDigest, dataToDigestLen, NULL, 0, sig, sigLen,
^CFTypeRef(CFDataRef in1, CFDataRef in2, CFRange *range, CFErrorRef *error) {
return SecKeyCreateSignature(key, algorithm, in1, error);
});
}
OSStatus SecKeyVerifyDigest(
SecKeyRef key,
const SecAsn1AlgId *algId,
const uint8_t *digestData,
size_t digestDataLen,
const uint8_t *sig,
size_t sigLen) {
SecKeyAlgorithm algorithm = SecKeyGetAlgorithmForSecAsn1AlgId(key, algId, false);
if (algorithm == NULL) {
return errSecUnimplemented;
}
return SecKeyPerformLegacyOperation(key, digestData, digestDataLen, sig, sigLen, NULL, NULL,
^CFTypeRef(CFDataRef in1, CFDataRef in2, CFRange *range, CFErrorRef *error) {
return SecKeyVerifySignature(key, algorithm, in1, in2, error) ?
kCFBooleanTrue : NULL;
});
}
OSStatus SecKeySignDigest(
SecKeyRef key,
const SecAsn1AlgId *algId,
const uint8_t *digestData,
size_t digestDataLen,
uint8_t *sig,
size_t *sigLen) {
SecKeyAlgorithm algorithm = SecKeyGetAlgorithmForSecAsn1AlgId(key, algId, false);
if (algorithm == NULL) {
return errSecUnimplemented;
}
return SecKeyPerformLegacyOperation(key, digestData, digestDataLen, NULL, 0, sig, sigLen,
^CFTypeRef(CFDataRef in1, CFDataRef in2, CFRange *range, CFErrorRef *error) {
return SecKeyCreateSignature(key, algorithm, in1, error);
});
}
#if TARGET_OS_OSX
#else
#undef SecKeyGetAlgorithmID
CFIndex SecKeyGetAlgorithmID(SecKeyRef key);
CFIndex SecKeyGetAlgorithmID(SecKeyRef key) {
return SecKeyGetAlgorithmId(key);
}
#endif
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 (whichSize == 0 || whichSize == 10) {
whichSize = kSecKeyKeySizeInBits;
}
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)
{
if (!key) {
secerror("SecKeyCopyPersistentRef: Need a key reference for this to work");
return errSecParam;
}
if (!persistentRef) {
secerror("SecKeyCopyPersistentRef: Need a persistentRef pointer for this to work");
return errSecParam;
}
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 {
secerror("SecKeyCopyPersistentRef: SecItemCopyMatching returned success, but we got type %lu instead of CFData for key %@.", CFGetTypeID(foundRef), key);
status = errSecItemNotFound;
}
} else {
secerror("SecKeyCopyPersistentRef: received status %i for key %@", (int)status, key);
CFStringRef str = CFStringCreateWithFormat(NULL, NULL, CFSTR("Expected to find persistentref for key %@"), key);
__security_stackshotreport(str, (int)status);
CFReleaseNull(str);
}
CFReleaseSafe(foundRef);
CFReleaseSafe(query);
return status;
}
#define SEC_CONST_DECL(k,v) const CFStringRef k = CFSTR(v);
SEC_CONST_DECL(_kSecKeyWrapPGPSymAlg, "kSecKeyWrapPGPSymAlg");
SEC_CONST_DECL(_kSecKeyWrapPGPFingerprint, "kSecKeyWrapPGPFingerprint");
SEC_CONST_DECL(_kSecKeyWrapPGPWrapAlg, "kSecKeyWrapPGPWrapAlg");
SEC_CONST_DECL(_kSecKeyWrapRFC6637Flags, "kSecKeyWrapPGPECFlags");
SEC_CONST_DECL(_kSecKeyWrapRFC6637WrapDigestSHA256KekAES128, "kSecKeyWrapPGPECWrapDigestSHA256KekAES128");
SEC_CONST_DECL(_kSecKeyWrapRFC6637WrapDigestSHA512KekAES256, "kSecKeyWrapPGPECWrapDigestSHA512KekAES256");
#undef SEC_CONST_DECL
CFDataRef
_SecKeyCopyWrapKey(SecKeyRef key, SecKeyWrapType type, CFDataRef unwrappedKey, CFDictionaryRef parameters, CFDictionaryRef *outParam, CFErrorRef *error)
{
if (error)
*error = NULL;
if (outParam)
*outParam = NULL;
if (key->key_class->version > 2 && key->key_class->copyWrapKey)
return key->key_class->copyWrapKey(key, type, unwrappedKey, parameters, outParam, error);
SecError(errSecUnsupportedOperation, error, CFSTR("No key wrap supported for key %@"), key);
return NULL;
}
CFDataRef
_SecKeyCopyUnwrapKey(SecKeyRef key, SecKeyWrapType type, CFDataRef wrappedKey, CFDictionaryRef parameters, CFDictionaryRef *outParam, CFErrorRef *error)
{
if (error)
*error = NULL;
if (outParam)
*outParam = NULL;
if (key->key_class->version > 2 && key->key_class->copyUnwrapKey)
return key->key_class->copyUnwrapKey(key, type, wrappedKey, parameters, outParam, error);
SecError(errSecUnsupportedOperation, error, CFSTR("No key unwrap for key %@"), key);
return NULL;
}
static CFIndex SecKeyParamsGetCFIndex(CFTypeRef value, CFStringRef errName, CFErrorRef *error) {
CFIndex result = -1;
CFNumberRef localValue = NULL;
if (isString(value)) {
CFNumberFormatterRef formatter = CFNumberFormatterCreate(kCFAllocatorDefault, CFLocaleGetSystem(), kCFNumberFormatterDecimalStyle);
localValue = CFNumberFormatterCreateNumberFromString(kCFAllocatorDefault, formatter, value, NULL, kCFNumberFormatterParseIntegersOnly);
CFReleaseSafe(formatter);
if (localValue) {
CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%@"), localValue);
if (CFEqual(t, value)) {
value = localValue;
}
CFReleaseSafe(t);
}
}
if (value != NULL && CFGetTypeID(value) == CFNumberGetTypeID()) {
if (!CFNumberGetValue(value, kCFNumberCFIndexType, &result) || result < 0) {
SecError(errSecParam, error, CFSTR("Unsupported %@: %@"), errName, value);
}
} else {
SecError(errSecParam, error, CFSTR("Unsupported %@: %@"), errName, value);
}
CFReleaseSafe(localValue);
return result;
}
SecKeyRef SecKeyCreateWithData(CFDataRef keyData, CFDictionaryRef parameters, CFErrorRef *error) {
SecKeyRef key = NULL;
CFAllocatorRef allocator = NULL;
if (CFDictionaryGetValue(parameters, kSecAttrTokenID) != NULL) {
return SecKeyCreateCTKKey(allocator, parameters, error);
}
else if (!keyData) {
SecError(errSecParam, error, CFSTR("Failed to provide key data to SecKeyCreateWithData"));
return NULL;
}
CFIndex algorithm, class;
CFTypeRef ktype = CFDictionaryGetValue(parameters, kSecAttrKeyType);
require_quiet((algorithm = SecKeyParamsGetCFIndex(ktype, CFSTR("key type"), error)) >= 0, out);
CFTypeRef kclass = CFDictionaryGetValue(parameters, kSecAttrKeyClass);
require_quiet((class = SecKeyParamsGetCFIndex(kclass, CFSTR("key class"), error)) >= 0, out);
switch (class) {
case 0: switch (algorithm) {
case 42: key = SecKeyCreateRSAPublicKey(allocator,
CFDataGetBytePtr(keyData), CFDataGetLength(keyData),
kSecKeyEncodingBytes);
if (key == NULL) {
SecError(errSecParam, error, CFSTR("RSA public key creation from data failed"));
}
break;
case 43: case 73: key = SecKeyCreateECPublicKey(allocator,
CFDataGetBytePtr(keyData), CFDataGetLength(keyData),
kSecKeyEncodingBytes);
if (key == NULL) {
SecError(errSecParam, error, CFSTR("EC public key creation from data failed"));
}
break;
default:
SecError(errSecParam, error, CFSTR("Unsupported public key type: %@"), ktype);
break;
};
break;
case 1: switch (algorithm) {
case 42: key = SecKeyCreateRSAPrivateKey(allocator,
CFDataGetBytePtr(keyData), CFDataGetLength(keyData),
kSecKeyEncodingBytes);
if (key == NULL) {
SecError(errSecParam, error, CFSTR("RSA private key creation from data failed"));
}
break;
case 43: case 73: key = SecKeyCreateECPrivateKey(allocator,
CFDataGetBytePtr(keyData), CFDataGetLength(keyData),
kSecKeyEncodingBytes);
if (key == NULL) {
SecError(errSecParam, error, CFSTR("EC public key creation from data failed"));
}
break;
default:
SecError(errSecParam, error, CFSTR("Unsupported private key type: %@"), ktype);
break;
};
break;
case 2: SecError(errSecUnimplemented, error, CFSTR("Unsupported symmetric key type: %@"), ktype);
break;
default:
SecError(errSecParam, error, CFSTR("Unsupported key class: %@"), kclass);
break;
}
out:
return key;
}
static inline bool SecKeyErrorPropagate(bool succeeded, CFErrorRef possibleError CF_CONSUMED, CFErrorRef *error) {
if (succeeded) {
return true;
} else {
if (error) {
*error = possibleError;
} else {
CFRelease(possibleError);
}
return false;
}
}
CFDataRef SecKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error) {
if (!key->key_class->copyExternalRepresentation) {
if (error != NULL) {
*error = NULL;
}
SecError(errSecUnimplemented, error, CFSTR("export not implemented for key %@"), key);
return NULL;
}
CFErrorRef localError = NULL;
CFDataRef result = key->key_class->copyExternalRepresentation(key, &localError);
SecKeyErrorPropagate(result != NULL, localError, error);
return result;
}
CFDictionaryRef SecKeyCopyAttributes(SecKeyRef key) {
if (key->key_class->copyDictionary) {
return key->key_class->copyDictionary(key);
} else {
CFMutableDictionaryRef dict = CFDictionaryCreateMutableForCFTypes(kCFAllocatorDefault);
CFIndex blockSize = SecKeyGetBlockSize(key) * 8;
if (blockSize > 0) {
CFNumberRef blockSizeRef = CFNumberCreate(kCFAllocatorDefault, kCFNumberCFIndexType, &blockSize);
CFDictionarySetValue(dict, kSecAttrKeySizeInBits, blockSizeRef);
CFRelease(blockSizeRef);
}
switch (SecKeyGetAlgorithmId(key)) {
case kSecRSAAlgorithmID:
CFDictionarySetValue(dict, kSecAttrKeyType, kSecAttrKeyTypeRSA);
break;
case kSecECDSAAlgorithmID:
CFDictionarySetValue(dict, kSecAttrKeyType, kSecAttrKeyTypeECSECPrimeRandom);
break;
}
if (key->key_class->rawSign != NULL || key->key_class->decrypt != NULL) {
CFDictionarySetValue(dict, kSecAttrKeyClass, kSecAttrKeyClassPrivate);
} else if (key->key_class->rawVerify != NULL || key->key_class->encrypt != NULL) {
CFDictionarySetValue(dict, kSecAttrKeyClass, kSecAttrKeyClassPublic);
}
return dict;
}
}
SecKeyRef SecKeyCopyPublicKey(SecKeyRef key) {
SecKeyRef result = NULL;
if (key->key_class->version >= 4 && key->key_class->copyPublicKey) {
result = key->key_class->copyPublicKey(key);
if (result != NULL) {
return result;
}
}
CFDataRef serializedPublic = NULL;
require_noerr_quiet(SecKeyCopyPublicBytes(key, &serializedPublic), fail);
require_quiet(serializedPublic, fail);
result = SecKeyCreateFromPublicData(kCFAllocatorDefault, SecKeyGetAlgorithmId(key), serializedPublic);
fail:
CFReleaseSafe(serializedPublic);
return result;
}
SecKeyRef SecKeyCreateRandomKey(CFDictionaryRef parameters, CFErrorRef *error) {
SecKeyRef privKey = NULL, pubKey = NULL;
OSStatus status = SecKeyGeneratePair(parameters, &pubKey, &privKey);
if (status != errSecSuccess) {
if (error != NULL) {
*error = NULL;
}
SecError(status, error, CFSTR("Key generation failed, error %d"), (int)status);
}
CFReleaseSafe(pubKey);
return privKey;
}
SecKeyRef SecKeyCreateDuplicate(SecKeyRef key) {
if (key->key_class->version >= 4 && key->key_class->createDuplicate) {
return key->key_class->createDuplicate(key);
} else {
return (SecKeyRef)CFRetain(key);
}
}
Boolean SecKeySetParameter(SecKeyRef key, CFStringRef name, CFPropertyListRef value, CFErrorRef *error) {
if (key == NULL) {
SecCTKKeySetTestMode(name, value);
return true;
} else if (key->key_class->version >= 4 && key->key_class->setParameter) {
CFErrorRef localError = NULL;
Boolean result = key->key_class->setParameter(key, name, value, &localError);
SecKeyErrorPropagate(result, localError, error);
return result;
} else {
if (error != NULL) {
*error = NULL;
}
return SecError(errSecUnimplemented, error, CFSTR("setParameter not implemented for %@"), key);
}
}
#pragma mark Generic algorithm adaptor lookup and invocation
static CFTypeRef SecKeyCopyBackendOperationResult(SecKeyOperationContext *context, SecKeyAlgorithm algorithm,
CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
CFTypeRef result = kCFNull;
assert(CFArrayGetCount(context->algorithm) > 0);
if (context->key->key_class->version >= 4 && context->key->key_class->copyOperationResult != NULL) {
return context->key->key_class->copyOperationResult(context->key, context->operation, algorithm,
context->algorithm, context->mode, in1, in2, error);
}
static const struct {
const SecKeyAlgorithm *algorithm;
CFIndex keyAlg;
SecPadding padding;
} paddingMap[] = {
{ &kSecKeyAlgorithmRSASignatureRaw, kSecRSAAlgorithmID, kSecPaddingNone },
{ &kSecKeyAlgorithmRSASignatureDigestPKCS1v15Raw, kSecRSAAlgorithmID, kSecPaddingPKCS1 },
{ &kSecKeyAlgorithmECDSASignatureRFC4754, kSecECDSAAlgorithmID, kSecPaddingSigRaw },
{ &kSecKeyAlgorithmECDSASignatureDigestX962, kSecECDSAAlgorithmID, kSecPaddingPKCS1 },
{ &kSecKeyAlgorithmRSAEncryptionRaw, kSecRSAAlgorithmID, kSecPaddingNone },
{ &kSecKeyAlgorithmRSAEncryptionPKCS1, kSecRSAAlgorithmID, kSecPaddingPKCS1 },
{ &kSecKeyAlgorithmRSAEncryptionOAEPSHA1, kSecRSAAlgorithmID, kSecPaddingOAEP },
};
SecPadding padding = (SecPadding)-1;
CFIndex keyAlg = SecKeyGetAlgorithmId(context->key);
for (size_t i = 0; i < array_size(paddingMap); ++i) {
if (keyAlg == paddingMap[i].keyAlg && CFEqual(algorithm, *paddingMap[i].algorithm)) {
padding = paddingMap[i].padding;
break;
}
}
require_quiet(padding != (SecPadding)-1, out);
size_t size = 0;
OSStatus status = errSecSuccess;
switch (context->operation) {
case kSecKeyOperationTypeSign:
if (context->key->key_class->rawSign != NULL) {
result = kCFBooleanTrue;
if (context->mode == kSecKeyOperationModePerform) {
size = SecKeyGetSize(context->key, kSecKeySignatureSize);
result = CFDataCreateMutableWithScratch(NULL, size);
status = context->key->key_class->rawSign(context->key, padding,
CFDataGetBytePtr(in1), CFDataGetLength(in1),
CFDataGetMutableBytePtr((CFMutableDataRef)result), &size);
}
}
break;
case kSecKeyOperationTypeVerify:
if (context->key->key_class->rawVerify != NULL) {
result = kCFBooleanTrue;
if (context->mode == kSecKeyOperationModePerform) {
status = context->key->key_class->rawVerify(context->key, padding,
CFDataGetBytePtr(in1), CFDataGetLength(in1),
CFDataGetBytePtr(in2), CFDataGetLength(in2));
}
}
break;
case kSecKeyOperationTypeEncrypt:
if (context->key->key_class->encrypt != NULL) {
result = kCFBooleanTrue;
if (context->mode == kSecKeyOperationModePerform) {
size = SecKeyGetSize(context->key, kSecKeyEncryptedDataSize);
result = CFDataCreateMutableWithScratch(NULL, size);
status = context->key->key_class->encrypt(context->key, padding,
CFDataGetBytePtr(in1), CFDataGetLength(in1),
CFDataGetMutableBytePtr((CFMutableDataRef)result), &size);
}
}
break;
case kSecKeyOperationTypeDecrypt:
if (context->key->key_class->decrypt != NULL) {
result = kCFBooleanTrue;
if (context->mode == kSecKeyOperationModePerform) {
size = SecKeyGetSize(context->key, kSecKeyEncryptedDataSize);
result = CFDataCreateMutableWithScratch(NULL, size);
status = context->key->key_class->decrypt(context->key, padding,
CFDataGetBytePtr(in1), CFDataGetLength(in1),
CFDataGetMutableBytePtr((CFMutableDataRef)result), &size);
}
}
break;
default:
goto out;
}
if (status == errSecSuccess) {
if (CFGetTypeID(result) == CFDataGetTypeID()) {
CFDataSetLength((CFMutableDataRef)result, size);
}
} else {
SecError(status, error, CFSTR("legacy SecKey backend operation:%d(%d) failed"), (int)context->operation, (int)padding);
CFReleaseNull(result);
}
out:
return result;
}
CFTypeRef SecKeyRunAlgorithmAndCopyResult(SecKeyOperationContext *context, CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
CFIndex algorithmCount = CFArrayGetCount(context->algorithm);
for (CFIndex index = 0; index < algorithmCount - 1; index++) {
SecKeyAlgorithm indexAlgorithm = CFArrayGetValueAtIndex(context->algorithm, index);
for (CFIndex tested = index + 1; tested < algorithmCount; tested++) {
require_quiet(!CFEqual(indexAlgorithm, CFArrayGetValueAtIndex(context->algorithm, tested)), fail);
}
}
SecKeyAlgorithm algorithm = CFArrayGetValueAtIndex(context->algorithm, algorithmCount - 1);
CFTypeRef output = SecKeyCopyBackendOperationResult(context, algorithm, in1, in2, error);
if (output != kCFNull) {
return output;
}
CFReleaseSafe(output);
SecKeyAlgorithmAdaptor adaptor = SecKeyGetAlgorithmAdaptor(context->operation, algorithm);
require_quiet(adaptor != NULL, fail);
CFTypeRef result = adaptor(context, in1, in2, error);
require_quiet(result != kCFNull, fail);
return result;
fail:
if (context->mode == kSecKeyOperationModePerform) {
SecError(errSecParam, error, CFSTR("%@: algorithm not supported by the key %@"),
CFArrayGetValueAtIndex(context->algorithm, 0), context->key);
return NULL;
} else {
return kCFNull;
}
}
#pragma mark Algorithm-related SecKey API entry points
static CFMutableArrayRef SecKeyCreateAlgorithmArray(SecKeyAlgorithm algorithm) {
CFMutableArrayRef result = CFArrayCreateMutableForCFTypes(kCFAllocatorDefault);
CFArrayAppendValue(result, algorithm);
return result;
}
CFDataRef SecKeyCreateSignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef dataToSign, CFErrorRef *error) {
CFErrorRef localError = NULL;
SecKeyOperationContext context = { key, kSecKeyOperationTypeSign, SecKeyCreateAlgorithmArray(algorithm) };
CFDataRef result = SecKeyRunAlgorithmAndCopyResult(&context, dataToSign, NULL, &localError);
SecKeyOperationContextDestroy(&context);
SecKeyErrorPropagate(result != NULL, localError, error);
return result;
}
Boolean SecKeyVerifySignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef signedData, CFDataRef signature,
CFErrorRef *error) {
CFErrorRef localError = NULL;
SecKeyOperationContext context = { key, kSecKeyOperationTypeVerify, SecKeyCreateAlgorithmArray(algorithm) };
CFTypeRef res = SecKeyRunAlgorithmAndCopyResult(&context, signedData, signature, &localError);
Boolean result = CFEqualSafe(res, kCFBooleanTrue);
CFReleaseSafe(res);
SecKeyOperationContextDestroy(&context);
SecKeyErrorPropagate(result, localError, error);
return result;
}
CFDataRef SecKeyCreateEncryptedDataWithParameters(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef plaintext,
CFDictionaryRef parameters, CFErrorRef *error) {
CFErrorRef localError = NULL;
SecKeyOperationContext context = { key, kSecKeyOperationTypeEncrypt, SecKeyCreateAlgorithmArray(algorithm) };
CFDataRef result = SecKeyRunAlgorithmAndCopyResult(&context, plaintext, parameters, &localError);
SecKeyOperationContextDestroy(&context);
SecKeyErrorPropagate(result, localError, error);
return result;
}
CFDataRef SecKeyCreateEncryptedData(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef plaintext, CFErrorRef *error) {
return SecKeyCreateEncryptedDataWithParameters(key, algorithm, plaintext, NULL, error);
}
CFDataRef SecKeyCreateDecryptedDataWithParameters(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef ciphertext,
CFDictionaryRef parameters, CFErrorRef *error) {
SecKeyOperationContext context = { key, kSecKeyOperationTypeDecrypt, SecKeyCreateAlgorithmArray(algorithm) };
CFDataRef result = SecKeyRunAlgorithmAndCopyResult(&context, ciphertext, parameters, error);
SecKeyOperationContextDestroy(&context);
return result;
}
CFDataRef SecKeyCreateDecryptedData(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef ciphertext, CFErrorRef *error) {
return SecKeyCreateDecryptedDataWithParameters(key, algorithm, ciphertext, NULL, error);
}
CFDataRef SecKeyCopyKeyExchangeResult(SecKeyRef key, SecKeyAlgorithm algorithm, SecKeyRef publicKey,
CFDictionaryRef parameters, CFErrorRef *error) {
CFErrorRef localError = NULL;
CFDataRef publicKeyData = NULL, result = NULL;
SecKeyOperationContext context = { key, kSecKeyOperationTypeKeyExchange, SecKeyCreateAlgorithmArray(algorithm) };
require_quiet(publicKeyData = SecKeyCopyExternalRepresentation(publicKey, error), out);
result = SecKeyRunAlgorithmAndCopyResult(&context, publicKeyData, parameters, &localError);
SecKeyErrorPropagate(result != NULL, localError, error);
out:
CFReleaseSafe(publicKeyData);
SecKeyOperationContextDestroy(&context);
return result;
}
Boolean SecKeyIsAlgorithmSupported(SecKeyRef key, SecKeyOperationType operation, SecKeyAlgorithm algorithm) {
SecKeyOperationContext context = { key, operation, SecKeyCreateAlgorithmArray(algorithm), kSecKeyOperationModeCheckIfSupported };
CFErrorRef error = NULL;
CFTypeRef res = SecKeyRunAlgorithmAndCopyResult(&context, NULL, NULL, &error);
Boolean result = CFEqualSafe(res, kCFBooleanTrue);
CFReleaseSafe(res);
CFReleaseSafe(error);
SecKeyOperationContextDestroy(&context);
return result;
}