]> git.saurik.com Git - apple/security.git/blob - OSX/libsecurity_transform/lib/SecSignVerifyTransform.c
Security-59754.41.1.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>
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 }