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