1/*
2 *  SecSignVerifyTransform.c
3 *  libsecurity_transform
4 *
5 *  Created by JOsborne on 3/11/10.
6 *  Copyright 2010 Apple. All rights reserved.
7 *
8 */
9
10#include "SecSignVerifyTransform.h"
11#include "SecCustomTransform.h"
12#include "Utilities.h"
13#include <Security/Security.h>
14#include "misc.h"
15
16
17const static CFStringRef SignName = CFSTR("com.apple.security.Sign"), VerifyName = CFSTR("com.apple.security.Verify");
18CFStringRef kSecKeyAttributeName = CFSTR("KEY"), kSecSignatureAttributeName = CFSTR("Signature"), kSecInputIsAttributeName = CFSTR("InputIs");
19// Internally we force kSecInputIsAttributeName to one of these 3 things, you can use == rather then CFStringCompare once that happens
20CFStringRef kSecInputIsPlainText = CFSTR("PlainText"), kSecInputIsDigest = CFSTR("Digest"), kSecInputIsRaw = CFSTR("Raw");
21
22static
23CFErrorRef do_sec_fail(OSStatus code, const char *func, const char *file, int line) {
24	CFStringRef msg = CFStringCreateWithFormat(NULL, NULL, CFSTR("Internal error #%x at %s %s:%d"), (unsigned)code, func, file, line);
25	CFErrorRef err = fancy_error(CFSTR("Internal CSSM error"), code, msg);
26	CFRelease(msg);
27
28	return err;
29}
30#define SEC_FAIL(err) if (err) { \
31    SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, do_sec_fail(err, __func__, __FILE__, __LINE__)); \
32    return (CFTypeRef)NULL; \
33}
34#define GET_SEC_FAIL(err) do_sec_fail(err, __func__, __FILE__, __LINE__)
35
36static
37CFErrorRef accumulate_data(CFMutableArrayRef *a, CFDataRef d) {
38	if (!*a) {
39		*a = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks);
40		if (!*a) {
41			return GetNoMemoryError();
42		}
43	}
44	CFDataRef dc = CFDataCreateCopy(NULL, d);
45	if (!dc) {
46		return GetNoMemoryError();
47	}
48	CFIndex c = CFArrayGetCount(*a);
49	CFArrayAppendValue(*a, dc);
50	CFRelease(dc);
51	if (CFArrayGetCount(*a) != c+1) {
52		return GetNoMemoryError();
53	}
54
55	return NULL;
56}
57
58static
59CFErrorRef fetch_and_clear_accumulated_data(CFMutableArrayRef *a, CFDataRef *data_out) {
60	if (!*a) {
61		*data_out = CFDataCreate(NULL, NULL, 0);
62		return (*data_out) ? NULL : GetNoMemoryError();
63	}
64
65	CFIndex i, c = CFArrayGetCount(*a);
66	CFIndex total = 0, prev_total = 0;
67
68	for(i = 0; i < c; i++) {
69		total += CFDataGetLength((CFDataRef)CFArrayGetValueAtIndex(*a, i));
70		if (total < prev_total) {
71			return GetNoMemoryError();
72		}
73		prev_total = total;
74	}
75
76	CFMutableDataRef out = CFDataCreateMutable(NULL, total);
77	if (!out) {
78		return GetNoMemoryError();
79	}
80
81	for(i = 0; i < c; i++) {
82		CFDataRef d = (CFDataRef)CFArrayGetValueAtIndex(*a, i);
83		CFDataAppendBytes(out, CFDataGetBytePtr(d), CFDataGetLength(d));
84	}
85
86	if (CFDataGetLength(out) != total) {
87		CFRelease(out);
88		return GetNoMemoryError();
89	}
90
91	CFArrayRef accumulator = *a;
92	CFRelease(accumulator);
93	*a = NULL;
94
95	// This might be nice:
96	//   *data_out = CFDataCreateCopy(NULL, out);
97	//   CFRelease(out);
98	// but that is slow (for large values) AND isn't really all that important anyway
99
100	*data_out = out;
101
102	return NULL;
103}
104
105struct digest_mapping {
106	// These 3 values are "search values"
107	CSSM_ALGORITHMS kclass;
108	CFStringRef digest_name;
109	int digest_length;
110
111	// "data values"
112	CSSM_ALGORITHMS plain_text_algo, digest_algo;
113};
114
115static
116Boolean digest_mapping_equal(struct digest_mapping *a, struct digest_mapping *b) {
117	if (a == b) {
118		return TRUE;
119	}
120
121	if (a->kclass == b->kclass && a->digest_length == b->digest_length && !CFStringCompare(a->digest_name, b->digest_name, 0)) {
122		return TRUE;
123	}
124
125	return FALSE;
126}
127
128static
129CFHashCode digest_mapping_hash(struct digest_mapping *dm) {
130	return CFHash(dm->digest_name) + dm->kclass + dm->digest_length;
131}
132
133static
134CSSM_ALGORITHMS alg_for_signature_context(CFStringRef input_is, const struct digest_mapping *dm) {
135	if (!CFStringCompare(kSecInputIsPlainText, input_is, 0)) {
136		return dm->plain_text_algo;
137	} else if (!CFStringCompare(kSecInputIsDigest, input_is, 0) || !CFStringCompare(kSecInputIsRaw, input_is, 0)) {
138		return dm->kclass;
139	} else {
140		return CSSM_ALGID_NONE;
141	}
142}
143
144static
145CFErrorRef pick_sign_alg(CFStringRef digest, int digest_length, const CSSM_KEY *ckey, struct digest_mapping **picked) {
146	static dispatch_once_t once = 0;
147	static CFMutableSetRef algos = NULL;
148
149	dispatch_once(&once, ^{
150		struct digest_mapping digest_mappings_stack[] = {
151			{CSSM_ALGID_RSA, kSecDigestSHA1, 0,   CSSM_ALGID_SHA1WithRSA, CSSM_ALGID_SHA1},
152			{CSSM_ALGID_RSA, kSecDigestSHA1, 160,   CSSM_ALGID_SHA1WithRSA, CSSM_ALGID_SHA1},
153
154			{CSSM_ALGID_RSA, kSecDigestMD2, 0,   CSSM_ALGID_MD2WithRSA, CSSM_ALGID_MD2},
155			{CSSM_ALGID_RSA, kSecDigestMD2, 128,   CSSM_ALGID_MD2WithRSA, CSSM_ALGID_MD2},
156
157			{CSSM_ALGID_RSA, kSecDigestMD5, 0,   CSSM_ALGID_MD5WithRSA, CSSM_ALGID_MD5},
158			{CSSM_ALGID_RSA, kSecDigestMD5, 128,   CSSM_ALGID_MD5WithRSA, CSSM_ALGID_MD5},
159
160			{CSSM_ALGID_RSA, kSecDigestSHA2, 0,   CSSM_ALGID_SHA512WithRSA, CSSM_ALGID_SHA512},
161			{CSSM_ALGID_RSA, kSecDigestSHA2, 512,   CSSM_ALGID_SHA512WithRSA, CSSM_ALGID_SHA512},
162			{CSSM_ALGID_RSA, kSecDigestSHA2, 384,   CSSM_ALGID_SHA384WithRSA, CSSM_ALGID_SHA384},
163			{CSSM_ALGID_RSA, kSecDigestSHA2, 256,   CSSM_ALGID_SHA256WithRSA, CSSM_ALGID_SHA256},
164			{CSSM_ALGID_RSA, kSecDigestSHA2, 224,   CSSM_ALGID_SHA224WithRSA, CSSM_ALGID_SHA224},
165
166
167			{CSSM_ALGID_ECDSA, kSecDigestSHA1, 0,   CSSM_ALGID_SHA1WithECDSA, CSSM_ALGID_SHA1},
168			{CSSM_ALGID_ECDSA, kSecDigestSHA1, 160,   CSSM_ALGID_SHA1WithECDSA, CSSM_ALGID_SHA1},
169
170			{CSSM_ALGID_ECDSA, kSecDigestSHA2, 0,   CSSM_ALGID_SHA512WithECDSA, CSSM_ALGID_SHA512},
171			{CSSM_ALGID_ECDSA, kSecDigestSHA2, 512,   CSSM_ALGID_SHA512WithECDSA, CSSM_ALGID_SHA512},
172			{CSSM_ALGID_ECDSA, kSecDigestSHA2, 384,   CSSM_ALGID_SHA384WithECDSA, CSSM_ALGID_SHA384},
173			{CSSM_ALGID_ECDSA, kSecDigestSHA2, 256,   CSSM_ALGID_SHA256WithECDSA, CSSM_ALGID_SHA256},
174			{CSSM_ALGID_ECDSA, kSecDigestSHA2, 224,   CSSM_ALGID_SHA224WithECDSA, CSSM_ALGID_SHA224},
175
176			{CSSM_ALGID_DSA, kSecDigestSHA1, 0,   CSSM_ALGID_SHA1WithDSA, CSSM_ALGID_SHA1},
177			{CSSM_ALGID_DSA, kSecDigestSHA1, 160,   CSSM_ALGID_SHA1WithDSA, CSSM_ALGID_SHA1},
178		};
179
180		CFIndex mapping_count = sizeof(digest_mappings_stack)/sizeof(digest_mappings_stack[0]);
181		void *digest_mappings = malloc(sizeof(digest_mappings_stack));
182		memcpy(digest_mappings, digest_mappings_stack, sizeof(digest_mappings_stack));
183
184		CFSetCallBacks dmcb = { .version = 0, .retain = NULL, .release = NULL, .copyDescription = NULL, .equal = (CFSetEqualCallBack)digest_mapping_equal, .hash = (CFSetHashCallBack)digest_mapping_hash };
185
186		algos = CFSetCreateMutable(NULL, mapping_count, &dmcb);
187		int i;
188		for(i = 0; i < mapping_count; i++) {
189			CFSetAddValue(algos, i + (struct digest_mapping *)digest_mappings);
190		}
191	});
192
193	struct digest_mapping search;
194	search.kclass = ckey->KeyHeader.AlgorithmId;
195	search.digest_name = digest;
196	search.digest_length = digest_length;
197
198	struct digest_mapping *dmapping = (void*)CFSetGetValue(algos, &search);
199
200	if (dmapping) {
201		*picked = dmapping;
202		return NULL;
203	}
204
205	// 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.
206	switch (ckey->KeyHeader.AlgorithmId) {
207		case CSSM_ALGID_RSA:
208			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"));
209			break;
210
211		case CSSM_ALGID_ECDSA:
212			return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Invalid digest algorithm for ECDSA signature, choose one of: SHA1, or SHA2 (512bits, 348bits, 256bits, or 224 bits)"));
213			break;
214
215		case CSSM_ALGID_DSA:
216			return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Invalid digest algorithm for DSA signature, only SHA1 is supported"));
217			break;
218
219		default:
220			return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Expected key to be RSA, DSA or ECDSA key"));
221	}
222}
223
224static SecTransformInstanceBlock SignTransform(CFStringRef name,
225							SecTransformRef newTransform,
226							SecTransformImplementationRef ref)
227{
228	SecTransformInstanceBlock instanceBlock = ^
229	{
230		CFErrorRef result = NULL;
231		SecTransformCustomSetAttribute(ref, kSecKeyAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue);
232		SecTransformCustomSetAttribute(ref, kSecInputIsAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue);
233
234		__block CSSM_CC_HANDLE cch;
235		__block SecKeyRef key = NULL;
236		__block SecTransformDataBlock first_process_data = NULL;
237		__block CFStringRef digest = NULL;
238		__block int digest_length = 0;
239		__block CFStringRef input_is = NULL;
240		__block CFMutableArrayRef data_accumulator = NULL;
241		__block struct digest_mapping *sign_alg;
242
243		SecTransformDataBlock plain_text_process_data =
244		^(CFTypeRef value)
245		{
246			CFDataRef d = value;
247			OSStatus rc;
248
249			if (d) {
250				CSSM_DATA c_d;
251				c_d.Data = (void*)CFDataGetBytePtr(d);
252				c_d.Length = CFDataGetLength(d);
253
254				rc = CSSM_SignDataUpdate(cch, &c_d, 1);
255				SEC_FAIL(rc);
256			} else {
257				CSSM_DATA sig;
258				const int max_sig_size = 32*1024;
259				unsigned char *sig_data = malloc(max_sig_size);
260				sig.Data = sig_data;
261				sig.Length = max_sig_size;
262
263				rc = CSSM_SignDataFinal(cch, &sig);
264				SEC_FAIL(rc);
265				assert(sig.Length <= 32*1024);
266				CSSM_DeleteContext(cch);
267				// Could use NoCopy and hold onto the allocation, and that will be a good idea when we can have it not so oversized
268				CFDataRef result = CFDataCreate(NULL, sig.Data, sig.Length);
269				SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, result);
270				CFRelease(result);
271				free(sig_data);
272
273				key = NULL;
274
275				CFRelease(digest);
276				digest = NULL;
277
278				digest_length = 0;
279
280				SecTransformSetDataAction(ref, kSecTransformActionProcessData,  first_process_data);
281
282				return (CFTypeRef)NULL;
283			}
284
285			return SecTransformNoData();
286		};
287
288		SecTransformDataBlock cooked_process_data =
289		^(CFTypeRef value)
290		{
291			CFDataRef d = value;
292			if (d) {
293				accumulate_data(&data_accumulator, d);
294			} else {
295				CSSM_DATA sig;
296				const int max_sig_size = 32*1024;
297				unsigned char *sig_data = malloc(max_sig_size);
298				sig.Data = sig_data;
299				sig.Length = max_sig_size;
300
301				CFDataRef alldata;
302				CFErrorRef err = fetch_and_clear_accumulated_data(&data_accumulator, &alldata);
303				if (err) {
304					return (CFTypeRef)err;
305				}
306				CSSM_DATA c_d;
307				c_d.Data = (void*)CFDataGetBytePtr(alldata);
308				c_d.Length = CFDataGetLength(alldata);
309
310				OSStatus rc = CSSM_SignData(cch, &c_d, 1, (input_is == kSecInputIsDigest) ? sign_alg->digest_algo : CSSM_ALGID_NONE, &sig);
311				SEC_FAIL(rc);
312				CFRelease(alldata);
313
314				assert(sig.Length <= 32*1024);
315				CSSM_DeleteContext(cch);
316				// Could use NoCopy and hold onto the allocation, and that will be a good idea when we can have it not so oversized
317				CFDataRef result = CFDataCreate(NULL, sig.Data, sig.Length);
318				SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, result);
319				CFRelease(result);
320				free(sig_data);
321
322				key = NULL;
323
324				CFRelease(digest);
325				digest = NULL;
326
327				digest_length = 0;
328
329				SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
330
331				return (CFTypeRef)NULL;
332			}
333
334			return SecTransformNoData();
335		};
336
337		first_process_data = Block_copy(^(CFTypeRef value)
338		{
339			OSStatus rc;
340			if (key && digest && input_is)
341			{
342				const CSSM_KEY *cssm_key;
343				rc = SecKeyGetCSSMKey(key, &cssm_key);
344				SEC_FAIL(rc);
345
346				CFErrorRef bad_alg = pick_sign_alg(digest, digest_length, cssm_key, &sign_alg);
347				if (bad_alg)
348				{
349					return (CFTypeRef)bad_alg;
350				}
351
352				CSSM_CSP_HANDLE csp;
353				rc = SecKeyGetCSPHandle(key, &csp);
354				SEC_FAIL(rc);
355
356				const CSSM_ACCESS_CREDENTIALS *access_cred;
357				rc = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault, &access_cred);
358				SEC_FAIL(rc);
359
360				CSSM_CSP_CreateSignatureContext(csp, alg_for_signature_context(input_is, sign_alg), access_cred, cssm_key, &cch);
361				SEC_FAIL(rc);
362
363				rc = CSSM_SignDataInit(cch);
364				SEC_FAIL(rc);
365
366				SecTransformDataBlock pd = (input_is == kSecInputIsPlainText) ? plain_text_process_data : cooked_process_data;
367
368				SecTransformSetDataAction(ref, kSecTransformActionProcessData, pd);
369				return pd(value);
370			}
371			else
372			{
373				SecTransformPushbackAttribute(ref, kSecTransformInputAttributeName, value);
374				return SecTransformNoData();
375			}
376		});
377
378		SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
379
380		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestTypeAttribute,
381			^(SecTransformAttributeRef ah, CFTypeRef value)
382			{
383				digest = CFRetain(value);
384				return value;
385			});
386
387		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecKeyAttributeName,
388			^(SecTransformAttributeRef ah, CFTypeRef value)
389			{
390				if (value == NULL) {
391                    return value;
392                }
393
394                const CSSM_KEY *cssm_key;
395				key = (SecKeyRef)value;
396
397				OSStatus rc = SecKeyGetCSSMKey(key, &cssm_key);
398				SEC_FAIL(rc);
399
400				if (!cssm_key->KeyHeader.KeyUsage & CSSM_KEYUSE_SIGN)
401				{
402					key = NULL;
403
404                    CFTypeRef error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Key %@ can not be used to sign", key);
405					SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, error);
406                    return (CFTypeRef)NULL;
407				}
408				return value;
409			});
410
411		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestLengthAttribute,
412			^(SecTransformAttributeRef ah, CFTypeRef value)
413			{
414				CFNumberGetValue(value, kCFNumberIntType, &digest_length);
415				return value;
416			});
417
418		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecInputIsAttributeName,
419			^(SecTransformAttributeRef ah, CFTypeRef value)
420			{
421				if (!CFStringCompare(value, kSecInputIsPlainText, 0)) {
422					input_is = kSecInputIsPlainText;
423				} else if (!CFStringCompare(value, kSecInputIsDigest, 0)) {
424					input_is = kSecInputIsDigest;
425				} else if (!CFStringCompare(value, kSecInputIsRaw, 0)) {
426					input_is = kSecInputIsRaw;
427				} else {
428					input_is = NULL;
429					return (CFTypeRef)fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidType, CFSTR("InputIs should be one of: PlainText, Digest, or Raw"));
430				}
431				return (CFTypeRef)input_is;
432			});
433
434		SecTransformSetTransformAction(ref, kSecTransformActionFinalize,
435			^{
436				Block_release(first_process_data);
437				return (CFTypeRef)NULL;
438			});
439
440		return result;
441	};
442
443	return Block_copy(instanceBlock);
444}
445
446SecTransformRef SecSignTransformCreate(SecKeyRef key, CFErrorRef* error)
447{
448	static dispatch_once_t once;
449	__block Boolean ok = TRUE;
450
451	dispatch_block_t aBlock = ^
452	{
453		ok = SecTransformRegister(SignName, &SignTransform, error);
454	};
455
456	dispatch_once(&once, aBlock);
457
458	if (!ok)
459	{
460		return NULL;
461	}
462
463	SecTransformRef tr = SecTransformCreate(SignName, error);
464	if (!tr) {
465		return tr;
466	}
467	SecTransformSetAttribute(tr, kSecKeyAttributeName, key, error);
468	SecTransformSetAttribute(tr, kSecDigestTypeAttribute, kSecDigestSHA1, NULL);
469	SecTransformSetAttribute(tr, kSecInputIsAttributeName, kSecInputIsPlainText, NULL);
470
471	return tr;
472}
473
474static SecTransformInstanceBlock VerifyTransform(CFStringRef name,
475							SecTransformRef newTransform,
476							SecTransformImplementationRef ref)
477{
478	SecTransformInstanceBlock instanceBlock = ^
479	{
480		CFErrorRef result = NULL;
481		SecTransformCustomSetAttribute(ref, kSecKeyAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue);
482		SecTransformCustomSetAttribute(ref, kSecSignatureAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue);
483		SecTransformCustomSetAttribute(ref, kSecInputIsAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue);
484
485		__block CSSM_CC_HANDLE cch;
486		__block const CSSM_KEY *cssm_key;
487		__block CSSM_CSP_HANDLE csp;
488		__block const CSSM_ACCESS_CREDENTIALS *access_cred;
489		__block CFDataRef signature = NULL;
490		__block unsigned char had_last_input = 0;
491		__block CFStringRef digest = NULL;
492		__block int digest_length = 0;
493		__block SecTransformDataBlock first_process_data;
494		__block SecKeyRef key = NULL;
495		__block CFStringRef input_is = NULL;
496		__block CFMutableArrayRef data_accumulator = NULL;
497		__block struct digest_mapping *verify_alg = NULL;
498
499		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecInputIsAttributeName,
500			^(SecTransformAttributeRef ah, CFTypeRef value)
501			{
502				if (!CFStringCompare(value, kSecInputIsPlainText, 0)) {
503					input_is = kSecInputIsPlainText;
504				} else if (!CFStringCompare(value, kSecInputIsDigest, 0)) {
505					input_is = kSecInputIsDigest;
506				} else if (!CFStringCompare(value, kSecInputIsRaw, 0)) {
507					input_is = kSecInputIsRaw;
508				} else {
509					input_is = NULL;
510					return (CFTypeRef)fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidType, CFSTR("InputIs should be one of: PlainText, Digest, or Raw"));
511				}
512				return (CFTypeRef)input_is;
513			});
514
515		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecKeyAttributeName,
516			^(SecTransformAttributeRef ah, CFTypeRef value)
517			{
518				OSStatus rc;
519
520                if (value == NULL) {
521                    return value;
522                }
523
524				rc = SecKeyGetCSSMKey((SecKeyRef)value, &cssm_key);
525				SEC_FAIL(rc);
526
527				if (!cssm_key->KeyHeader.KeyUsage & CSSM_KEYUSE_VERIFY)
528				{
529					// This key cannot verify!
530					return (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Key %@ can not be used to verify", key);
531				}
532
533				// we don't need to retain this because the owning transform is doing that for us
534				key = (SecKeyRef) value;
535				return value;
536			});
537
538		// We call this when we get the last input and when we get the signature.   If both are true when it is
539		// called we are really done, and it gennerates the output
540		void (^done)(void) =
541		^{
542			if (signature && had_last_input)
543			{
544				CSSM_DATA sig;
545				OSStatus rc;
546				sig.Data = (void*)CFDataGetBytePtr(signature);
547				sig.Length = CFDataGetLength(signature);
548				CFRelease(signature);
549				signature = NULL;
550
551				if (input_is == kSecInputIsPlainText) {
552					rc = CSSM_VerifyDataFinal(cch, &sig);
553				} else {
554					CFDataRef alldata;
555					CFErrorRef err = fetch_and_clear_accumulated_data(&data_accumulator, &alldata);
556					if (err) {
557						SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, (CFTypeRef)err);
558						return;
559					}
560
561					CSSM_DATA c_d;
562					c_d.Data = (void*)CFDataGetBytePtr(alldata);
563					c_d.Length = CFDataGetLength(alldata);
564					rc = CSSM_VerifyData(cch, &c_d, 1, (input_is == kSecInputIsDigest) ? verify_alg->digest_algo : CSSM_ALGID_NONE, &sig);
565                    CFRelease(alldata);
566
567				}
568				CSSM_DeleteContext(cch);
569				if (rc == 0 || rc == CSSMERR_CSP_VERIFY_FAILED) {
570					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, rc ? kCFBooleanFalse : kCFBooleanTrue);
571					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, NULL);
572				} else {
573					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, GET_SEC_FAIL(rc));
574				}
575				had_last_input = FALSE;
576				SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
577			}
578		};
579
580		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecSignatureAttributeName,
581			^(SecTransformAttributeRef ah, CFTypeRef value)
582			{
583				if (value) {
584					signature = CFRetain(value);
585				}
586
587				done();
588
589				return (CFTypeRef)value;
590			});
591
592		SecTransformDataBlock process_data =
593			^(CFTypeRef value)
594			{
595				OSStatus rc;
596				CFDataRef d = value;
597
598				if (d) {
599					if (input_is == kSecInputIsPlainText) {
600						CSSM_DATA c_d;
601						c_d.Data = (void*)CFDataGetBytePtr(d);
602						c_d.Length = CFDataGetLength(d);
603
604						rc = CSSM_VerifyDataUpdate(cch, &c_d, 1);
605						SEC_FAIL(rc);
606					} else {
607						accumulate_data(&data_accumulator, d);
608					}
609				} else {
610					had_last_input = 1;
611					done();
612				}
613
614				return SecTransformNoData();
615			};
616
617		first_process_data =
618			^(CFTypeRef value)
619			{
620				if (key && digest && input_is) {
621					// XXX: For RSA keys, signal an error if the digest size>keysize
622
623					OSStatus rc = SecKeyGetCSPHandle(key, &csp);
624					SEC_FAIL(rc);
625
626					rc = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_ANY, kSecCredentialTypeDefault, &access_cred);
627					SEC_FAIL(rc);
628
629					CFErrorRef bad_alg = pick_sign_alg(digest, digest_length, cssm_key, &verify_alg);
630					if (bad_alg) {
631						return (CFTypeRef)bad_alg;
632					}
633
634					CSSM_CSP_CreateSignatureContext(csp, alg_for_signature_context(input_is, verify_alg), NULL, cssm_key, &cch);
635					SEC_FAIL(rc);
636
637					rc = CSSM_VerifyDataInit(cch);
638					SEC_FAIL(rc);
639
640					SecTransformSetDataAction(ref, kSecTransformActionProcessData, process_data);
641					return process_data(value);
642				} else {
643					SecTransformPushbackAttribute(ref, kSecTransformInputAttributeName, value);
644					return SecTransformNoData();
645				}
646			};
647		first_process_data = Block_copy(first_process_data);
648
649		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestTypeAttribute,
650			^(SecTransformAttributeRef ah, CFTypeRef value)
651			{
652				digest = CFRetain(value);
653				return value;
654			});
655
656		SecTransformSetTransformAction(ref, kSecTransformActionFinalize,
657			^{
658				Block_release(first_process_data);
659				return (CFTypeRef)NULL;
660			});
661
662		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestLengthAttribute,
663			^(SecTransformAttributeRef ah, CFTypeRef value)
664			{
665				CFNumberGetValue(value, kCFNumberIntType, &digest_length);
666				return value;
667			});
668
669		SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
670
671		return result;
672	};
673
674	return Block_copy(instanceBlock);
675}
676
677SecTransformRef SecVerifyTransformCreate(SecKeyRef key, CFDataRef signature, CFErrorRef* error)
678{
679	static dispatch_once_t once;
680	__block Boolean ok = TRUE;
681
682	dispatch_block_t aBlock = ^
683	{
684		ok = SecTransformRegister(VerifyName, &VerifyTransform, error);
685	};
686
687	dispatch_once(&once, aBlock);
688
689	if (!ok)
690	{
691		return NULL;
692	}
693
694
695	SecTransformRef tr = SecTransformCreate(VerifyName, error);
696	if (!tr) {
697		return tr;
698	}
699
700	SecTransformSetAttribute(tr, kSecKeyAttributeName, key, error);
701	if (signature)
702	{
703		SecTransformSetAttribute(tr, kSecSignatureAttributeName, signature, error);
704	}
705	SecTransformSetAttribute(tr, kSecDigestTypeAttribute, kSecDigestSHA1, NULL);
706	SecTransformSetAttribute(tr, kSecDigestTypeAttribute, kSecDigestSHA1, NULL);
707	SecTransformSetAttribute(tr, kSecInputIsAttributeName, kSecInputIsPlainText, NULL);
708
709	return tr;
710}
711