]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 | 1 | /* |
d8f41ccd | 2 | * Copyright (c) 2010-2014 Apple Inc. All Rights Reserved. |
b1ab9ed8 | 3 | * |
d8f41ccd A |
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@ | |
b1ab9ed8 A |
22 | */ |
23 | ||
d8f41ccd | 24 | |
b1ab9ed8 A |
25 | #include "SecSignVerifyTransform.h" |
26 | #include "SecCustomTransform.h" | |
27 | #include "Utilities.h" | |
28 | #include <Security/Security.h> | |
29 | #include "misc.h" | |
30 | ||
31 | ||
32 | const static CFStringRef SignName = CFSTR("com.apple.security.Sign"), VerifyName = CFSTR("com.apple.security.Verify"); | |
33 | CFStringRef 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 | CFStringRef kSecInputIsPlainText = CFSTR("PlainText"), kSecInputIsDigest = CFSTR("Digest"), kSecInputIsRaw = CFSTR("Raw"); | |
36 | ||
427c49bc | 37 | static |
b1ab9ed8 | 38 | CFErrorRef do_sec_fail(OSStatus code, const char *func, const char *file, int line) { |
427c49bc | 39 | CFStringRef msg = CFStringCreateWithFormat(NULL, NULL, CFSTR("Internal error #%x at %s %s:%d"), (unsigned)code, func, file, line); |
b1ab9ed8 A |
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 | ||
427c49bc | 51 | static |
b1ab9ed8 A |
52 | CFErrorRef 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 | ||
427c49bc | 73 | static |
b1ab9ed8 A |
74 | CFErrorRef 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 | ||
120 | struct 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 | ||
427c49bc | 130 | static |
b1ab9ed8 A |
131 | Boolean 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 | ||
427c49bc | 143 | static |
b1ab9ed8 A |
144 | CFHashCode digest_mapping_hash(struct digest_mapping *dm) { |
145 | return CFHash(dm->digest_name) + dm->kclass + dm->digest_length; | |
146 | } | |
147 | ||
427c49bc | 148 | static |
b1ab9ed8 A |
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)) { | |
153 | return dm->kclass; | |
154 | } else { | |
155 | return CSSM_ALGID_NONE; | |
156 | } | |
157 | } | |
158 | ||
427c49bc | 159 | static |
b1ab9ed8 A |
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; | |
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 | ||
239 | static 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 | ||
461 | SecTransformRef 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 | ||
489 | static 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); | |
b1ab9ed8 | 579 | rc = CSSM_VerifyData(cch, &c_d, 1, (input_is == kSecInputIsDigest) ? verify_alg->digest_algo : CSSM_ALGID_NONE, &sig); |
427c49bc A |
580 | CFRelease(alldata); |
581 | ||
b1ab9ed8 A |
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 | ||
692 | SecTransformRef 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 | } |