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