]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_transform/lib/SecSignVerifyTransform.c
Security-58286.200.222.tar.gz
[apple/security.git] / OSX / libsecurity_transform / lib / 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> // for dyld_get_program_sdk_version
31
32 const static CFStringRef SignName = CFSTR("com.apple.security.Sign"), VerifyName = CFSTR("com.apple.security.Verify");
33 const CFStringRef __nonnull 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
35 const CFStringRef __nonnull kSecInputIsPlainText = CFSTR("PlainText"), kSecInputIsDigest = CFSTR("Digest"), kSecInputIsRaw = CFSTR("Raw");
36
37 static
38 CFErrorRef 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 CFReleaseNull(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
51 static
52 CFErrorRef 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 CFReleaseNull(dc);
66 if (CFArrayGetCount(*a) != c+1) {
67 return GetNoMemoryError();
68 }
69
70 return NULL;
71 }
72
73 static
74 CFErrorRef 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 CFReleaseNull(out);
103 return GetNoMemoryError();
104 }
105
106 CFArrayRef accumulator = *a;
107 CFReleaseNull(accumulator);
108 *a = NULL;
109
110 // This might be nice:
111 // *data_out = CFDataCreateCopy(NULL, out);
112 // CFReleaseNull(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
120 struct 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
130 static
131 Boolean 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
143 static
144 CFHashCode digest_mapping_hash(struct digest_mapping *dm) {
145 return CFHash(dm->digest_name) + dm->kclass + dm->digest_length;
146 }
147
148 static
149 CSSM_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
159 static
160 CFErrorRef 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
225 case CSSM_ALGID_ECDSA:
226 return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Invalid digest algorithm for ECDSA signature, choose one of: SHA1, or SHA2 (512bits, 348bits, 256bits, or 224 bits)"));
227
228 case CSSM_ALGID_DSA:
229 return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Invalid digest algorithm for DSA signature, only SHA1 is supported"));
230
231 default:
232 return fancy_error(kSecTransformErrorDomain, kSecTransformErrorInvalidAlgorithm, CFSTR("Expected key to be RSA, DSA or ECDSA key"));
233 }
234 }
235
236 static SecTransformInstanceBlock SignTransform(CFStringRef name,
237 SecTransformRef newTransform,
238 SecTransformImplementationRef ref)
239 {
240 SecTransformInstanceBlock instanceBlock = ^
241 {
242 CFErrorRef result = NULL;
243 SecTransformCustomSetAttribute(ref, kSecKeyAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue);
244 SecTransformCustomSetAttribute(ref, kSecInputIsAttributeName, kSecTransformMetaAttributeRequired, kCFBooleanTrue);
245
246 __block CSSM_CC_HANDLE cch;
247 __block SecKeyRef key = NULL;
248 __block SecTransformDataBlock first_process_data = NULL;
249 __block CFStringRef digest = NULL;
250 __block int digest_length = 0;
251 __block CFStringRef input_is = NULL;
252 __block CFMutableArrayRef data_accumulator = NULL;
253 __block struct digest_mapping *sign_alg;
254
255 SecTransformDataBlock plain_text_process_data =
256 ^(CFTypeRef value)
257 {
258 CFDataRef d = value;
259 OSStatus rc;
260
261 if (d) {
262 CSSM_DATA c_d;
263 c_d.Data = (void*)CFDataGetBytePtr(d);
264 c_d.Length = CFDataGetLength(d);
265
266 rc = CSSM_SignDataUpdate(cch, &c_d, 1);
267 SEC_FAIL(rc);
268 } else {
269 CSSM_DATA sig;
270 const int max_sig_size = 32*1024;
271 unsigned char *sig_data = malloc(max_sig_size);
272 sig.Data = sig_data;
273 sig.Length = max_sig_size;
274
275 rc = CSSM_SignDataFinal(cch, &sig);
276 SEC_FAIL(rc);
277 assert(sig.Length <= 32*1024);
278 CSSM_DeleteContext(cch);
279 // Could use NoCopy and hold onto the allocation, and that will be a good idea when we can have it not so oversized
280 CFDataRef result = CFDataCreate(NULL, sig.Data, sig.Length);
281 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, result);
282 CFReleaseNull(result);
283 free(sig_data);
284
285 key = NULL;
286
287 CFReleaseNull(digest);
288 digest = NULL;
289
290 digest_length = 0;
291
292 SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
293
294 return (CFTypeRef)NULL;
295 }
296
297 return SecTransformNoData();
298 };
299
300 SecTransformDataBlock cooked_process_data =
301 ^(CFTypeRef value)
302 {
303 CFDataRef d = value;
304 if (d) {
305 accumulate_data(&data_accumulator, d);
306 } else {
307 CSSM_DATA sig;
308 const int max_sig_size = 32*1024;
309 unsigned char *sig_data = malloc(max_sig_size);
310 sig.Data = sig_data;
311 sig.Length = max_sig_size;
312
313 CFDataRef alldata;
314 CFErrorRef err = fetch_and_clear_accumulated_data(&data_accumulator, &alldata);
315 if (err) {
316 free(sig_data);
317 return (CFTypeRef)err;
318 }
319 CSSM_DATA c_d;
320 c_d.Data = (void*)CFDataGetBytePtr(alldata);
321 c_d.Length = CFDataGetLength(alldata);
322
323 OSStatus rc = CSSM_SignData(cch, &c_d, 1, (input_is == kSecInputIsDigest) ? sign_alg->digest_algo : CSSM_ALGID_NONE, &sig);
324 SEC_FAIL(rc);
325 CFReleaseNull(alldata);
326
327 assert(sig.Length <= 32*1024);
328 CSSM_DeleteContext(cch);
329 // Could use NoCopy and hold onto the allocation, and that will be a good idea when we can have it not so oversized
330 CFDataRef result = CFDataCreate(NULL, sig.Data, sig.Length);
331 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, result);
332 CFReleaseNull(result);
333 free(sig_data);
334
335 key = NULL;
336
337 CFReleaseNull(digest);
338 digest = NULL;
339
340 digest_length = 0;
341
342 SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
343
344 return (CFTypeRef)NULL;
345 }
346
347 return SecTransformNoData();
348 };
349
350 first_process_data = Block_copy(^(CFTypeRef value)
351 {
352 OSStatus rc;
353 if (key && digest && input_is)
354 {
355 const CSSM_KEY *cssm_key;
356 rc = SecKeyGetCSSMKey(key, &cssm_key);
357 SEC_FAIL(rc);
358
359 CFErrorRef bad_alg = pick_sign_alg(digest, digest_length, cssm_key, &sign_alg);
360 if (bad_alg)
361 {
362 return (CFTypeRef)bad_alg;
363 }
364
365 CSSM_CSP_HANDLE csp;
366 rc = SecKeyGetCSPHandle(key, &csp);
367 SEC_FAIL(rc);
368
369 const CSSM_ACCESS_CREDENTIALS *access_cred;
370 rc = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_SIGN, kSecCredentialTypeDefault, &access_cred);
371 SEC_FAIL(rc);
372
373 CSSM_CSP_CreateSignatureContext(csp, alg_for_signature_context(input_is, sign_alg), access_cred, cssm_key, &cch);
374 SEC_FAIL(rc);
375
376 rc = CSSM_SignDataInit(cch);
377 SEC_FAIL(rc);
378
379 SecTransformDataBlock pd = (input_is == kSecInputIsPlainText) ? plain_text_process_data : cooked_process_data;
380
381 SecTransformSetDataAction(ref, kSecTransformActionProcessData, pd);
382 return pd(value);
383 }
384 else
385 {
386 SecTransformPushbackAttribute(ref, kSecTransformInputAttributeName, value);
387 return SecTransformNoData();
388 }
389 });
390
391 SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
392
393 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestTypeAttribute,
394 ^(SecTransformAttributeRef ah, CFTypeRef value)
395 {
396 digest = CFRetainSafe(value);
397 return value;
398 });
399
400 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecKeyAttributeName,
401 ^(SecTransformAttributeRef ah, CFTypeRef value)
402 {
403 if (value == NULL) {
404 return value;
405 }
406
407 const CSSM_KEY *cssm_key;
408 key = (SecKeyRef)value;
409
410 OSStatus rc = SecKeyGetCSSMKey(key, &cssm_key);
411 SEC_FAIL(rc);
412
413 if (((!cssm_key->KeyHeader.KeyUsage) & CSSM_KEYUSE_SIGN) // Keep the previous test to be compatible with existing apps
414 || ((dyld_get_program_sdk_version() >= DYLD_MACOSX_VERSION_10_13) // Better check for newly compiled apps
415 && !(cssm_key->KeyHeader.KeyUsage & (CSSM_KEYUSE_SIGN|CSSM_KEYUSE_ANY))))
416 {
417 key = NULL; // This key cannot sign!
418 CFTypeRef error = CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Key %@ can not be used to sign", key);
419 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, error);
420 return (CFTypeRef)NULL;
421 }
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
461 SecTransformRef 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
489 static 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_SIGN) // Keep the previous test to be compatible with existing apps
543 || ((dyld_get_program_sdk_version() >= DYLD_MACOSX_VERSION_10_13) // Better check for newly compiled apps
544 && !(cssm_key->KeyHeader.KeyUsage & (CSSM_KEYUSE_VERIFY|CSSM_KEYUSE_ANY))))
545 {
546 key = NULL; // This key cannot verify!
547 CFTypeRef error = (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Key %@ can not be used to verify", key);
548 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, error);
549 return (CFTypeRef)NULL;
550 }
551
552 // we don't need to retain this because the owning transform is doing that for us
553 key = (SecKeyRef) value;
554 return value;
555 });
556
557 // We call this when we get the last input and when we get the signature. If both are true when it is
558 // called we are really done, and it gennerates the output
559 void (^done)(void) =
560 ^{
561 if (signature && had_last_input)
562 {
563 CSSM_DATA sig;
564 OSStatus rc;
565 sig.Data = (void*)CFDataGetBytePtr(signature);
566 sig.Length = CFDataGetLength(signature);
567 CFReleaseNull(signature);
568 signature = NULL;
569
570 if (input_is == kSecInputIsPlainText) {
571 rc = CSSM_VerifyDataFinal(cch, &sig);
572 } else {
573 CFDataRef alldata;
574 CFErrorRef err = fetch_and_clear_accumulated_data(&data_accumulator, &alldata);
575 if (err) {
576 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, (CFTypeRef)err);
577 return;
578 }
579
580 CSSM_DATA c_d;
581 c_d.Data = (void*)CFDataGetBytePtr(alldata);
582 c_d.Length = CFDataGetLength(alldata);
583 rc = CSSM_VerifyData(cch, &c_d, 1, (input_is == kSecInputIsDigest) ? verify_alg->digest_algo : CSSM_ALGID_NONE, &sig);
584 CFReleaseNull(alldata);
585
586 }
587 CSSM_DeleteContext(cch);
588 if (rc == 0 || rc == CSSMERR_CSP_VERIFY_FAILED) {
589 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, rc ? kCFBooleanFalse : kCFBooleanTrue);
590 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, NULL);
591 } else {
592 SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, GET_SEC_FAIL(rc));
593 }
594 had_last_input = FALSE;
595 SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
596 }
597 };
598
599 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecSignatureAttributeName,
600 ^(SecTransformAttributeRef ah, CFTypeRef value)
601 {
602 if (value) {
603 signature = CFRetain(value);
604 }
605
606 done();
607
608 return (CFTypeRef)value;
609 });
610
611 SecTransformDataBlock process_data =
612 ^(CFTypeRef value)
613 {
614 OSStatus rc;
615 CFDataRef d = value;
616
617 if (d) {
618 if (input_is == kSecInputIsPlainText) {
619 CSSM_DATA c_d;
620 c_d.Data = (void*)CFDataGetBytePtr(d);
621 c_d.Length = CFDataGetLength(d);
622
623 rc = CSSM_VerifyDataUpdate(cch, &c_d, 1);
624 SEC_FAIL(rc);
625 } else {
626 accumulate_data(&data_accumulator, d);
627 }
628 } else {
629 had_last_input = 1;
630 done();
631 }
632
633 return SecTransformNoData();
634 };
635
636 first_process_data =
637 ^(CFTypeRef value)
638 {
639 if (key && digest && input_is) {
640 // XXX: For RSA keys, signal an error if the digest size>keysize
641
642 OSStatus rc = SecKeyGetCSPHandle(key, &csp);
643 SEC_FAIL(rc);
644
645 rc = SecKeyGetCredentials(key, CSSM_ACL_AUTHORIZATION_ANY, kSecCredentialTypeDefault, &access_cred);
646 SEC_FAIL(rc);
647
648 CFErrorRef bad_alg = pick_sign_alg(digest, digest_length, cssm_key, &verify_alg);
649 if (bad_alg) {
650 return (CFTypeRef)bad_alg;
651 }
652
653 CSSM_CSP_CreateSignatureContext(csp, alg_for_signature_context(input_is, verify_alg), NULL, cssm_key, &cch);
654 SEC_FAIL(rc);
655
656 rc = CSSM_VerifyDataInit(cch);
657 SEC_FAIL(rc);
658
659 SecTransformSetDataAction(ref, kSecTransformActionProcessData, process_data);
660 return process_data(value);
661 } else {
662 SecTransformPushbackAttribute(ref, kSecTransformInputAttributeName, value);
663 return SecTransformNoData();
664 }
665 };
666 first_process_data = Block_copy(first_process_data);
667
668 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestTypeAttribute,
669 ^(SecTransformAttributeRef ah, CFTypeRef value)
670 {
671 digest = CFRetainSafe(value);
672 return value;
673 });
674
675 SecTransformSetTransformAction(ref, kSecTransformActionFinalize,
676 ^{
677 Block_release(first_process_data);
678 return (CFTypeRef)NULL;
679 });
680
681 SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecDigestLengthAttribute,
682 ^(SecTransformAttributeRef ah, CFTypeRef value)
683 {
684 CFNumberGetValue(value, kCFNumberIntType, &digest_length);
685 return value;
686 });
687
688 SecTransformSetDataAction(ref, kSecTransformActionProcessData, first_process_data);
689
690 return result;
691 };
692
693 return Block_copy(instanceBlock);
694 }
695
696 SecTransformRef SecVerifyTransformCreate(SecKeyRef key, CFDataRef signature, CFErrorRef* error)
697 {
698 static dispatch_once_t once;
699 __block Boolean ok = TRUE;
700
701 dispatch_block_t aBlock = ^
702 {
703 ok = SecTransformRegister(VerifyName, &VerifyTransform, error);
704 };
705
706 dispatch_once(&once, aBlock);
707
708 if (!ok)
709 {
710 return NULL;
711 }
712
713
714 SecTransformRef tr = SecTransformCreate(VerifyName, error);
715 if (!tr) {
716 return tr;
717 }
718
719 SecTransformSetAttribute(tr, kSecKeyAttributeName, key, error);
720 if (signature)
721 {
722 SecTransformSetAttribute(tr, kSecSignatureAttributeName, signature, error);
723 }
724 SecTransformSetAttribute(tr, kSecDigestTypeAttribute, kSecDigestSHA1, NULL);
725 SecTransformSetAttribute(tr, kSecInputIsAttributeName, kSecInputIsPlainText, NULL);
726
727 return tr;
728 }