]> git.saurik.com Git - apple/security.git/blob - libsecurity_transform/lib/SecSignVerifyTransform.c
Security-55471.14.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 static
23 CFErrorRef 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
36 static
37 CFErrorRef 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
58 static
59 CFErrorRef 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
105 struct 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
115 static
116 Boolean 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
128 static
129 CFHashCode digest_mapping_hash(struct digest_mapping *dm) {
130 return CFHash(dm->digest_name) + dm->kclass + dm->digest_length;
131 }
132
133 static
134 CSSM_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
144 static
145 CFErrorRef 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
224 static 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
446 SecTransformRef 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
474 static 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
677 SecTransformRef 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 }