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 17const static CFStringRef SignName = CFSTR("com.apple.security.Sign"), VerifyName = CFSTR("com.apple.security.Verify"); 18CFStringRef 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 20CFStringRef kSecInputIsPlainText = CFSTR("PlainText"), kSecInputIsDigest = CFSTR("Digest"), kSecInputIsRaw = CFSTR("Raw"); 21 22static 23CFErrorRef 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 36static 37CFErrorRef 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 58static 59CFErrorRef 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 105struct 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 115static 116Boolean 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 128static 129CFHashCode digest_mapping_hash(struct digest_mapping *dm) { 130 return CFHash(dm->digest_name) + dm->kclass + dm->digest_length; 131} 132 133static 134CSSM_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 144static 145CFErrorRef 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 224static 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 446SecTransformRef 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 474static 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 677SecTransformRef 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} 711