X-Git-Url: https://git.saurik.com/apple/security.git/blobdiff_plain/80e2389990082500d76eb566d4946be3e786c3ef..d8f41ccd20de16f8ebe2ccc84d47bf1cb2b26bbb:/Security/libsecurity_transform/lib/SecSignVerifyTransform.c diff --git a/Security/libsecurity_transform/lib/SecSignVerifyTransform.c b/Security/libsecurity_transform/lib/SecSignVerifyTransform.c new file mode 100644 index 00000000..37fbcb25 --- /dev/null +++ b/Security/libsecurity_transform/lib/SecSignVerifyTransform.c @@ -0,0 +1,725 @@ +/* + * Copyright (c) 2010-2014 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@ + */ + + +#include "SecSignVerifyTransform.h" +#include "SecCustomTransform.h" +#include "Utilities.h" +#include +#include "misc.h" + + +const static CFStringRef SignName = CFSTR("com.apple.security.Sign"), VerifyName = CFSTR("com.apple.security.Verify"); +CFStringRef kSecKeyAttributeName = CFSTR("KEY"), kSecSignatureAttributeName = CFSTR("Signature"), kSecInputIsAttributeName = CFSTR("InputIs"); +// Internally we force kSecInputIsAttributeName to one of these 3 things, you can use == rather then CFStringCompare once that happens +CFStringRef kSecInputIsPlainText = CFSTR("PlainText"), kSecInputIsDigest = CFSTR("Digest"), kSecInputIsRaw = CFSTR("Raw"); + +static +CFErrorRef do_sec_fail(OSStatus code, const char *func, const char *file, int line) { + CFStringRef msg = CFStringCreateWithFormat(NULL, NULL, CFSTR("Internal error #%x at %s %s:%d"), (unsigned)code, func, file, line); + CFErrorRef err = fancy_error(CFSTR("Internal CSSM error"), code, msg); + CFRelease(msg); + + return err; +} +#define SEC_FAIL(err) if (err) { \ + SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, do_sec_fail(err, __func__, __FILE__, __LINE__)); \ + return (CFTypeRef)NULL; \ +} +#define GET_SEC_FAIL(err) do_sec_fail(err, __func__, __FILE__, __LINE__) + +static +CFErrorRef accumulate_data(CFMutableArrayRef *a, CFDataRef d) { + if (!*a) { + *a = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + if (!*a) { + return GetNoMemoryError(); + } + } + CFDataRef dc = CFDataCreateCopy(NULL, d); + if (!dc) { + return GetNoMemoryError(); + } + CFIndex c = CFArrayGetCount(*a); + CFArrayAppendValue(*a, dc); + CFRelease(dc); + if (CFArrayGetCount(*a) != c+1) { + return GetNoMemoryError(); + } + + return NULL; +} + +static +CFErrorRef fetch_and_clear_accumulated_data(CFMutableArrayRef *a, CFDataRef *data_out) { + if (!*a) { + *data_out = CFDataCreate(NULL, NULL, 0); + return (*data_out) ? NULL : GetNoMemoryError(); + } + + CFIndex i, c = CFArrayGetCount(*a); + CFIndex total = 0, prev_total = 0; + + for(i = 0; i < c; i++) { + total += CFDataGetLength((CFDataRef)CFArrayGetValueAtIndex(*a, i)); + if (total < prev_total) { + return GetNoMemoryError(); + } + prev_total = total; + } + + CFMutableDataRef out = CFDataCreateMutable(NULL, total); + if (!out) { + return GetNoMemoryError(); + } + + for(i = 0; i < c; i++) { + CFDataRef d = (CFDataRef)CFArrayGetValueAtIndex(*a, i); + CFDataAppendBytes(out, CFDataGetBytePtr(d), CFDataGetLength(d)); + } + + if (CFDataGetLength(out) != total) { + CFRelease(out); + return GetNoMemoryError(); + } + + CFArrayRef accumulator = *a; + CFRelease(accumulator); + *a = NULL; + + // This might be nice: + // *data_out = CFDataCreateCopy(NULL, out); + // CFRelease(out); + // but that is slow (for large values) AND isn't really all that important anyway + + *data_out = out; + + return NULL; +} + +struct digest_mapping { + // These 3 values are "search values" + CSSM_ALGORITHMS kclass; + CFStringRef digest_name; + int digest_length; + + // "data values" + CSSM_ALGORITHMS plain_text_algo, digest_algo; +}; + +static +Boolean digest_mapping_equal(struct digest_mapping *a, struct digest_mapping *b) { + if (a == b) { + return TRUE; + } + + if (a->kclass == b->kclass && a->digest_length == b->digest_length && !CFStringCompare(a->digest_name, b->digest_name, 0)) { + return TRUE; + } + + return FALSE; +} + +static +CFHashCode digest_mapping_hash(struct digest_mapping *dm) { + return CFHash(dm->digest_name) + dm->kclass + dm->digest_length; +} + +static +CSSM_ALGORITHMS alg_for_signature_context(CFStringRef input_is, const struct digest_mapping *dm) { + if (!CFStringCompare(kSecInputIsPlainText, input_is, 0)) { + return dm->plain_text_algo; + } else if (!CFStringCompare(kSecInputIsDigest, input_is, 0) || !CFStringCompare(kSecInputIsRaw, input_is, 0)) { + return dm->kclass; + } else { + return CSSM_ALGID_NONE; + } +} + +static +CFErrorRef pick_sign_alg(CFStringRef digest, int digest_length, const CSSM_KEY *ckey, struct digest_mapping **picked) { + static dispatch_once_t once = 0; + static CFMutableSetRef algos = NULL; + + dispatch_once(&once, ^{ + struct digest_mapping digest_mappings_stack[] = { + {CSSM_ALGID_RSA, kSecDigestSHA1, 0, CSSM_ALGID_SHA1WithRSA, CSSM_ALGID_SHA1}, + {CSSM_ALGID_RSA, kSecDigestSHA1, 160, CSSM_ALGID_SHA1WithRSA, CSSM_ALGID_SHA1}, + + {CSSM_ALGID_RSA, kSecDigestMD2, 0, CSSM_ALGID_MD2WithRSA, CSSM_ALGID_MD2}, + {CSSM_ALGID_RSA, kSecDigestMD2, 128, CSSM_ALGID_MD2WithRSA, CSSM_ALGID_MD2}, + + {CSSM_ALGID_RSA, kSecDigestMD5, 0, CSSM_ALGID_MD5WithRSA, CSSM_ALGID_MD5}, + {CSSM_ALGID_RSA, kSecDigestMD5, 128, CSSM_ALGID_MD5WithRSA, CSSM_ALGID_MD5}, + + {CSSM_ALGID_RSA, kSecDigestSHA2, 0, CSSM_ALGID_SHA512WithRSA, CSSM_ALGID_SHA512}, + {CSSM_ALGID_RSA, kSecDigestSHA2, 512, CSSM_ALGID_SHA512WithRSA, CSSM_ALGID_SHA512}, + {CSSM_ALGID_RSA, kSecDigestSHA2, 384, CSSM_ALGID_SHA384WithRSA, CSSM_ALGID_SHA384}, + {CSSM_ALGID_RSA, kSecDigestSHA2, 256, CSSM_ALGID_SHA256WithRSA, CSSM_ALGID_SHA256}, + {CSSM_ALGID_RSA, kSecDigestSHA2, 224, CSSM_ALGID_SHA224WithRSA, CSSM_ALGID_SHA224}, + + + {CSSM_ALGID_ECDSA, kSecDigestSHA1, 0, CSSM_ALGID_SHA1WithECDSA, CSSM_ALGID_SHA1}, + {CSSM_ALGID_ECDSA, kSecDigestSHA1, 160, CSSM_ALGID_SHA1WithECDSA, CSSM_ALGID_SHA1}, + + {CSSM_ALGID_ECDSA, kSecDigestSHA2, 0, CSSM_ALGID_SHA512WithECDSA, CSSM_ALGID_SHA512}, + {CSSM_ALGID_ECDSA, kSecDigestSHA2, 512, CSSM_ALGID_SHA512WithECDSA, CSSM_ALGID_SHA512}, + {CSSM_ALGID_ECDSA, kSecDigestSHA2, 384, CSSM_ALGID_SHA384WithECDSA, CSSM_ALGID_SHA384}, + {CSSM_ALGID_ECDSA, kSecDigestSHA2, 256, CSSM_ALGID_SHA256WithECDSA, CSSM_ALGID_SHA256}, + {CSSM_ALGID_ECDSA, kSecDigestSHA2, 224, CSSM_ALGID_SHA224WithECDSA, CSSM_ALGID_SHA224}, + + {CSSM_ALGID_DSA, kSecDigestSHA1, 0, CSSM_ALGID_SHA1WithDSA, CSSM_ALGID_SHA1}, + {CSSM_ALGID_DSA, kSecDigestSHA1, 160, CSSM_ALGID_SHA1WithDSA, CSSM_ALGID_SHA1}, + }; + + CFIndex mapping_count = sizeof(digest_mappings_stack)/sizeof(digest_mappings_stack[0]); + void *digest_mappings = malloc(sizeof(digest_mappings_stack)); + memcpy(digest_mappings, digest_mappings_stack, sizeof(digest_mappings_stack)); + + CFSetCallBacks dmcb = { .version = 0, .retain = NULL, .release = NULL, .copyDescription = NULL, .equal = (CFSetEqualCallBack)digest_mapping_equal, .hash = (CFSetHashCallBack)digest_mapping_hash }; + + algos = CFSetCreateMutable(NULL, mapping_count, &dmcb); + int i; + for(i = 0; i < mapping_count; i++) { + CFSetAddValue(algos, i + (struct digest_mapping *)digest_mappings); + } + }); + + struct digest_mapping search; + search.kclass = ckey->KeyHeader.AlgorithmId; + search.digest_name = digest; + search.digest_length = digest_length; + + struct digest_mapping *dmapping = (void*)CFSetGetValue(algos, &search); + + if (dmapping) { + *picked = dmapping; + return NULL; + } + + // It is argueable better to gennerate these messages by looking at digest_mappings, but with only 3 keytypes and 4 digests (only one of which has signifigant length variations) a case statment is likely the best way. + switch (ckey->KeyHeader.AlgorithmId) { + case CSSM_ALGID_RSA: + return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Invalid digest algorithm for RSA signature, choose one of: SHA1, SHA2 (512bits, 348bits, 256bits, or 224 bits), MD2, or MD5")); + break; + + case CSSM_ALGID_ECDSA: + return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Invalid digest algorithm for ECDSA signature, choose one of: SHA1, or SHA2 (512bits, 348bits, 256bits, or 224 bits)")); + break; + + case CSSM_ALGID_DSA: + return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Invalid digest algorithm for DSA signature, only SHA1 is supported")); + break; + + default: + return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Expected key to be RSA, DSA or ECDSA key")); + } +} + +static SecTransformInstanceBlock SignTransform(CFStringRef name, + SecTransformRef newTransform, + SecTransformImplementationRef ref) +{ + SecTransformInstanceBlock instanceBlock = ^ + { + CFErrorRef result = NULL; + SecTransformCustomSetAttribute(ref, kSecKeyAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue); + SecTransformCustomSetAttribute(ref, kSecInputIsAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue); + + __block CSSM_CC_HANDLE cch; + __block SecKeyRef key = NULL; + __block SecTransformDataBlock first_process_data = NULL; + __block CFStringRef digest = NULL; + __block int digest_length = 0; + __block CFStringRef input_is = NULL; + __block CFMutableArrayRef data_accumulator = NULL; + __block struct digest_mapping *sign_alg; + + SecTransformDataBlock plain_text_process_data = + ^(CFTypeRef value) + { + CFDataRef d = value; + OSStatus rc; + + if (d) { + CSSM_DATA c_d; + c_d.Data = (void*)CFDataGetBytePtr(d); + c_d.Length = CFDataGetLength(d); + + rc = CSSM_SignDataUpdate(cch, &c_d, 1); + SEC_FAIL(rc); + } else { + CSSM_DATA sig; + const int max_sig_size = 32*1024; + unsigned char *sig_data = malloc(max_sig_size); + sig.Data = sig_data; + sig.Length = max_sig_size; + + rc = CSSM_SignDataFinal(cch, &sig); + SEC_FAIL(rc); + assert(sig.Length <= 32*1024); + CSSM_DeleteContext(cch); + // Could use NoCopy and hold onto the allocation, and that will be a good idea when we can have it not so oversized + CFDataRef result = CFDataCreate(NULL, sig.Data, sig.Length); + SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, result); + CFRelease(result); + free(sig_data); + + key = NULL; + + CFRelease(digest); + digest = NULL; + + digest_length = 0; + + SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data); + + return (CFTypeRef)NULL; + } + + return SecTransformNoData(); + }; + + SecTransformDataBlock cooked_process_data = + ^(CFTypeRef value) + { + CFDataRef d = value; + if (d) { + accumulate_data(&data_accumulator, d); + } else { + CSSM_DATA sig; + const int max_sig_size = 32*1024; + unsigned char *sig_data = malloc(max_sig_size); + sig.Data = sig_data; + sig.Length = max_sig_size; + + CFDataRef alldata; + CFErrorRef err = fetch_and_clear_accumulated_data(&data_accumulator, &alldata); + if (err) { + return (CFTypeRef)err; + } + CSSM_DATA c_d; + c_d.Data = (void*)CFDataGetBytePtr(alldata); + c_d.Length = CFDataGetLength(alldata); + + OSStatus rc = CSSM_SignData(cch, &c_d, 1, (input_is == kSecInputIsDigest) ? sign_alg->digest_algo : CSSM_ALGID_NONE, &sig); + SEC_FAIL(rc); + CFRelease(alldata); + + assert(sig.Length <= 32*1024); + CSSM_DeleteContext(cch); + // Could use NoCopy and hold onto the allocation, and that will be a good idea when we can have it not so oversized + CFDataRef result = CFDataCreate(NULL, sig.Data, sig.Length); + SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, result); + CFRelease(result); + free(sig_data); + + key = NULL; + + CFRelease(digest); + digest = NULL; + + digest_length = 0; + + SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data); + + return (CFTypeRef)NULL; + } + + return SecTransformNoData(); + }; + + first_process_data = Block_copy(^(CFTypeRef value) + { + OSStatus rc; + if (key && digest && input_is) + { + const CSSM_KEY *cssm_key; + rc = SecKeyGetCSSMKey(key, &cssm_key); + SEC_FAIL(rc); + + CFErrorRef bad_alg = pick_sign_alg(digest, digest_length, cssm_key, &sign_alg); + if (bad_alg) + { + return (CFTypeRef)bad_alg; + } + + CSSM_CSP_HANDLE csp; + rc = SecKeyGetCSPHandle(key, &csp); + SEC_FAIL(rc); + + const CSSM_ACCESS_CREDENTIALS *access_cred; + rc = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault, &access_cred); + SEC_FAIL(rc); + + CSSM_CSP_CreateSignatureContext(csp, alg_for_signature_context(input_is, sign_alg), access_cred, cssm_key, &cch); + SEC_FAIL(rc); + + rc = CSSM_SignDataInit(cch); + SEC_FAIL(rc); + + SecTransformDataBlock pd = (input_is == kSecInputIsPlainText) ? plain_text_process_data : cooked_process_data; + + SecTransformSetDataAction(ref, kSecTransformActionProcessData, pd); + return pd(value); + } + else + { + SecTransformPushbackAttribute(ref, kSecTransformInputAttributeName, value); + return SecTransformNoData(); + } + }); + + SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data); + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestTypeAttribute, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + digest = CFRetain(value); + return value; + }); + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecKeyAttributeName, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + if (value == NULL) { + return value; + } + + const CSSM_KEY *cssm_key; + key = (SecKeyRef)value; + + OSStatus rc = SecKeyGetCSSMKey(key, &cssm_key); + SEC_FAIL(rc); + + if (!cssm_key->KeyHeader.KeyUsage & CSSM_KEYUSE_SIGN) + { + key = NULL; + + CFTypeRef error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Key %@ can not be used to sign", key); + SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, error); + return (CFTypeRef)NULL; + } + return value; + }); + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestLengthAttribute, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + CFNumberGetValue(value, kCFNumberIntType, &digest_length); + return value; + }); + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecInputIsAttributeName, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + if (!CFStringCompare(value, kSecInputIsPlainText, 0)) { + input_is = kSecInputIsPlainText; + } else if (!CFStringCompare(value, kSecInputIsDigest, 0)) { + input_is = kSecInputIsDigest; + } else if (!CFStringCompare(value, kSecInputIsRaw, 0)) { + input_is = kSecInputIsRaw; + } else { + input_is = NULL; + return (CFTypeRef)fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidType, CFSTR("InputIs should be one of: PlainText, Digest, or Raw")); + } + return (CFTypeRef)input_is; + }); + + SecTransformSetTransformAction(ref, kSecTransformActionFinalize, + ^{ + Block_release(first_process_data); + return (CFTypeRef)NULL; + }); + + return result; + }; + + return Block_copy(instanceBlock); +} + +SecTransformRef SecSignTransformCreate(SecKeyRef key, CFErrorRef* error) +{ + static dispatch_once_t once; + __block Boolean ok = TRUE; + + dispatch_block_t aBlock = ^ + { + ok = SecTransformRegister(SignName, &SignTransform, error); + }; + + dispatch_once(&once, aBlock); + + if (!ok) + { + return NULL; + } + + SecTransformRef tr = SecTransformCreate(SignName, error); + if (!tr) { + return tr; + } + SecTransformSetAttribute(tr, kSecKeyAttributeName, key, error); + SecTransformSetAttribute(tr, kSecDigestTypeAttribute, kSecDigestSHA1, NULL); + SecTransformSetAttribute(tr, kSecInputIsAttributeName, kSecInputIsPlainText, NULL); + + return tr; +} + +static SecTransformInstanceBlock VerifyTransform(CFStringRef name, + SecTransformRef newTransform, + SecTransformImplementationRef ref) +{ + SecTransformInstanceBlock instanceBlock = ^ + { + CFErrorRef result = NULL; + SecTransformCustomSetAttribute(ref, kSecKeyAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue); + SecTransformCustomSetAttribute(ref, kSecSignatureAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue); + SecTransformCustomSetAttribute(ref, kSecInputIsAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue); + + __block CSSM_CC_HANDLE cch; + __block const CSSM_KEY *cssm_key; + __block CSSM_CSP_HANDLE csp; + __block const CSSM_ACCESS_CREDENTIALS *access_cred; + __block CFDataRef signature = NULL; + __block unsigned char had_last_input = 0; + __block CFStringRef digest = NULL; + __block int digest_length = 0; + __block SecTransformDataBlock first_process_data; + __block SecKeyRef key = NULL; + __block CFStringRef input_is = NULL; + __block CFMutableArrayRef data_accumulator = NULL; + __block struct digest_mapping *verify_alg = NULL; + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecInputIsAttributeName, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + if (!CFStringCompare(value, kSecInputIsPlainText, 0)) { + input_is = kSecInputIsPlainText; + } else if (!CFStringCompare(value, kSecInputIsDigest, 0)) { + input_is = kSecInputIsDigest; + } else if (!CFStringCompare(value, kSecInputIsRaw, 0)) { + input_is = kSecInputIsRaw; + } else { + input_is = NULL; + return (CFTypeRef)fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidType, CFSTR("InputIs should be one of: PlainText, Digest, or Raw")); + } + return (CFTypeRef)input_is; + }); + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecKeyAttributeName, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + OSStatus rc; + + if (value == NULL) { + return value; + } + + rc = SecKeyGetCSSMKey((SecKeyRef)value, &cssm_key); + SEC_FAIL(rc); + + if (!cssm_key->KeyHeader.KeyUsage & CSSM_KEYUSE_VERIFY) + { + // This key cannot verify! + return (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Key %@ can not be used to verify", key); + } + + // we don't need to retain this because the owning transform is doing that for us + key = (SecKeyRef) value; + return value; + }); + + // We call this when we get the last input and when we get the signature. If both are true when it is + // called we are really done, and it gennerates the output + void (^done)(void) = + ^{ + if (signature && had_last_input) + { + CSSM_DATA sig; + OSStatus rc; + sig.Data = (void*)CFDataGetBytePtr(signature); + sig.Length = CFDataGetLength(signature); + CFRelease(signature); + signature = NULL; + + if (input_is == kSecInputIsPlainText) { + rc = CSSM_VerifyDataFinal(cch, &sig); + } else { + CFDataRef alldata; + CFErrorRef err = fetch_and_clear_accumulated_data(&data_accumulator, &alldata); + if (err) { + SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, (CFTypeRef)err); + return; + } + + CSSM_DATA c_d; + c_d.Data = (void*)CFDataGetBytePtr(alldata); + c_d.Length = CFDataGetLength(alldata); + rc = CSSM_VerifyData(cch, &c_d, 1, (input_is == kSecInputIsDigest) ? verify_alg->digest_algo : CSSM_ALGID_NONE, &sig); + CFRelease(alldata); + + } + CSSM_DeleteContext(cch); + if (rc == 0 || rc == CSSMERR_CSP_VERIFY_FAILED) { + SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, rc ? kCFBooleanFalse : kCFBooleanTrue); + SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, NULL); + } else { + SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, GET_SEC_FAIL(rc)); + } + had_last_input = FALSE; + SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data); + } + }; + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecSignatureAttributeName, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + if (value) { + signature = CFRetain(value); + } + + done(); + + return (CFTypeRef)value; + }); + + SecTransformDataBlock process_data = + ^(CFTypeRef value) + { + OSStatus rc; + CFDataRef d = value; + + if (d) { + if (input_is == kSecInputIsPlainText) { + CSSM_DATA c_d; + c_d.Data = (void*)CFDataGetBytePtr(d); + c_d.Length = CFDataGetLength(d); + + rc = CSSM_VerifyDataUpdate(cch, &c_d, 1); + SEC_FAIL(rc); + } else { + accumulate_data(&data_accumulator, d); + } + } else { + had_last_input = 1; + done(); + } + + return SecTransformNoData(); + }; + + first_process_data = + ^(CFTypeRef value) + { + if (key && digest && input_is) { + // XXX: For RSA keys, signal an error if the digest size>keysize + + OSStatus rc = SecKeyGetCSPHandle(key, &csp); + SEC_FAIL(rc); + + rc = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_ANY, kSecCredentialTypeDefault, &access_cred); + SEC_FAIL(rc); + + CFErrorRef bad_alg = pick_sign_alg(digest, digest_length, cssm_key, &verify_alg); + if (bad_alg) { + return (CFTypeRef)bad_alg; + } + + CSSM_CSP_CreateSignatureContext(csp, alg_for_signature_context(input_is, verify_alg), NULL, cssm_key, &cch); + SEC_FAIL(rc); + + rc = CSSM_VerifyDataInit(cch); + SEC_FAIL(rc); + + SecTransformSetDataAction(ref, kSecTransformActionProcessData, process_data); + return process_data(value); + } else { + SecTransformPushbackAttribute(ref, kSecTransformInputAttributeName, value); + return SecTransformNoData(); + } + }; + first_process_data = Block_copy(first_process_data); + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestTypeAttribute, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + digest = CFRetain(value); + return value; + }); + + SecTransformSetTransformAction(ref, kSecTransformActionFinalize, + ^{ + Block_release(first_process_data); + return (CFTypeRef)NULL; + }); + + SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestLengthAttribute, + ^(SecTransformAttributeRef ah, CFTypeRef value) + { + CFNumberGetValue(value, kCFNumberIntType, &digest_length); + return value; + }); + + SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data); + + return result; + }; + + return Block_copy(instanceBlock); +} + +SecTransformRef SecVerifyTransformCreate(SecKeyRef key, CFDataRef signature, CFErrorRef* error) +{ + static dispatch_once_t once; + __block Boolean ok = TRUE; + + dispatch_block_t aBlock = ^ + { + ok = SecTransformRegister(VerifyName, &VerifyTransform, error); + }; + + dispatch_once(&once, aBlock); + + if (!ok) + { + return NULL; + } + + + SecTransformRef tr = SecTransformCreate(VerifyName, error); + if (!tr) { + return tr; + } + + SecTransformSetAttribute(tr, kSecKeyAttributeName, key, error); + if (signature) + { + SecTransformSetAttribute(tr, kSecSignatureAttributeName, signature, error); + } + SecTransformSetAttribute(tr, kSecDigestTypeAttribute, kSecDigestSHA1, NULL); + SecTransformSetAttribute(tr, kSecDigestTypeAttribute, kSecDigestSHA1, NULL); + SecTransformSetAttribute(tr, kSecInputIsAttributeName, kSecInputIsPlainText, NULL); + + return tr; +}