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