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 }