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>
32 const static CFStringRef SignName
= CFSTR("com.apple.security.Sign"), VerifyName
= CFSTR("com.apple.security.Verify");
33 const CFStringRef __nonnull 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
35 const CFStringRef __nonnull kSecInputIsPlainText
= CFSTR("PlainText"), kSecInputIsDigest
= CFSTR("Digest"), kSecInputIsRaw
= CFSTR("Raw");
38 CFErrorRef
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
);
45 #define SEC_FAIL(err) if (err) { \
46 SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, do_sec_fail(err, __func__, __FILE__, __LINE__)); \
47 return (CFTypeRef)NULL; \
49 #define GET_SEC_FAIL(err) do_sec_fail(err, __func__, __FILE__, __LINE__)
52 CFErrorRef
accumulate_data(CFMutableArrayRef
*a
, CFDataRef d
) {
54 *a
= CFArrayCreateMutable(NULL
, 0, &kCFTypeArrayCallBacks
);
56 return GetNoMemoryError();
59 CFDataRef dc
= CFDataCreateCopy(NULL
, d
);
61 return GetNoMemoryError();
63 CFIndex c
= CFArrayGetCount(*a
);
64 CFArrayAppendValue(*a
, dc
);
66 if (CFArrayGetCount(*a
) != c
+1) {
67 return GetNoMemoryError();
74 CFErrorRef
fetch_and_clear_accumulated_data(CFMutableArrayRef
*a
, CFDataRef
*data_out
) {
76 *data_out
= CFDataCreate(NULL
, NULL
, 0);
77 return (*data_out
) ? NULL
: GetNoMemoryError();
80 CFIndex i
, c
= CFArrayGetCount(*a
);
81 CFIndex total
= 0, prev_total
= 0;
83 for(i
= 0; i
< c
; i
++) {
84 total
+= CFDataGetLength((CFDataRef
)CFArrayGetValueAtIndex(*a
, i
));
85 if (total
< prev_total
) {
86 return GetNoMemoryError();
91 CFMutableDataRef out
= CFDataCreateMutable(NULL
, total
);
93 return GetNoMemoryError();
96 for(i
= 0; i
< c
; i
++) {
97 CFDataRef d
= (CFDataRef
)CFArrayGetValueAtIndex(*a
, i
);
98 CFDataAppendBytes(out
, CFDataGetBytePtr(d
), CFDataGetLength(d
));
101 if (CFDataGetLength(out
) != total
) {
103 return GetNoMemoryError();
106 CFArrayRef accumulator
= *a
;
107 CFRelease(accumulator
);
110 // This might be nice:
111 // *data_out = CFDataCreateCopy(NULL, out);
113 // but that is slow (for large values) AND isn't really all that important anyway
120 struct digest_mapping
{
121 // These 3 values are "search values"
122 CSSM_ALGORITHMS kclass
;
123 CFStringRef digest_name
;
127 CSSM_ALGORITHMS plain_text_algo
, digest_algo
;
131 Boolean
digest_mapping_equal(struct digest_mapping
*a
, struct digest_mapping
*b
) {
136 if (a
->kclass
== b
->kclass
&& a
->digest_length
== b
->digest_length
&& !CFStringCompare(a
->digest_name
, b
->digest_name
, 0)) {
144 CFHashCode
digest_mapping_hash(struct digest_mapping
*dm
) {
145 return CFHash(dm
->digest_name
) + dm
->kclass
+ dm
->digest_length
;
149 CSSM_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)) {
155 return CSSM_ALGID_NONE
;
160 CFErrorRef
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
;
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
},
169 {CSSM_ALGID_RSA
, kSecDigestMD2
, 0, CSSM_ALGID_MD2WithRSA
, CSSM_ALGID_MD2
},
170 {CSSM_ALGID_RSA
, kSecDigestMD2
, 128, CSSM_ALGID_MD2WithRSA
, CSSM_ALGID_MD2
},
172 {CSSM_ALGID_RSA
, kSecDigestMD5
, 0, CSSM_ALGID_MD5WithRSA
, CSSM_ALGID_MD5
},
173 {CSSM_ALGID_RSA
, kSecDigestMD5
, 128, CSSM_ALGID_MD5WithRSA
, CSSM_ALGID_MD5
},
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
},
182 {CSSM_ALGID_ECDSA
, kSecDigestSHA1
, 0, CSSM_ALGID_SHA1WithECDSA
, CSSM_ALGID_SHA1
},
183 {CSSM_ALGID_ECDSA
, kSecDigestSHA1
, 160, CSSM_ALGID_SHA1WithECDSA
, CSSM_ALGID_SHA1
},
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
},
191 {CSSM_ALGID_DSA
, kSecDigestSHA1
, 0, CSSM_ALGID_SHA1WithDSA
, CSSM_ALGID_SHA1
},
192 {CSSM_ALGID_DSA
, kSecDigestSHA1
, 160, CSSM_ALGID_SHA1WithDSA
, CSSM_ALGID_SHA1
},
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
));
199 CFSetCallBacks dmcb
= { .version
= 0, .retain
= NULL
, .release
= NULL
, .copyDescription
= NULL
, .equal
= (CFSetEqualCallBack
)digest_mapping_equal
, .hash
= (CFSetHashCallBack
)digest_mapping_hash
};
201 algos
= CFSetCreateMutable(NULL
, mapping_count
, &dmcb
);
203 for(i
= 0; i
< mapping_count
; i
++) {
204 CFSetAddValue(algos
, i
+ (struct digest_mapping
*)digest_mappings
);
208 struct digest_mapping search
;
209 search
.kclass
= ckey
->KeyHeader
.AlgorithmId
;
210 search
.digest_name
= digest
;
211 search
.digest_length
= digest_length
;
213 struct digest_mapping
*dmapping
= (void*)CFSetGetValue(algos
, &search
);
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
) {
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"));
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)"));
231 return fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidAlgorithm
, CFSTR("Invalid digest algorithm for DSA signature, only SHA1 is supported"));
235 return fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidAlgorithm
, CFSTR("Expected key to be RSA, DSA or ECDSA key"));
239 static SecTransformInstanceBlock
SignTransform(CFStringRef name
,
240 SecTransformRef newTransform
,
241 SecTransformImplementationRef ref
)
243 SecTransformInstanceBlock instanceBlock
= ^
245 CFErrorRef result
= NULL
;
246 SecTransformCustomSetAttribute(ref
, kSecKeyAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
247 SecTransformCustomSetAttribute(ref
, kSecInputIsAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
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
;
258 SecTransformDataBlock plain_text_process_data
=
266 c_d
.Data
= (void*)CFDataGetBytePtr(d
);
267 c_d
.Length
= CFDataGetLength(d
);
269 rc
= CSSM_SignDataUpdate(cch
, &c_d
, 1);
273 const int max_sig_size
= 32*1024;
274 unsigned char *sig_data
= malloc(max_sig_size
);
276 sig
.Length
= max_sig_size
;
278 rc
= CSSM_SignDataFinal(cch
, &sig
);
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
);
295 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
297 return (CFTypeRef
)NULL
;
300 return SecTransformNoData();
303 SecTransformDataBlock cooked_process_data
=
308 accumulate_data(&data_accumulator
, d
);
311 const int max_sig_size
= 32*1024;
312 unsigned char *sig_data
= malloc(max_sig_size
);
314 sig
.Length
= max_sig_size
;
317 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
);
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
);
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
= CFRetain(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
)
419 CFTypeRef error
= CreateSecTransformErrorRef(kSecTransformErrorInvalidInput
, "Key %@ can not be used to sign", key
);
420 SecTransformCustomSetAttribute(ref
, kSecTransformAbortAttributeName
, kSecTransformMetaAttributeValue
, error
);
421 return (CFTypeRef
)NULL
;
426 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestLengthAttribute
,
427 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
429 CFNumberGetValue(value
, kCFNumberIntType
, &digest_length
);
433 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecInputIsAttributeName
,
434 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
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
;
444 return (CFTypeRef
)fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidType
, CFSTR("InputIs should be one of: PlainText, Digest, or Raw"));
446 return (CFTypeRef
)input_is
;
449 SecTransformSetTransformAction(ref
, kSecTransformActionFinalize
,
451 Block_release(first_process_data
);
452 return (CFTypeRef
)NULL
;
458 return Block_copy(instanceBlock
);
461 SecTransformRef
SecSignTransformCreate(SecKeyRef key
, CFErrorRef
* error
)
463 static dispatch_once_t once
;
464 __block Boolean ok
= TRUE
;
466 dispatch_block_t aBlock
= ^
468 ok
= SecTransformRegister(SignName
, &SignTransform
, error
);
471 dispatch_once(&once
, aBlock
);
478 SecTransformRef tr
= SecTransformCreate(SignName
, error
);
482 SecTransformSetAttribute(tr
, kSecKeyAttributeName
, key
, error
);
483 SecTransformSetAttribute(tr
, kSecDigestTypeAttribute
, kSecDigestSHA1
, NULL
);
484 SecTransformSetAttribute(tr
, kSecInputIsAttributeName
, kSecInputIsPlainText
, NULL
);
489 static SecTransformInstanceBlock
VerifyTransform(CFStringRef name
,
490 SecTransformRef newTransform
,
491 SecTransformImplementationRef ref
)
493 SecTransformInstanceBlock instanceBlock
= ^
495 CFErrorRef result
= NULL
;
496 SecTransformCustomSetAttribute(ref
, kSecKeyAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
497 SecTransformCustomSetAttribute(ref
, kSecSignatureAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
498 SecTransformCustomSetAttribute(ref
, kSecInputIsAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
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
;
514 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecInputIsAttributeName
,
515 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
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
;
525 return (CFTypeRef
)fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidType
, CFSTR("InputIs should be one of: PlainText, Digest, or Raw"));
527 return (CFTypeRef
)input_is
;
530 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecKeyAttributeName
,
531 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
539 rc
= SecKeyGetCSSMKey((SecKeyRef
)value
, &cssm_key
);
542 if (!cssm_key
->KeyHeader
.KeyUsage
& CSSM_KEYUSE_VERIFY
)
544 // This key cannot verify!
545 return (CFTypeRef
)CreateSecTransformErrorRef(kSecTransformErrorInvalidInput
, "Key %@ can not be used to verify", key
);
548 // we don't need to retain this because the owning transform is doing that for us
549 key
= (SecKeyRef
) value
;
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
557 if (signature
&& had_last_input
)
561 sig
.Data
= (void*)CFDataGetBytePtr(signature
);
562 sig
.Length
= CFDataGetLength(signature
);
563 CFRelease(signature
);
566 if (input_is
== kSecInputIsPlainText
) {
567 rc
= CSSM_VerifyDataFinal(cch
, &sig
);
570 CFErrorRef err
= fetch_and_clear_accumulated_data(&data_accumulator
, &alldata
);
572 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, (CFTypeRef
)err
);
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
);
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
);
588 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, GET_SEC_FAIL(rc
));
590 had_last_input
= FALSE
;
591 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
595 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecSignatureAttributeName
,
596 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
599 signature
= CFRetain(value
);
604 return (CFTypeRef
)value
;
607 SecTransformDataBlock process_data
=
614 if (input_is
== kSecInputIsPlainText
) {
616 c_d
.Data
= (void*)CFDataGetBytePtr(d
);
617 c_d
.Length
= CFDataGetLength(d
);
619 rc
= CSSM_VerifyDataUpdate(cch
, &c_d
, 1);
622 accumulate_data(&data_accumulator
, d
);
629 return SecTransformNoData();
635 if (key
&& digest
&& input_is
) {
636 // XXX: For RSA keys, signal an error if the digest size>keysize
638 OSStatus rc
= SecKeyGetCSPHandle(key
, &csp
);
641 rc
= SecKeyGetCredentials(key
, CSSM_ACL_AUTHORIZATION_ANY
, kSecCredentialTypeDefault
, &access_cred
);
644 CFErrorRef bad_alg
= pick_sign_alg(digest
, digest_length
, cssm_key
, &verify_alg
);
646 return (CFTypeRef
)bad_alg
;
649 CSSM_CSP_CreateSignatureContext(csp
, alg_for_signature_context(input_is
, verify_alg
), NULL
, cssm_key
, &cch
);
652 rc
= CSSM_VerifyDataInit(cch
);
655 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, process_data
);
656 return process_data(value
);
658 SecTransformPushbackAttribute(ref
, kSecTransformInputAttributeName
, value
);
659 return SecTransformNoData();
662 first_process_data
= Block_copy(first_process_data
);
664 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestTypeAttribute
,
665 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
667 digest
= CFRetain(value
);
671 SecTransformSetTransformAction(ref
, kSecTransformActionFinalize
,
673 Block_release(first_process_data
);
674 return (CFTypeRef
)NULL
;
677 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestLengthAttribute
,
678 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
680 CFNumberGetValue(value
, kCFNumberIntType
, &digest_length
);
684 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
689 return Block_copy(instanceBlock
);
692 SecTransformRef
SecVerifyTransformCreate(SecKeyRef key
, CFDataRef signature
, CFErrorRef
* error
)
694 static dispatch_once_t once
;
695 __block Boolean ok
= TRUE
;
697 dispatch_block_t aBlock
= ^
699 ok
= SecTransformRegister(VerifyName
, &VerifyTransform
, error
);
702 dispatch_once(&once
, aBlock
);
710 SecTransformRef tr
= SecTransformCreate(VerifyName
, error
);
715 SecTransformSetAttribute(tr
, kSecKeyAttributeName
, key
, error
);
718 SecTransformSetAttribute(tr
, kSecSignatureAttributeName
, signature
, error
);
720 SecTransformSetAttribute(tr
, kSecDigestTypeAttribute
, kSecDigestSHA1
, NULL
);
721 SecTransformSetAttribute(tr
, kSecDigestTypeAttribute
, kSecDigestSHA1
, NULL
);
722 SecTransformSetAttribute(tr
, kSecInputIsAttributeName
, kSecInputIsPlainText
, NULL
);