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