2 * Copyright (c) 2010-2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
25 #include "SecSignVerifyTransform.h"
26 #include "SecCustomTransform.h"
27 #include "Utilities.h"
28 #include <Security/Security.h>
30 #include <mach-o/dyld_priv.h>
32 #include "simulatecrash_assert.h"
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");
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
);
47 #define SEC_FAIL(err) if (err) { \
48 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, do_sec_fail(err, __func__, __FILE__, __LINE__)); \
49 return (CFTypeRef)NULL; \
51 #define GET_SEC_FAIL(err) do_sec_fail(err, __func__, __FILE__, __LINE__)
54 CFErrorRef
accumulate_data(CFMutableArrayRef
*a
, CFDataRef d
) {
56 *a
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
58 return GetNoMemoryError();
61 CFDataRef dc
= CFDataCreateCopy(NULL
, d
);
63 return GetNoMemoryError();
65 CFIndex c
= CFArrayGetCount(*a
);
66 CFArrayAppendValue(*a
, dc
);
68 if (CFArrayGetCount(*a
) != c
+1) {
69 return GetNoMemoryError();
76 CFErrorRef
fetch_and_clear_accumulated_data(CFMutableArrayRef
*a
, CFDataRef
*data_out
) {
78 *data_out
= CFDataCreate(NULL
, NULL
, 0);
79 return (*data_out
) ? NULL
: GetNoMemoryError();
82 CFIndex i
, c
= CFArrayGetCount(*a
);
83 CFIndex total
= 0, prev_total
= 0;
85 for(i
= 0; i
< c
; i
++) {
86 total
+= CFDataGetLength((CFDataRef
)CFArrayGetValueAtIndex(*a
, i
));
87 if (total
< prev_total
) {
88 return GetNoMemoryError();
93 CFMutableDataRef out
= CFDataCreateMutable(NULL
, total
);
95 return GetNoMemoryError();
98 for(i
= 0; i
< c
; i
++) {
99 CFDataRef d
= (CFDataRef
)CFArrayGetValueAtIndex(*a
, i
);
100 CFDataAppendBytes(out
, CFDataGetBytePtr(d
), CFDataGetLength(d
));
103 if (CFDataGetLength(out
) != total
) {
105 return GetNoMemoryError();
108 CFArrayRef accumulator
= *a
;
109 CFReleaseNull(accumulator
);
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
122 struct digest_mapping
{
123 // These 3 values are "search values"
124 CSSM_ALGORITHMS kclass
;
125 CFStringRef digest_name
;
129 CSSM_ALGORITHMS plain_text_algo
, digest_algo
;
133 Boolean
digest_mapping_equal(struct digest_mapping
*a
, struct digest_mapping
*b
) {
138 if (a
->kclass
== b
->kclass
&& a
->digest_length
== b
->digest_length
&& !CFStringCompare(a
->digest_name
, b
->digest_name
, 0)) {
146 CFHashCode
digest_mapping_hash(struct digest_mapping
*dm
) {
147 return CFHash(dm
->digest_name
) + dm
->kclass
+ dm
->digest_length
;
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)) {
157 return CSSM_ALGID_NONE
;
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
;
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
},
171 {CSSM_ALGID_RSA
, kSecDigestMD2
, 0, CSSM_ALGID_MD2WithRSA
, CSSM_ALGID_MD2
},
172 {CSSM_ALGID_RSA
, kSecDigestMD2
, 128, CSSM_ALGID_MD2WithRSA
, CSSM_ALGID_MD2
},
174 {CSSM_ALGID_RSA
, kSecDigestMD5
, 0, CSSM_ALGID_MD5WithRSA
, CSSM_ALGID_MD5
},
175 {CSSM_ALGID_RSA
, kSecDigestMD5
, 128, CSSM_ALGID_MD5WithRSA
, CSSM_ALGID_MD5
},
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
},
184 {CSSM_ALGID_ECDSA
, kSecDigestSHA1
, 0, CSSM_ALGID_SHA1WithECDSA
, CSSM_ALGID_SHA1
},
185 {CSSM_ALGID_ECDSA
, kSecDigestSHA1
, 160, CSSM_ALGID_SHA1WithECDSA
, CSSM_ALGID_SHA1
},
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
},
193 {CSSM_ALGID_DSA
, kSecDigestSHA1
, 0, CSSM_ALGID_SHA1WithDSA
, CSSM_ALGID_SHA1
},
194 {CSSM_ALGID_DSA
, kSecDigestSHA1
, 160, CSSM_ALGID_SHA1WithDSA
, CSSM_ALGID_SHA1
},
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
));
201 CFSetCallBacks dmcb
= { .version
= 0, .retain
= NULL
, .release
= NULL
, .copyDescription
= NULL
, .equal
= (CFSetEqualCallBack
)digest_mapping_equal
, .hash
= (CFSetHashCallBack
)digest_mapping_hash
};
203 algos
= CFSetCreateMutable(NULL
, mapping_count
, &dmcb
);
205 for(i
= 0; i
< mapping_count
; i
++) {
206 CFSetAddValue(algos
, i
+ (struct digest_mapping
*)digest_mappings
);
210 struct digest_mapping search
;
211 search
.kclass
= ckey
->KeyHeader
.AlgorithmId
;
212 search
.digest_name
= digest
;
213 search
.digest_length
= digest_length
;
215 struct digest_mapping
*dmapping
= (void*)CFSetGetValue(algos
, &search
);
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
) {
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"));
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)"));
231 return fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidAlgorithm
, CFSTR("Invalid digest algorithm for DSA signature, only SHA1 is supported"));
234 return fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidAlgorithm
, CFSTR("Expected key to be RSA, DSA or ECDSA key"));
238 static SecTransformInstanceBlock
SignTransform(CFStringRef name
,
239 SecTransformRef newTransform
,
240 SecTransformImplementationRef ref
)
242 SecTransformInstanceBlock instanceBlock
= ^
244 CFErrorRef result
= NULL
;
245 SecTransformCustomSetAttribute(ref
, kSecKeyAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
246 SecTransformCustomSetAttribute(ref
, kSecInputIsAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
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
;
257 SecTransformDataBlock plain_text_process_data
=
265 c_d
.Data
= (void*)CFDataGetBytePtr(d
);
266 c_d
.Length
= CFDataGetLength(d
);
268 rc
= CSSM_SignDataUpdate(cch
, &c_d
, 1);
272 const int max_sig_size
= 32*1024;
273 unsigned char *sig_data
= malloc(max_sig_size
);
275 sig
.Length
= max_sig_size
;
277 rc
= CSSM_SignDataFinal(cch
, &sig
);
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
);
289 CFReleaseNull(digest
);
294 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
296 return (CFTypeRef
)NULL
;
299 return SecTransformNoData();
302 SecTransformDataBlock cooked_process_data
=
307 accumulate_data(&data_accumulator
, d
);
310 const int max_sig_size
= 32*1024;
311 unsigned char *sig_data
= malloc(max_sig_size
);
313 sig
.Length
= max_sig_size
;
316 CFErrorRef err
= fetch_and_clear_accumulated_data(&data_accumulator
, &alldata
);
319 return (CFTypeRef
)err
;
322 c_d
.Data
= (void*)CFDataGetBytePtr(alldata
);
323 c_d
.Length
= CFDataGetLength(alldata
);
325 OSStatus rc
= CSSM_SignData(cch
, &c_d
, 1, (input_is
== kSecInputIsDigest
) ? sign_alg
->digest_algo
: CSSM_ALGID_NONE
, &sig
);
327 CFReleaseNull(alldata
);
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
);
339 CFReleaseNull(digest
);
344 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
346 return (CFTypeRef
)NULL
;
349 return SecTransformNoData();
352 first_process_data
= Block_copy(^(CFTypeRef value
)
355 if (key
&& digest
&& input_is
)
357 const CSSM_KEY
*cssm_key
;
358 rc
= SecKeyGetCSSMKey(key
, &cssm_key
);
361 CFErrorRef bad_alg
= pick_sign_alg(digest
, digest_length
, cssm_key
, &sign_alg
);
364 return (CFTypeRef
)bad_alg
;
368 rc
= SecKeyGetCSPHandle(key
, &csp
);
371 const CSSM_ACCESS_CREDENTIALS
*access_cred
;
372 rc
= SecKeyGetCredentials(key
, CSSM_ACL_AUTHORIZATION_SIGN
, kSecCredentialTypeDefault
, &access_cred
);
375 CSSM_CSP_CreateSignatureContext(csp
, alg_for_signature_context(input_is
, sign_alg
), access_cred
, cssm_key
, &cch
);
378 rc
= CSSM_SignDataInit(cch
);
381 SecTransformDataBlock pd
= (input_is
== kSecInputIsPlainText
) ? plain_text_process_data
: cooked_process_data
;
383 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, pd
);
388 SecTransformPushbackAttribute(ref
, kSecTransformInputAttributeName
, value
);
389 return SecTransformNoData();
393 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
395 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestTypeAttribute
,
396 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
398 digest
= CFRetainSafe(value
);
402 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecKeyAttributeName
,
403 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
409 const CSSM_KEY
*cssm_key
;
410 key
= (SecKeyRef
)value
;
412 OSStatus rc
= SecKeyGetCSSMKey(key
, &cssm_key
);
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
))))
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
;
428 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestLengthAttribute
,
429 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
431 CFNumberGetValue(value
, kCFNumberIntType
, &digest_length
);
435 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecInputIsAttributeName
,
436 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
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
;
446 return (CFTypeRef
)fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidType
, CFSTR("InputIs should be one of: PlainText, Digest, or Raw"));
448 return (CFTypeRef
)input_is
;
451 SecTransformSetTransformAction(ref
, kSecTransformActionFinalize
,
453 Block_release(first_process_data
);
454 return (CFTypeRef
)NULL
;
460 return Block_copy(instanceBlock
);
463 SecTransformRef
SecSignTransformCreate(SecKeyRef key
, CFErrorRef
* error
)
465 static dispatch_once_t once
;
466 __block Boolean ok
= TRUE
;
468 dispatch_block_t aBlock
= ^
470 ok
= SecTransformRegister(SignName
, &SignTransform
, error
);
473 dispatch_once(&once
, aBlock
);
480 SecTransformRef tr
= SecTransformCreate(SignName
, error
);
484 SecTransformSetAttribute(tr
, kSecKeyAttributeName
, key
, error
);
485 SecTransformSetAttribute(tr
, kSecDigestTypeAttribute
, kSecDigestSHA1
, NULL
);
486 SecTransformSetAttribute(tr
, kSecInputIsAttributeName
, kSecInputIsPlainText
, NULL
);
491 static SecTransformInstanceBlock
VerifyTransform(CFStringRef name
,
492 SecTransformRef newTransform
,
493 SecTransformImplementationRef ref
)
495 SecTransformInstanceBlock instanceBlock
= ^
497 CFErrorRef result
= NULL
;
498 SecTransformCustomSetAttribute(ref
, kSecKeyAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
499 SecTransformCustomSetAttribute(ref
, kSecSignatureAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
500 SecTransformCustomSetAttribute(ref
, kSecInputIsAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
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
;
516 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecInputIsAttributeName
,
517 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
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
;
527 return (CFTypeRef
)fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidType
, CFSTR("InputIs should be one of: PlainText, Digest, or Raw"));
529 return (CFTypeRef
)input_is
;
532 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecKeyAttributeName
,
533 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
541 rc
= SecKeyGetCSSMKey((SecKeyRef
)value
, &cssm_key
);
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
))))
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
;
554 // we don't need to retain this because the owning transform is doing that for us
555 key
= (SecKeyRef
) value
;
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
563 if (signature
&& had_last_input
)
567 sig
.Data
= (void*)CFDataGetBytePtr(signature
);
568 sig
.Length
= CFDataGetLength(signature
);
569 CFReleaseNull(signature
);
572 if (input_is
== kSecInputIsPlainText
) {
573 rc
= CSSM_VerifyDataFinal(cch
, &sig
);
576 CFErrorRef err
= fetch_and_clear_accumulated_data(&data_accumulator
, &alldata
);
578 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, (CFTypeRef
)err
);
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
);
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
);
594 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, GET_SEC_FAIL(rc
));
596 had_last_input
= FALSE
;
597 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
601 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecSignatureAttributeName
,
602 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
605 signature
= CFRetain(value
);
610 return (CFTypeRef
)value
;
613 SecTransformDataBlock process_data
=
620 if (input_is
== kSecInputIsPlainText
) {
622 c_d
.Data
= (void*)CFDataGetBytePtr(d
);
623 c_d
.Length
= CFDataGetLength(d
);
625 rc
= CSSM_VerifyDataUpdate(cch
, &c_d
, 1);
628 accumulate_data(&data_accumulator
, d
);
635 return SecTransformNoData();
641 if (key
&& digest
&& input_is
) {
642 // XXX: For RSA keys, signal an error if the digest size>keysize
644 OSStatus rc
= SecKeyGetCSPHandle(key
, &csp
);
647 rc
= SecKeyGetCredentials(key
, CSSM_ACL_AUTHORIZATION_ANY
, kSecCredentialTypeDefault
, &access_cred
);
650 CFErrorRef bad_alg
= pick_sign_alg(digest
, digest_length
, cssm_key
, &verify_alg
);
652 return (CFTypeRef
)bad_alg
;
655 CSSM_CSP_CreateSignatureContext(csp
, alg_for_signature_context(input_is
, verify_alg
), NULL
, cssm_key
, &cch
);
658 rc
= CSSM_VerifyDataInit(cch
);
661 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, process_data
);
662 return process_data(value
);
664 SecTransformPushbackAttribute(ref
, kSecTransformInputAttributeName
, value
);
665 return SecTransformNoData();
668 first_process_data
= Block_copy(first_process_data
);
670 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestTypeAttribute
,
671 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
673 digest
= CFRetainSafe(value
);
677 SecTransformSetTransformAction(ref
, kSecTransformActionFinalize
,
679 Block_release(first_process_data
);
680 return (CFTypeRef
)NULL
;
683 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestLengthAttribute
,
684 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
686 CFNumberGetValue(value
, kCFNumberIntType
, &digest_length
);
690 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
695 return Block_copy(instanceBlock
);
698 SecTransformRef
SecVerifyTransformCreate(SecKeyRef key
, CFDataRef signature
, CFErrorRef
* error
)
700 static dispatch_once_t once
;
701 __block Boolean ok
= TRUE
;
703 dispatch_block_t aBlock
= ^
705 ok
= SecTransformRegister(VerifyName
, &VerifyTransform
, error
);
708 dispatch_once(&once
, aBlock
);
716 SecTransformRef tr
= SecTransformCreate(VerifyName
, error
);
721 SecTransformSetAttribute(tr
, kSecKeyAttributeName
, key
, error
);
724 SecTransformSetAttribute(tr
, kSecSignatureAttributeName
, signature
, error
);
726 SecTransformSetAttribute(tr
, kSecDigestTypeAttribute
, kSecDigestSHA1
, NULL
);
727 SecTransformSetAttribute(tr
, kSecInputIsAttributeName
, kSecInputIsPlainText
, NULL
);