+
+static SInt32 SecKeyParamsGetSInt32(CFTypeRef value, CFStringRef errName, CFErrorRef *error) {
+ SInt32 result = -1;
+ if (CFGetTypeID(value) == CFNumberGetTypeID()) {
+ if (!CFNumberGetValue(value, kCFNumberSInt32Type, &result) || result < 0) {
+ SecError(errSecParam, error, CFSTR("Unsupported %@: %@"), errName, value);
+ }
+ } else if (isString(value)) {
+ result = CFStringGetIntValue(value);
+ CFStringRef t = CFStringCreateWithFormat(0, 0, CFSTR("%ld"), (long) result);
+ if (!CFEqual(t, value) || result < 0) {
+ SecError(errSecParam, error, CFSTR("Unsupported %@: %@"), errName, value);
+ result = -1;
+ }
+ CFReleaseSafe(t);
+ } else {
+ SecError(errSecParam, error, CFSTR("Unsupported %@: %@"), errName, value);
+ }
+ return result;
+}
+
+SecKeyRef SecKeyCreateWithData(CFDataRef keyData, CFDictionaryRef parameters, CFErrorRef *error) {
+
+ SecKeyRef key = NULL;
+ CFAllocatorRef allocator = NULL;
+
+ /* First figure out the key type (algorithm). */
+ SInt32 algorithm;
+ CFTypeRef ktype = CFDictionaryGetValue(parameters, kSecAttrKeyType);
+ require_quiet((algorithm = SecKeyParamsGetSInt32(ktype, CFSTR("key type"), error)) >= 0, out);
+ SInt32 class;
+ CFTypeRef kclass = CFDictionaryGetValue(parameters, kSecAttrKeyClass);
+ require_quiet((class = SecKeyParamsGetSInt32(kclass, CFSTR("key class"), error)) >= 0, out);
+
+ switch (class) {
+ case 0: // kSecAttrKeyClassPublic
+ switch (algorithm) {
+ case 42: // kSecAlgorithmRSA
+ 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: // kSecAlgorithmECDSA
+ case 73: // kSecAlgorithmEC
+ 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: // kSecAttrKeyClassPrivate
+ if (CFDictionaryGetValue(parameters, kSecAttrTokenID) != NULL) {
+ key = SecKeyCreateCTKKey(allocator, parameters, error);
+ break;
+ }
+ switch (algorithm) {
+ case 42: // kSecAlgorithmRSA
+ 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: // kSecAlgorithmECDSA
+ case 73: // kSecAlgorithmEC
+ 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: // kSecAttrKeyClassSymmetric
+ SecError(errSecUnimplemented, error, CFSTR("Unsupported symmetric key type: %@"), ktype);
+ break;
+ default:
+ SecError(errSecParam, error, CFSTR("Unsupported key class: %@"), kclass);
+ break;
+ }
+
+out:
+ return key;
+}
+
+CFDataRef SecKeyCopyExternalRepresentation(SecKeyRef key, CFErrorRef *error) {
+ if (!key->key_class->copyExternalRepresentation) {
+ SecError(errSecUnimplemented, error, CFSTR("export not implemented for key %@"), key);
+ return NULL;
+ }
+
+ return key->key_class->copyExternalRepresentation(key, error);
+}
+
+CFDictionaryRef SecKeyCopyAttributes(SecKeyRef key) {
+ if (key->key_class->copyDictionary)
+ return key->key_class->copyDictionary(key);
+ return NULL;
+}
+
+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, SecKeyGetAlgorithmIdentifier(key), serializedPublic);
+
+fail:
+ CFReleaseSafe(serializedPublic);
+ return result;
+}
+
+SecKeyRef SecKeyCreateRandomKey(CFDictionaryRef parameters, CFErrorRef *error) {
+ SecKeyRef privKey = NULL, pubKey = NULL;
+ OSStatus status = SecKeyGeneratePair(parameters, &pubKey, &privKey);
+ SecError(status, error, CFSTR("Key generation failed, error %d"), (int)status);
+ CFReleaseSafe(pubKey);
+ return privKey;
+}
+
+#pragma mark Generic algorithm adaptor lookup and invocation
+
+static CFTypeRef SecKeyCopyBackendOperationResult(SecKeyOperationContext *context, SecKeyAlgorithm algorithm,
+ CFTypeRef in1, CFTypeRef in2, CFErrorRef *error) {
+ CFTypeRef result = NULL;
+ 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);
+ }
+
+ // Mapping from algorithms to legacy SecPadding values.
+ 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 = SecKeyGetAlgorithmIdentifier(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);
+
+ // Check legacy virtual table entries.
+ 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) {
+
+ // Check algorithm array for cycles; if any value of it is duplicated inside, report 'algorithm not found' 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) {
+ // Backend handled the operation, return result.
+ return output;
+ }
+
+ // To silence static analyzer.
+ CFReleaseSafe(output);
+
+ // Get adaptor which is able to handle requested algorithm.
+ SecKeyAlgorithmAdaptor adaptor = SecKeyGetAlgorithmAdaptor(context->operation, algorithm);
+ require_quiet(adaptor != NULL, fail);
+
+ // Invoke the adaptor and return result.
+ 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) {
+ SecKeyOperationContext context = { key, kSecKeyOperationTypeSign, SecKeyCreateAlgorithmArray(algorithm) };
+ CFDataRef result = SecKeyRunAlgorithmAndCopyResult(&context, dataToSign, NULL, error);
+ SecKeyOperationContextDestroy(&context);
+ return result;
+}
+
+Boolean SecKeyVerifySignature(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef signedData, CFDataRef signature,
+ CFErrorRef *error) {
+ SecKeyOperationContext context = { key, kSecKeyOperationTypeVerify, SecKeyCreateAlgorithmArray(algorithm) };
+ CFTypeRef res = SecKeyRunAlgorithmAndCopyResult(&context, signedData, signature, error);
+ Boolean result = CFEqualSafe(res, kCFBooleanTrue);
+ CFReleaseSafe(res);
+ SecKeyOperationContextDestroy(&context);
+ return result;
+}
+
+CFDataRef SecKeyCreateEncryptedData(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef plainText, CFErrorRef *error) {
+ SecKeyOperationContext context = { key, kSecKeyOperationTypeEncrypt, SecKeyCreateAlgorithmArray(algorithm) };
+ CFDataRef result = SecKeyRunAlgorithmAndCopyResult(&context, plainText, NULL, error);
+ SecKeyOperationContextDestroy(&context);
+ return result;
+}
+
+CFDataRef SecKeyCreateDecryptedData(SecKeyRef key, SecKeyAlgorithm algorithm, CFDataRef cipherText, CFErrorRef *error) {
+ SecKeyOperationContext context = { key, kSecKeyOperationTypeDecrypt, SecKeyCreateAlgorithmArray(algorithm) };
+ CFDataRef result = SecKeyRunAlgorithmAndCopyResult(&context, cipherText, NULL, error);
+ SecKeyOperationContextDestroy(&context);
+ return result;
+}
+
+CFDataRef SecKeyCopyKeyExchangeResult(SecKeyRef key, SecKeyAlgorithm algorithm, SecKeyRef publicKey,
+ CFDictionaryRef parameters, CFErrorRef *error) {
+ CFDataRef publicKeyData = NULL, result = NULL;
+ SecKeyOperationContext context = { key, kSecKeyOperationTypeKeyExchange, SecKeyCreateAlgorithmArray(algorithm) };
+ require_quiet(publicKeyData = SecKeyCopyExternalRepresentation(publicKey, error), out);
+ result = SecKeyRunAlgorithmAndCopyResult(&context, publicKeyData, parameters, 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;
+}