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> // for dyld_get_program_sdk_version
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 CFReleaseNull(accumulator
);
110 // This might be nice:
111 // *data_out = CFDataCreateCopy(NULL, out);
112 // CFReleaseNull(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"));
225 case CSSM_ALGID_ECDSA
:
226 return fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidAlgorithm
, CFSTR("Invalid digest algorithm for ECDSA signature, choose one of: SHA1, or SHA2 (512bits, 348bits, 256bits, or 224 bits)"));
229 return fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidAlgorithm
, CFSTR("Invalid digest algorithm for DSA signature, only SHA1 is supported"));
232 return fancy_error(kSecTransformErrorDomain
, kSecTransformErrorInvalidAlgorithm
, CFSTR("Expected key to be RSA, DSA or ECDSA key"));
236 static SecTransformInstanceBlock
SignTransform(CFStringRef name
,
237 SecTransformRef newTransform
,
238 SecTransformImplementationRef ref
)
240 SecTransformInstanceBlock instanceBlock
= ^
242 CFErrorRef result
= NULL
;
243 SecTransformCustomSetAttribute(ref
, kSecKeyAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
244 SecTransformCustomSetAttribute(ref
, kSecInputIsAttributeName
, kSecTransformMetaAttributeRequired
, kCFBooleanTrue
);
246 __block CSSM_CC_HANDLE cch
;
247 __block SecKeyRef key
= NULL
;
248 __block SecTransformDataBlock first_process_data
= NULL
;
249 __block CFStringRef digest
= NULL
;
250 __block
int digest_length
= 0;
251 __block CFStringRef input_is
= NULL
;
252 __block CFMutableArrayRef data_accumulator
= NULL
;
253 __block
struct digest_mapping
*sign_alg
;
255 SecTransformDataBlock plain_text_process_data
=
263 c_d
.Data
= (void*)CFDataGetBytePtr(d
);
264 c_d
.Length
= CFDataGetLength(d
);
266 rc
= CSSM_SignDataUpdate(cch
, &c_d
, 1);
270 const int max_sig_size
= 32*1024;
271 unsigned char *sig_data
= malloc(max_sig_size
);
273 sig
.Length
= max_sig_size
;
275 rc
= CSSM_SignDataFinal(cch
, &sig
);
277 assert(sig
.Length
<= 32*1024);
278 CSSM_DeleteContext(cch
);
279 // Could use NoCopy and hold onto the allocation, and that will be a good idea when we can have it not so oversized
280 CFDataRef result
= CFDataCreate(NULL
, sig
.Data
, sig
.Length
);
281 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, result
);
282 CFReleaseNull(result
);
287 CFReleaseNull(digest
);
292 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
294 return (CFTypeRef
)NULL
;
297 return SecTransformNoData();
300 SecTransformDataBlock cooked_process_data
=
305 accumulate_data(&data_accumulator
, d
);
308 const int max_sig_size
= 32*1024;
309 unsigned char *sig_data
= malloc(max_sig_size
);
311 sig
.Length
= max_sig_size
;
314 CFErrorRef err
= fetch_and_clear_accumulated_data(&data_accumulator
, &alldata
);
317 return (CFTypeRef
)err
;
320 c_d
.Data
= (void*)CFDataGetBytePtr(alldata
);
321 c_d
.Length
= CFDataGetLength(alldata
);
323 OSStatus rc
= CSSM_SignData(cch
, &c_d
, 1, (input_is
== kSecInputIsDigest
) ? sign_alg
->digest_algo
: CSSM_ALGID_NONE
, &sig
);
325 CFReleaseNull(alldata
);
327 assert(sig
.Length
<= 32*1024);
328 CSSM_DeleteContext(cch
);
329 // Could use NoCopy and hold onto the allocation, and that will be a good idea when we can have it not so oversized
330 CFDataRef result
= CFDataCreate(NULL
, sig
.Data
, sig
.Length
);
331 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, result
);
332 CFReleaseNull(result
);
337 CFReleaseNull(digest
);
342 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
344 return (CFTypeRef
)NULL
;
347 return SecTransformNoData();
350 first_process_data
= Block_copy(^(CFTypeRef value
)
353 if (key
&& digest
&& input_is
)
355 const CSSM_KEY
*cssm_key
;
356 rc
= SecKeyGetCSSMKey(key
, &cssm_key
);
359 CFErrorRef bad_alg
= pick_sign_alg(digest
, digest_length
, cssm_key
, &sign_alg
);
362 return (CFTypeRef
)bad_alg
;
366 rc
= SecKeyGetCSPHandle(key
, &csp
);
369 const CSSM_ACCESS_CREDENTIALS
*access_cred
;
370 rc
= SecKeyGetCredentials(key
, CSSM_ACL_AUTHORIZATION_SIGN
, kSecCredentialTypeDefault
, &access_cred
);
373 CSSM_CSP_CreateSignatureContext(csp
, alg_for_signature_context(input_is
, sign_alg
), access_cred
, cssm_key
, &cch
);
376 rc
= CSSM_SignDataInit(cch
);
379 SecTransformDataBlock pd
= (input_is
== kSecInputIsPlainText
) ? plain_text_process_data
: cooked_process_data
;
381 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, pd
);
386 SecTransformPushbackAttribute(ref
, kSecTransformInputAttributeName
, value
);
387 return SecTransformNoData();
391 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
393 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestTypeAttribute
,
394 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
396 digest
= CFRetainSafe(value
);
400 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecKeyAttributeName
,
401 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
407 const CSSM_KEY
*cssm_key
;
408 key
= (SecKeyRef
)value
;
410 OSStatus rc
= SecKeyGetCSSMKey(key
, &cssm_key
);
413 if (((!cssm_key
->KeyHeader
.KeyUsage
) & CSSM_KEYUSE_SIGN
) // Keep the previous test to be compatible with existing apps
414 || ((dyld_get_program_sdk_version() >= DYLD_MACOSX_VERSION_10_13
) // Better check for newly compiled apps
415 && !(cssm_key
->KeyHeader
.KeyUsage
& (CSSM_KEYUSE_SIGN
|CSSM_KEYUSE_ANY
))))
417 key
= NULL
; // This key cannot sign!
418 CFTypeRef error
= CreateSecTransformErrorRef(kSecTransformErrorInvalidInput
, "Key %@ can not be used to sign", key
);
419 SecTransformCustomSetAttribute(ref
, kSecTransformAbortAttributeName
, kSecTransformMetaAttributeValue
, error
);
420 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_SIGN
) // Keep the previous test to be compatible with existing apps
543 || ((dyld_get_program_sdk_version() >= DYLD_MACOSX_VERSION_10_13
) // Better check for newly compiled apps
544 && !(cssm_key
->KeyHeader
.KeyUsage
& (CSSM_KEYUSE_VERIFY
|CSSM_KEYUSE_ANY
))))
546 key
= NULL
; // This key cannot verify!
547 CFTypeRef error
= (CFTypeRef
)CreateSecTransformErrorRef(kSecTransformErrorInvalidInput
, "Key %@ can not be used to verify", key
);
548 SecTransformCustomSetAttribute(ref
, kSecTransformAbortAttributeName
, kSecTransformMetaAttributeValue
, error
);
549 return (CFTypeRef
)NULL
;
552 // we don't need to retain this because the owning transform is doing that for us
553 key
= (SecKeyRef
) value
;
557 // We call this when we get the last input and when we get the signature. If both are true when it is
558 // called we are really done, and it gennerates the output
561 if (signature
&& had_last_input
)
565 sig
.Data
= (void*)CFDataGetBytePtr(signature
);
566 sig
.Length
= CFDataGetLength(signature
);
567 CFReleaseNull(signature
);
570 if (input_is
== kSecInputIsPlainText
) {
571 rc
= CSSM_VerifyDataFinal(cch
, &sig
);
574 CFErrorRef err
= fetch_and_clear_accumulated_data(&data_accumulator
, &alldata
);
576 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, (CFTypeRef
)err
);
581 c_d
.Data
= (void*)CFDataGetBytePtr(alldata
);
582 c_d
.Length
= CFDataGetLength(alldata
);
583 rc
= CSSM_VerifyData(cch
, &c_d
, 1, (input_is
== kSecInputIsDigest
) ? verify_alg
->digest_algo
: CSSM_ALGID_NONE
, &sig
);
584 CFReleaseNull(alldata
);
587 CSSM_DeleteContext(cch
);
588 if (rc
== 0 || rc
== CSSMERR_CSP_VERIFY_FAILED
) {
589 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, rc
? kCFBooleanFalse
: kCFBooleanTrue
);
590 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, NULL
);
592 SecTransformCustomSetAttribute(ref
, kSecTransformOutputAttributeName
, kSecTransformMetaAttributeValue
, GET_SEC_FAIL(rc
));
594 had_last_input
= FALSE
;
595 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
599 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecSignatureAttributeName
,
600 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
603 signature
= CFRetain(value
);
608 return (CFTypeRef
)value
;
611 SecTransformDataBlock process_data
=
618 if (input_is
== kSecInputIsPlainText
) {
620 c_d
.Data
= (void*)CFDataGetBytePtr(d
);
621 c_d
.Length
= CFDataGetLength(d
);
623 rc
= CSSM_VerifyDataUpdate(cch
, &c_d
, 1);
626 accumulate_data(&data_accumulator
, d
);
633 return SecTransformNoData();
639 if (key
&& digest
&& input_is
) {
640 // XXX: For RSA keys, signal an error if the digest size>keysize
642 OSStatus rc
= SecKeyGetCSPHandle(key
, &csp
);
645 rc
= SecKeyGetCredentials(key
, CSSM_ACL_AUTHORIZATION_ANY
, kSecCredentialTypeDefault
, &access_cred
);
648 CFErrorRef bad_alg
= pick_sign_alg(digest
, digest_length
, cssm_key
, &verify_alg
);
650 return (CFTypeRef
)bad_alg
;
653 CSSM_CSP_CreateSignatureContext(csp
, alg_for_signature_context(input_is
, verify_alg
), NULL
, cssm_key
, &cch
);
656 rc
= CSSM_VerifyDataInit(cch
);
659 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, process_data
);
660 return process_data(value
);
662 SecTransformPushbackAttribute(ref
, kSecTransformInputAttributeName
, value
);
663 return SecTransformNoData();
666 first_process_data
= Block_copy(first_process_data
);
668 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestTypeAttribute
,
669 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
671 digest
= CFRetainSafe(value
);
675 SecTransformSetTransformAction(ref
, kSecTransformActionFinalize
,
677 Block_release(first_process_data
);
678 return (CFTypeRef
)NULL
;
681 SecTransformSetAttributeAction(ref
, kSecTransformActionAttributeNotification
, kSecDigestLengthAttribute
,
682 ^(SecTransformAttributeRef ah
, CFTypeRef value
)
684 CFNumberGetValue(value
, kCFNumberIntType
, &digest_length
);
688 SecTransformSetDataAction(ref
, kSecTransformActionProcessData
, first_process_data
);
693 return Block_copy(instanceBlock
);
696 SecTransformRef
SecVerifyTransformCreate(SecKeyRef key
, CFDataRef signature
, CFErrorRef
* error
)
698 static dispatch_once_t once
;
699 __block Boolean ok
= TRUE
;
701 dispatch_block_t aBlock
= ^
703 ok
= SecTransformRegister(VerifyName
, &VerifyTransform
, error
);
706 dispatch_once(&once
, aBlock
);
714 SecTransformRef tr
= SecTransformCreate(VerifyName
, error
);
719 SecTransformSetAttribute(tr
, kSecKeyAttributeName
, key
, error
);
722 SecTransformSetAttribute(tr
, kSecSignatureAttributeName
, signature
, error
);
724 SecTransformSetAttribute(tr
, kSecDigestTypeAttribute
, kSecDigestSHA1
, NULL
);
725 SecTransformSetAttribute(tr
, kSecInputIsAttributeName
, kSecInputIsPlainText
, NULL
);