1/*
2 * Copyright (c) 2010 Apple Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24
25
26#import "SecTransform.h"
27#import "SecCustomTransform.h"
28#import "SecDigestTransform.h"
29#import "SecEncryptTransform.h"
30#import "SecEncodeTransform.h"
31#import "SecDecodeTransform.h"
32#import "SecSignVerifyTransform.h"
33#import "SecNullTransform.h"
34#import "SecExternalSourceTransform.h"
35#import <Security/SecItem.h>
36#import "misc.h"
37#import "Utilities.h"
38#import "SecNullTransform.h"
39#include "regex.h"
40#include <dispatch/dispatch.h>
41#import "SecMaskGenerationFunctionTransform.h"
42#import "SecTransformInternal.h"
43#import "custom.h"
44#include "SecTransformReadTransform.h"
45#import "SecTransformValidator.h"
46#include <sys/types.h>
47#include <sys/sysctl.h>
48#include <ctype.h>
49#include <string.h>
50#include <stdlib.h>
51#include <stdio.h>
52#include <CommonCrypto/CommonCryptor.h>
53#include <sys/stat.h>
54#import "NSData+HexString.h"
55#include <CoreFoundation/CFBase.h>
56#include <CoreFoundation/CFData.h>
57#include <CoreFoundation/CFRuntime.h>
58
59// compatibility layer
60struct SecTransformCreateBlockParameters {
61	CFTypeRef (^send)(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type, CFTypeRef value);
62	CFTypeRef (^get)(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type);
63	CFTypeRef (^pushback)(SecTransformStringOrAttributeRef attribute, CFTypeRef value);
64	CFErrorRef (^overrideTransform)(CFStringRef action, SecTransformActionBlock newAction);
65	CFErrorRef (^overrideAttribute)(CFStringRef action, SecTransformStringOrAttributeRef attribute, SecTransformAttributeActionBlock newAction);
66};
67
68typedef void (^SecTransformCreateBlock)(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params);
69
70SecTransformCreateBlock global_create_block;
71
72static SecTransformInstanceBlock block_for_custom_transform(CFStringRef name, SecTransformRef tr, SecTransformImplementationRef ir)
73{
74	SecTransformInstanceBlock b = ^{
75		// XXX: leak, need to override Finalize and clean up…   (and need to handle caller overriding finalize…)
76		SecTransformCreateBlockParameters *params = static_cast<SecTransformCreateBlockParameters *>(malloc(sizeof(SecTransformCreateBlockParameters)));
77
78		params->overrideAttribute = ^(CFStringRef action, SecTransformStringOrAttributeRef attribute, SecTransformAttributeActionBlock newAction) {
79			// We don't need to special case ProcessData to call SecTransformSetDataAction as there are no longer any uses of it
80			return SecTransformSetAttributeAction(ir, action, attribute, newAction);
81		};
82
83		params->overrideTransform = ^(CFStringRef action, SecTransformActionBlock newAction) {
84			return SecTransformSetTransformAction(ir, action, newAction);
85		};
86
87		params->get = ^(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type) {
88			return SecTranformCustomGetAttribute(ir, attribute, type);
89		};
90
91		params->send = ^(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type, CFTypeRef value) {
92			return SecTransformCustomSetAttribute(ir, attribute, type, value);
93		};
94
95		params->pushback = ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) {
96			return SecTransformPushbackAttribute(ir, attribute, value);
97		};
98
99		params->overrideAttribute = Block_copy(params->overrideAttribute);
100		params->overrideTransform = Block_copy(params->overrideTransform);
101		params->get = Block_copy(params->get);
102		params->send = Block_copy(params->send);
103		params->pushback = Block_copy(params->pushback);
104
105		global_create_block(name, tr, params);
106
107		return (CFErrorRef)NULL;
108	};
109
110	return Block_copy(b);
111}
112
113// Sort of a bridge from the old Custom SPI to the new API, but is also
114// useful when you REALLY need to access stack locals as __block variables,
115// but don't need multithreading, or generic internalizing.
116static SecTransformRef custom_transform(CFStringRef base_name, SecTransformCreateBlock cb)
117{
118	static int ct_cnt = 0;
119	static dispatch_queue_t cnt_q = dispatch_queue_create("com.apple.security.custom_trasnform-cnt", 0);
120	__block CFStringRef name = NULL;
121	__block SecTransformRef ret = NULL;
122
123	dispatch_sync(cnt_q, ^{
124		CFErrorRef err = NULL;
125
126		name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.%d"), base_name, ct_cnt++);
127		global_create_block = cb;
128		if (SecTransformRegister(name, block_for_custom_transform, &err)) {
129			ret = SecTransformCreate(name, &err);
130			if (err) {
131				CFfprintf(stderr, "Error %@ creating %@\n", err, base_name);
132				CFRelease(err);
133			}
134		} else {
135			CFfprintf(stderr, "Error %@ registering %@\n", err, base_name);
136			CFRelease(err);
137		}
138		global_create_block = NULL;
139		CFRelease(name);
140	});
141
142	return ret;
143}
144
145
146#define STAssertErrorHas(err, rx, msg...) STAssertTrue(ErrorHas(err, rx), ##msg);
147
148static BOOL ErrorHas(NSError *error, NSString *rx) {
149	if (!error) {
150		return NO;
151	}
152	if (![error isKindOfClass:[NSError class]]) {
153		return NO;
154	}
155
156	NSString *es = [error description];
157	if (!es) {
158		return NO;
159	}
160	return [es rangeOfString:rx options:NSRegularExpressionSearch].location != NSNotFound;
161}
162
163
164static SecTransformInstanceBlock DelayTransformBlock(CFStringRef name,
165							SecTransformRef newTransform,
166							SecTransformImplementationRef ref)
167{
168	SecTransformInstanceBlock instanceBlock =
169	^{
170		CFErrorRef result = NULL;
171
172
173		SecTransformSetDataAction(ref, kSecTransformActionProcessData,
174			^(CFTypeRef value)
175			{
176
177				if (NULL != value && CFNumberGetTypeID() == CFGetTypeID(value))
178				{
179					long long n;
180					CFNumberGetValue((CFNumberRef)value, kCFNumberLongLongType, &n);
181					usleep((useconds_t)(n / NSEC_PER_USEC));
182				}
183
184				return value;
185			});
186		return result;
187	};
188
189	return Block_copy(instanceBlock);
190}
191
192static SecTransformRef delay_transform(long long nsec) {
193	CFStringRef name = CFSTR("com.apple.security.unit-test.delay");
194
195
196
197	static dispatch_once_t once;
198	__block Boolean ok = TRUE;
199
200	dispatch_block_t aBlock = ^
201	{
202		ok = SecTransformRegister(name, &DelayTransformBlock, NULL);
203	};
204
205	dispatch_once(&once, aBlock);
206
207	if (!ok)
208	{
209		return NULL;
210	}
211
212	SecTransformRef ct = SecTransformCreate(name, NULL);
213	CFNumberRef nr = CFNumberCreate(NULL, kCFNumberLongLongType, &nsec);
214	SecTransformSetAttribute(ct, CFSTR("DELAY"), nr, NULL);
215	CFRelease(nr);
216
217	return ct;
218}
219
220@implementation custom
221
222class BufferStream
223{
224protected:
225	const char* mBuffer;
226	size_t mLength;
227	size_t mStringLength;
228	size_t mPos;
229
230	char *mCurrentString;
231
232public:
233	BufferStream(const char* buffer, size_t length) : mBuffer(buffer), mLength(length), mStringLength(0), mPos(0), mCurrentString(NULL) {}
234	~BufferStream();
235
236	const char* GetNextString();
237	void SplitString(const char*& stringA, const char*& stringB);
238};
239
240
241
242BufferStream::~BufferStream()
243{
244	if (NULL != mCurrentString)
245	{
246		free(mCurrentString);
247	}
248}
249
250
251
252const char* BufferStream::GetNextString()
253{
254	size_t p = mPos;
255	if (p >= mLength)
256	{
257		return NULL; // eof
258	}
259
260	// run to either the end of the buffer or a return
261	while (p < mLength && mBuffer[p] != '\n')
262	{
263		p += 1;
264	}
265
266	if (p != mLength)
267	{
268		// handle the end of the buffer specially, since it doesn't point
269		// to valid space
270		p -= 1;
271	}
272
273	// p now points to the last character in the string
274	// allocate memory for our buffer
275	mStringLength = p - mPos + 1;
276	mCurrentString = (char*) realloc(mCurrentString, mStringLength + 1);
277	memmove(mCurrentString, mBuffer + mPos, mStringLength);
278	mCurrentString[mStringLength] = 0;
279	mPos = p + 2;
280
281	return mCurrentString;
282}
283
284
285
286void BufferStream::SplitString(const char*& a, const char*& b)
287{
288	// scan the buffer, looking for a ':'
289	size_t p = 0;
290	while (mCurrentString[p] != 0 && mCurrentString[p] != ':')
291	{
292		p += 1;
293	}
294
295	// the first string is always our buffer pointer
296	a = mCurrentString;
297
298	if (mCurrentString[p] == ':')
299	{
300		mCurrentString[p] = 0;
301
302		// look for the beginning of the next string
303		p += 1;
304		while (p < mLength && isspace(mCurrentString[p]))
305		{
306			p += 1;
307		}
308
309		b = mCurrentString + p;
310	}
311	else
312	{
313		b = NULL;
314	}
315}
316
317
318
319-(void)disabledtestzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
320{
321	// open leaks and make a connection to it.
322	char* name;
323
324	const int kChunkSize = 16384;
325	char buffer[kChunkSize];
326	pid_t thePid = getpid();
327	asprintf(&name, "/tmp/leaks%d.txt", thePid);
328	sprintf(buffer, "/usr/bin/leaks %d >%s", thePid, name);
329	system(buffer);
330
331	struct stat st;
332	stat(name, &st);
333
334	char* rBuffer = (char*) malloc((size_t)st.st_size);
335	FILE* f = fopen(name, "r");
336	fread(rBuffer, 1, (size_t)st.st_size, f);
337	fclose(f);
338
339	// set up our output parser
340	BufferStream bStream(rBuffer, (size_t)st.st_size);
341	const char* s = bStream.GetNextString();
342
343	bool didError = true;
344
345	if (NULL != s)
346	{
347		// we have our string, split it and see what it means
348		const char* key;
349		const char* value;
350
351		bStream.SplitString(key, value);
352		if (strcmp(key, "leaks Report Version") != 0 || strcmp(value, "2.0") != 0)
353		{
354			didError = true;
355		}
356		else
357		{
358			didError = false;
359		}
360	}
361
362	if (!didError)
363	{
364		const char* key;
365		const char* value;
366
367		// figure out what our target line will look like
368		char* target;
369		asprintf(&target, "Process %d", thePid);
370
371		const char* nextString = bStream.GetNextString();
372		while (nextString)
373		{
374			bStream.SplitString(key, value);
375			if (strcmp(key, target) == 0) // we found our target!!!
376			{
377				// do it again
378				bStream.GetNextString();
379				bStream.SplitString(key, value);
380
381				if (value[0] != '0') // we have a non-zero result... :(
382				{
383					didError = true;
384				}
385			}
386
387			nextString = bStream.GetNextString();
388		}
389
390		free(target);
391	}
392
393	STAssertFalse(didError, @"You have leaks!");
394
395	if (didError)
396	{
397		// dump to our output file
398		// make a file name for the leaks output
399		FILE* f = fopen(name, "w");
400		fwrite(rBuffer, 1, (size_t)st.st_size, f);
401		fclose(f);
402	}
403	else
404	{
405		unlink(name);
406	}
407
408	free(name);
409}
410
411
412
413static const char* gHMACText = "The judicial Power shall extend to all Cases, in "
414							   "Law and Equity, arising under this Constitution, "
415							   "the Laws of the United States, and Treaties made, "
416							   "or which shall be made, under their Authority;--to "
417							   "all Cases affecting Ambassadors, other public "
418							   "Ministers and Consuls;--to all Cases of admiralty "
419							   "and maritime Jurisdiction;--to Controversies to "
420							   "which the United States shall be a Party;--to "
421							   "Controversies between two or more States;-- "
422							   "between a State and Citizens of another State, "
423							   "--between Citizens of different States,-- "
424							   "between Citizens of the same State claiming Lands "
425							   "under Grants of different States, and between a "
426							   "State, or the Citizens thereof, and foreign "
427							   "States, Citizens or Subjects";
428
429const NSString* gAbortTransformName = (NSString*) kSecTransformAbortAttributeName;
430
431static const char* gHMACKey = "No person shall be held to answer for a capital, or "
432							  "otherwise infamous crime, unless on a presentment "
433							  "or indictment of a Grand Jury, except in cases "
434							  "arising in the land or naval forces, or in the "
435							  "Militia, when in actual service in time of War or "
436							  "public danger; nor shall any person be subject for "
437							  "the same offence to be twice put in jeopardy of life "
438							  "or limb; nor shall be compelled in any criminal case "
439							  "to be a witness against himself, nor be deprived of "
440							  "life, liberty, or property, without due process of "
441							  "law; nor shall private property be taken for public "
442							  "use, without just compensation.";
443
444static const u_int8_t gSHA1HMAC[] = {0x2f, 0x68, 0x4b, 0x6b, 0x4f,
445									 0xf7, 0x41, 0xc3, 0x76, 0x3d,
446									 0x0b, 0xc3, 0x25, 0x02, 0x99,
447									 0x03, 0xfa, 0xa5, 0xe9, 0xde};
448
449static const u_int8_t gSHA256HMAC[] = {0xc2, 0x5c, 0x9a, 0x65, 0x08, 0x9e, 0x61, 0xb5,
450									   0x03, 0xfe, 0xcb, 0x57, 0xb7, 0x55, 0x4f, 0x69,
451									   0xdb, 0xef, 0xdb, 0xe7, 0x0d, 0xe2, 0x78, 0x2e,
452									   0xf9, 0x48, 0xbd, 0xf6, 0x4f, 0x4b, 0x94, 0x0c};
453-(void)testPaddings
454{
455    CFStringRef paddings[] = {kSecPaddingNoneKey, kSecPaddingPKCS7Key, kSecPaddingPKCS5Key, kSecPaddingPKCS1Key};
456
457    for(int i = 0; i < sizeof(paddings) / sizeof(*paddings); i++) {
458        CFErrorRef error = NULL;
459        SecKeyRef cryptokey = NULL;
460        SecTransformRef encrypt = NULL, decrypt = NULL;
461        CFDataRef cfdatacryptokey = NULL, sourceData = NULL, encryptedData = NULL, decryptedData = NULL;
462        const uint8_t rawcryptokey[16] = { 63, 17, 27, 99, 185, 231, 1, 191, 217, 74, 141, 16, 12, 99, 253, 41 }; // 128-bit AES key.
463        const char *sourceCString = "All these worlds are yours except Europa.";  // I'm not so sure about that Earth one either
464
465        CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(
466                                                                      kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
467                                                                      &kCFTypeDictionaryValueCallBacks);
468
469        CFDictionarySetValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeAES);
470
471        cfdatacryptokey = CFDataCreate(kCFAllocatorDefault, rawcryptokey,
472                                       sizeof(rawcryptokey));
473        cryptokey = SecKeyCreateFromData(parameters,
474                                         cfdatacryptokey, &error);
475        STAssertNil((id)error, @"Unexpected SecKeyCreateFromData error: %@", error);
476
477        size_t len = strlen(sourceCString) +1;
478        if (paddings[i] == kSecPaddingNoneKey) {
479            STAssertTrue(len >= kCCBlockSizeAES128, @"Had at least one block");
480            // Get to an AES block multiple, discarding bytes wildly.
481            len -= len % kCCBlockSizeAES128;
482        }
483        sourceData = (CFDataRef)[NSData dataWithBytes:sourceCString length:len];
484
485        encrypt = SecEncryptTransformCreate(cryptokey, &error);
486        STAssertNil((id)error, @"Unexpected error creating encrypt transform: %@", error);
487        decrypt = SecDecryptTransformCreate(cryptokey, &error);
488        STAssertNil((id)error, @"Unexpected error creating decrypt transform: %@", error);
489
490        /* Set the padding on the transforms */
491        SecTransformSetAttribute(encrypt, kSecPaddingKey, paddings[i], &error);
492        STAssertNil((id)error, @"Couldn't set encrypt padding to %@: %@", paddings[i], error);
493        SecTransformSetAttribute(decrypt, kSecPaddingKey, paddings[i], &error);
494        STAssertNil((id)error, @"Couldn't set decrypt padding to %@: %@", paddings[i], error);
495
496        SecTransformSetAttribute(encrypt, kSecTransformInputAttributeName, sourceData, &error);
497        STAssertNil((id)error, @"Couldn't set encrypt transform input: %@", error);
498
499        encryptedData = (CFDataRef)SecTransformExecute(encrypt, &error);
500        STAssertNil((id)error, @"Couldn't execute encrypt: %@ (padding %@)", paddings[i], error);
501        STAssertNotNil((id)encryptedData, @"Didn't get encrypted data");
502
503        SecTransformSetAttribute(decrypt, kSecTransformInputAttributeName, encryptedData, &error);
504        STAssertNil((id)error, @"Couldn't set decrypt transform input: %@", error);
505
506        decryptedData = (CFDataRef)SecTransformExecute(decrypt, &error);
507        STAssertNil((id)error, @"Couldn't execute decrypt: %@", error);
508        STAssertNotNil((id)decryptedData, @"Didn't get decrypt data");
509
510        STAssertEqualObjects((id)decryptedData, (id)sourceData, @"Decrypt output didn't match encrypt input for padding %@", paddings[i]);
511    }
512}
513
514static SecTransformInstanceBlock nopInstance(CFStringRef name, SecTransformRef newTransform, SecTransformImplementationRef ref)
515{
516    SecTransformInstanceBlock instanceBlock = ^{
517        return (CFErrorRef)NULL;
518    };
519
520    return Block_copy(instanceBlock);
521}
522
523
524-(void)test_manyregister
525{
526    dispatch_apply(4000, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^(size_t i) {
527        NSString *name = [NSString stringWithFormat:@"many%luregister", i];
528        CFErrorRef err = NULL;
529        BOOL ok = SecTransformRegister((CFStringRef)name, nopInstance, &err);
530        STAssertTrue(ok, @"register not ok");
531        STAssertNil((id)err, @"register error: %@", err);
532    });
533}
534
535-(void)test_emptyOAEP
536{
537    SecKeychainRef tmp_keychain = NULL;
538    char *kcfname;
539    asprintf(&kcfname, "%s-OAEP-XXXXXXXXXX", "/tmp/");
540    const char *passwd = "sekret";
541    // NOTE: "mktemp" isn't as safe as you might think...but this is test code and doesn't have to be, but
542    // if you copy it elsewhere you may well need to rewrite it.  (use mkstemp)
543    mktemp(kcfname);
544    OSStatus status = SecKeychainCreate(kcfname, (UInt32)strlen(passwd), passwd, NO, NULL, &tmp_keychain);
545    STAssertTrue(status == 0, @"Expected to make keychain, but got error 0x%x", status);
546
547    const char *pem_key_bytes[] = {
548        // From the spec
549        "-----BEGIN PUBLIC KEY-----\nMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC7+C8JBoLOnCM4rCudqHH3No0H\n7tQQQ6RA1rbwdFT1H7jfuq8DXAKrYepIzutvzUh27VINYOHsRhlxnYpbi4B/r7jg\no9/HN3I+5rS32TolhO5qZJ0GCVN0iDSyRUWYOU7gqrEte2GlH1J6mkH2wWh/4lNy\nmMoqj1lG+OX9CR29ywIBEQ==\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQC7+C8JBoLOnCM4rCudqHH3No0H7tQQQ6RA1rbwdFT1H7jfuq8D\nXAKrYepIzutvzUh27VINYOHsRhlxnYpbi4B/r7jgo9/HN3I+5rS32TolhO5qZJ0G\nCVN0iDSyRUWYOU7gqrEte2GlH1J6mkH2wWh/4lNymMoqj1lG+OX9CR29ywIBEQKB\ngQCl2vxTQfryicS5iNswwc34PzElHgZotCeEgTgBV5ZBspQQs8eZjWvEZXReXDkm\nadaHDaLAgqk543/cuC7JPtrJf/OtWVCsz7wRHHbxqVKUROVqr2jFbAks043DvvXS\nCpOZJu1PdKE+3fvhoc7MSJSvlCjCt7iIP+RGOkvIWxyzwQJBAO7ProGxubPJCIEL\nEKG1YAGZ659ErvT9pJO4Gp49hPYyEk7wI25dHjt+KPrnqgQKLVslIXZFnR85dUG6\nKlj7ZZkCQQDJf7HwJ/RT9jQSM+qq0dk1P2xC0IhmsdBaDyA1AoudhphAtBZmtC6S\n6g2jtDIEtc/OM1JSTQQWpaRB5wCvRhUDAkBUSUymProDN+TiQCP81ppa6wfd3AGD\npNCsm1SwUfKxPtlJCXXqt3QU/1nB92kumi4gKzj8kQpHQXStyTwfZ8mBAkBHHgKQ\n/wrwdQNRt/h4hkypYa29Oop+mRxcBVapTDFGp/mAP49viuNC6TH9iuR6Ig0bmaSV\nhJgH/jn5JFqYNto9AkEAsGxP2rtjARmNJlvbrpQjs4Dycfc0U4hQkwd/zTniEZ/J\nhjIVT1iDsWepZ79AK06eLg+WVuaY6jZm7fsleYA59w==\n-----END RSA PRIVATE KEY-----\n",
550        NULL,
551    };
552    struct key_pair {
553        SecKeyRef pubKey, privKey;
554    };
555    key_pair keys[1];
556
557    int i;
558    for(i = 0; i < sizeof(keys)/sizeof(key_pair); i++) {
559        NSAssert(pem_key_bytes[i] != NULL, @"Expected a key");
560        NSLog(@"Importing: %s", pem_key_bytes[i]);
561        CFDataRef pem_data = CFDataCreate(NULL, (UInt8*)(pem_key_bytes[i]), strlen(pem_key_bytes[i]));
562        SecKeyImportExportParameters import_params;
563        bzero(&import_params, sizeof(import_params));
564
565        import_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
566        import_params.keyUsage = CSSM_KEYUSE_ANY;
567        import_params.keyAttributes = CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE;
568        import_params.accessRef = NULL;
569        import_params.passphrase = CFSTR("");
570        import_params.alertPrompt = CFSTR("");
571
572        CFArrayRef keypair = NULL;
573        SecExternalFormat key_format = kSecFormatOpenSSL;
574        SecExternalItemType itemType = kSecItemTypeUnknown;
575        status = SecKeychainItemImport(pem_data, CFSTR(".pem"), &key_format, &itemType, 0, &import_params, tmp_keychain, &keypair);
576        STAssertTrue(status == 0, @"Expected pubkey import to be ok, got err=0x%x", status);
577        NSAssert(keypair != NULL, @"Expected to get some keys back");
578        STAssertNotNil((id)keypair, @"Expected to get some keys back");
579        STAssertTrue(CFArrayGetCount(keypair) == 2, @"Expected 2 keys, got %@", keypair);
580        keys[i].pubKey = (SecKeyRef)CFArrayGetValueAtIndex(keypair, 0);
581        keys[i].privKey = (SecKeyRef)CFArrayGetValueAtIndex(keypair, 1);
582    }
583    STAssertNil((id)pem_key_bytes[i], @"Expected to convert all pem keys, but found at least: %s", pem_key_bytes[i]);
584    CFDataRef encoding_parameters = CFDataCreate(NULL, NULL, 0);
585
586
587	CFErrorRef err = NULL;
588
589	SecTransformRef encryptor = SecEncryptTransformCreate(keys[0].pubKey, &err);
590
591	CFReadStreamRef empty_stream = CFReadStreamCreateWithBytesNoCopy(NULL, (UInt8*)"", 0, kCFAllocatorNull);
592	SecTransformSetAttribute(encryptor, kSecTransformInputAttributeName, empty_stream, &err);
593	SecTransformSetAttribute(encryptor, kSecPaddingKey, kSecPaddingOAEPKey, &err);
594	SecTransformSetAttribute(encryptor, kSecOAEPEncodingParametersAttributeName, encoding_parameters, &err);
595
596	CFTypeRef encryptedData = SecTransformExecute(encryptor, &err);
597	STAssertNotNil((id)encryptedData, @"Expected to get encrypted data");
598	STAssertNil((NSError*)err, @"Expected no error, got err=%@", err);
599	// Can't support "seed" with commoncrypto, just check round trip.
600	//STAssertEqualObjects((id)encryptedData, (id)tests[i].encryptedMessage, @"encrypted data should have matched test vector (%@) data", tests[i].label);
601	CFRelease(encryptor);
602
603	SecTransformRef decryptor = SecDecryptTransformCreate(keys[0].privKey, NULL);
604	// XXX: totally round trip, not even partial KAT (KAT can't really be done on OAEP
605	// without supporitng settign the seed externally)
606	SecTransformSetAttribute(decryptor, kSecTransformInputAttributeName, encryptedData, NULL);
607	SecTransformSetAttribute(decryptor, kSecPaddingKey, kSecPaddingOAEPKey, NULL);
608	SecTransformSetAttribute(decryptor, kSecOAEPEncodingParametersAttributeName, encoding_parameters, NULL);
609	CFTypeRef decryptedData = SecTransformExecute(decryptor, &err);
610	STAssertNil((id)err, @"Expected no error, got: %@", err);
611	STAssertNotNil((id)decryptedData, @"Expected to get decrypted data");
612	// What do we expect an empty enc/dec to look like?   Mostly "not a crash"
613	CFDataRef empty_data = CFDataCreate(NULL, (UInt8*)"", 0);
614	STAssertEqualObjects((id)decryptedData, (id)empty_data, @"Expected decrypted data to match original message");
615	CFRelease(decryptor);
616    sleep(5);
617
618    return;
619}
620
621-(void)testzzzzZZZZ
622{
623	// Give xcode a little time to parse all the output before the unit tests exit
624	sleep(2);
625}
626
627-(void)test_multiOAEP
628{
629    SecKeychainRef tmp_keychain = NULL;
630    char *kcfname;
631    asprintf(&kcfname, "%s-OAEP-XXXXXXXXXX", "/tmp/");
632    const char *passwd = "sekret";
633    // NOTE: "mktemp" isn't as safe as you might think...but this is test code and doesn't have to be, but
634    // if you copy it elsewhere you may well need to rewrite it.  (use mkstemp)
635    mktemp(kcfname);
636    OSStatus status = SecKeychainCreate(kcfname, (UInt32)strlen(passwd), passwd, NO, NULL, &tmp_keychain);
637    STAssertTrue(status == 0, @"Expected to make keychain, but got error 0x%x", status);
638
639    const char *pem_key_bytes[] = {
640        // From the spec
641        "-----BEGIN PUBLIC KEY-----\nMIGdMA0GCSqGSIb3DQEBAQUAA4GLADCBhwKBgQC7+C8JBoLOnCM4rCudqHH3No0H\n7tQQQ6RA1rbwdFT1H7jfuq8DXAKrYepIzutvzUh27VINYOHsRhlxnYpbi4B/r7jg\no9/HN3I+5rS32TolhO5qZJ0GCVN0iDSyRUWYOU7gqrEte2GlH1J6mkH2wWh/4lNy\nmMoqj1lG+OX9CR29ywIBEQ==\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICWwIBAAKBgQC7+C8JBoLOnCM4rCudqHH3No0H7tQQQ6RA1rbwdFT1H7jfuq8D\nXAKrYepIzutvzUh27VINYOHsRhlxnYpbi4B/r7jgo9/HN3I+5rS32TolhO5qZJ0G\nCVN0iDSyRUWYOU7gqrEte2GlH1J6mkH2wWh/4lNymMoqj1lG+OX9CR29ywIBEQKB\ngQCl2vxTQfryicS5iNswwc34PzElHgZotCeEgTgBV5ZBspQQs8eZjWvEZXReXDkm\nadaHDaLAgqk543/cuC7JPtrJf/OtWVCsz7wRHHbxqVKUROVqr2jFbAks043DvvXS\nCpOZJu1PdKE+3fvhoc7MSJSvlCjCt7iIP+RGOkvIWxyzwQJBAO7ProGxubPJCIEL\nEKG1YAGZ659ErvT9pJO4Gp49hPYyEk7wI25dHjt+KPrnqgQKLVslIXZFnR85dUG6\nKlj7ZZkCQQDJf7HwJ/RT9jQSM+qq0dk1P2xC0IhmsdBaDyA1AoudhphAtBZmtC6S\n6g2jtDIEtc/OM1JSTQQWpaRB5wCvRhUDAkBUSUymProDN+TiQCP81ppa6wfd3AGD\npNCsm1SwUfKxPtlJCXXqt3QU/1nB92kumi4gKzj8kQpHQXStyTwfZ8mBAkBHHgKQ\n/wrwdQNRt/h4hkypYa29Oop+mRxcBVapTDFGp/mAP49viuNC6TH9iuR6Ig0bmaSV\nhJgH/jn5JFqYNto9AkEAsGxP2rtjARmNJlvbrpQjs4Dycfc0U4hQkwd/zTniEZ/J\nhjIVT1iDsWepZ79AK06eLg+WVuaY6jZm7fsleYA59w==\n-----END RSA PRIVATE KEY-----\n",
642        // The next 10 are from oaep-vect.txt (via a lot of OpenSSL higgerdy-jiggerdey)
643        "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCos7KEr461CzhwNKhg8UbEkZ8x\nh2PNbFWYyK5IEaHgq8TH4LCC1pOl5/ztZ1z0ZoUSdywMvGSnQsbGMPUzyMxy9iro\nM8QL8lhC6YS7eL2/l8AQfVW9tmL1xOD6uYRctRSO9zkt06r/k64ea2Z7s9QkdhbU\n9boQ1M/SJt6I058W+wIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQCos7KEr461CzhwNKhg8UbEkZ8xh2PNbFWYyK5IEaHgq8TH4LCC\n1pOl5/ztZ1z0ZoUSdywMvGSnQsbGMPUzyMxy9iroM8QL8lhC6YS7eL2/l8AQfVW9\ntmL1xOD6uYRctRSO9zkt06r/k64ea2Z7s9QkdhbU9boQ1M/SJt6I058W+wIDAQAB\nAoGAUzOc/befyEZqZVxzFqyoXFX9j23YmP2vEZUX709S6P2OJY35P+4YD6Dkqylp\nPNg7FSpVPUrE0YEri5+lrw5/Vf5zBN9BVwkm8zEfFcTWWnMsSDEW7j09LQrzVJrZ\nv3y/t4rYhPhNW+sEck3HNpsx3vN9DPU56c/N095lNynq1dECQQDTJzfnJn/+E0Gy\n1cDRUKgbWG+zEyvtL41SYoZKnLnzCvOL5EhZjUE6Fy77gCwhrPHBHFIMLyakcdyt\nIS6sfKOdAkEAzIhT0dVNpjD6wAT0cfKBx7iYLYIkpJDtvrM9Pj1cyTxHZXA9HdeR\nZC8fEWoN2FK+JBmyr3K/6aAw6GCwKItddwJADhK/FxjpzvVZm6HDiC/oBGqQh07v\nzo8szCDk8nQfsKM6OEiuyckwX77L0tdoGZZ9RnGsxkMeQDeWjbN4eOaVwQJBAJUp\new+Vovpn0AcH1gnf1PwFyJ2vwu9tbqVb7HceozNzTZJR55CC7NqGbv7xPEWeGmMT\nhrfjVMiZ9fESyoXXFYMCQE9FbFAkk73A7Sq3VqOm7U1nNSppfUIW6TISsSemPVQR\nzm+pjV2+/XMmPjcoFCdDgYFm7X3WNofdKoyh0vT72OE=\n-----END RSA PRIVATE KEY-----\n",
644        "RSA key ok\n-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQGUfH/OkEJfRyeecIUfJdXmIxb+\nih3xk3Hj5ijiYFQ+SQHvYIH2jAuBQRkNKujaun0SUOxttjbpROw3Iod8fB0KZ/FL\nFpTF8DeUUaQ+SaMt3oNnC3PakaHJm8I7Q2pgBVxhDwuvmcGgeVZblaPxUmYy0dTa\nYPIO2iXmU8TwAnZvRQIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICXAIBAAKBgQGUfH/OkEJfRyeecIUfJdXmIxb+ih3xk3Hj5ijiYFQ+SQHvYIH2\njAuBQRkNKujaun0SUOxttjbpROw3Iod8fB0KZ/FLFpTF8DeUUaQ+SaMt3oNnC3Pa\nkaHJm8I7Q2pgBVxhDwuvmcGgeVZblaPxUmYy0dTaYPIO2iXmU8TwAnZvRQIDAQAB\nAoGAaHJZomJX8Thzf5M4nNltSXcIKgRKRSY4w4ucRRBw0ICTslduV9bD5cWEjYTm\nCg0b3M3ur0ndFhFJGdedusRlzrJ3phMQcCvg8AygYOPN4gqYbIqz7xshfRxwQoGT\nGwFbOc4FQzlmlGna+VJDZ8sxykucXXKZh+wfN0vR7xXmj6UCQQFZ294Eoz7wb7YI\nuAsZD00+IrzBOsjkoIEDOr+kFu2wsziqCLVzCepaUkDn3G5UN4xpQUwx2X3bH0Bt\ns3acxBpDAkEBK2UvMEA7OLQJlf1v9BoazIracDcyNrcgLTmy7jDPtG2wlRH28wfM\nYcwhYGwYp1uKYvgi3wMboN8Nr9VQb1aL1wJAQ271CN5zZRnC2kxYDZjILLdFKj+1\n763Ducd4mhvGWE95Wt270yQ5x0aGVS7LbCwwek069/U57sFXJIx7MfGiVQJBASsV\nqJ89+ys5Bz5z8CvdDBp7N53UNfBc3eLv+eRilIt87GLukFDV4IFuB4WoVrSRCNy3\nXzaDh00cpjKaGQEwZv8CQAJw2xfVkUsBjXYRiyQ4mnNQ7INrAGOiFyEjb9jtttib\nUefuuHthG3Eyy36nNWwjFRwed1FQfHhtnuF5QXCoyOg=\n-----END RSA PRIVATE KEY-----\n",
645        "RSA key ok\n-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQK1j+wDmoYHAKTXtkYvk+bN1JEW\nHd109OgQtA48FlIAalwneyd0wRMFpMurWnjvpX4XqG33o/o2/EsdIknyLsfC3WpG\nMjKszqkG1m6+gLVwSxBynab4MyNKu1791KKSy/rTO00z+noUuMOXtW46zSEgNCi3\nfN+jOm2nBrPYsPxD6QIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICXQIBAAKBgQK1j+wDmoYHAKTXtkYvk+bN1JEWHd109OgQtA48FlIAalwneyd0\nwRMFpMurWnjvpX4XqG33o/o2/EsdIknyLsfC3WpGMjKszqkG1m6+gLVwSxBynab4\nMyNKu1791KKSy/rTO00z+noUuMOXtW46zSEgNCi3fN+jOm2nBrPYsPxD6QIDAQAB\nAoGAFbSKW1aDqUZw4jtXGPgU+g4T+FA49QcRGCy6YVEFgfPSLH4jLvk34i5VHWi4\nbi+MsarYvi5Ij13379J54/Vo1Orzb4DPcUGs5g/MkRP7bEqEH9ULvHxRL/y+/yFI\neqgR6zyoxiAFNGqG3oa/odipSP0/NIwi6q3zM8PObOEyCP0CQQG/AdIW1zWVzwJw\nwr63jUCg2ER9MdqRmpg/fup4G3fYX+Nxs+k3PntpIX0xUKAtiVjef62dVVFglYtE\nVBJ+Dn6vAkEBjTOZZYFm2zgpgW17KVQWdZ6ckZh/Wy2K7NY7BLSL17L88im7f4pt\nyIuhPdLjmtVbbRoGFgcI+XAL6AuP03RM5wJABsCiSdIKby7nXIi0lNU/aq6ZqkJ8\niMKLFjp2lEXl85DPQMJ0/W6mMppc58fOA6IVg5buKnhFeG4J4ohalyjk5QJBANHS\nfCn+3ZLYbDSO3QzL+sFPdG4FHOHRgR3zXWHy7hyX1L8oBIAvZCcYe6jpCor0QkO0\nB5sDRF5gLin6UZPmT+kCQQCMsvdWvYlBsdO3cOWtMe43Oyis2mn/m29A/leLnxr7\nhYNvlifTes/3PCd55jS7JgEcLI9/M2GuKp6mXtaJ42Oa\n-----END RSA PRIVATE KEY-----\n",
646        "RSA key ok\n-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQUSQLbMAAT6SNATRnHAeMfI3sOz\n4vJbwlZEZzOds4hT0GuF7qWy3jU7/0KsLka8l/rmrJYY2pU3pcj1U8HjV2JZkdYQ\njc14hfs6JUE/U+/K2UjLNc2bmunBxnYm0RPVfd5MW+p2u1u33pbADQc3LpaFptdc\n+dI5+hSNcJMbXz+wOQIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgQUSQLbMAAT6SNATRnHAeMfI3sOz4vJbwlZEZzOds4hT0GuF7qWy\n3jU7/0KsLka8l/rmrJYY2pU3pcj1U8HjV2JZkdYQjc14hfs6JUE/U+/K2UjLNc2b\nmunBxnYm0RPVfd5MW+p2u1u33pbADQc3LpaFptdc+dI5+hSNcJMbXz+wOQIDAQAB\nAoGBAQs6yuW+80dZiYsOKwgFVIpiYEI86so8fGlkHNnPRLaL5jYRY4Yn+yk4Z87t\nT54uYnTs/ZBsHd7wfycQcI6NRC5hgVY5sbQKDJDJIHgDPvxewvmE+mgbRFo7v4RH\nGGacGivrZVhXdDMpOm3KyxRfToWUJIq6IhT0AeURYrezGJABAkECdFjBnsFjaRnn\nNsmvJdYJpRuPVh0Zxr9pQ90e4auKSj8jIQC9QLiN7Ma6I1VItu95KhHJ3oI9Cnki\nxwlbbrpXAQJBAhDumzOrYXFuJ9JRvUZfSzWhojLi2gCQHClL8iNQzkkNCZ9kK1N1\nYS22O6HyA4ZJK/BNNLPCK865CdE0QbU7UTkCQDn6AouCbojBEht1CoskL6mjXFtm\nvf0fpjfTzEioSk9FehlOdyfkn3vMblpaQSZX/EcMcyLrw3QW70WMMHqMCQECQQFd\nmahBlZQ5efqeG+LDwbafQy9G/QPkfVvvu7/WsdE3HYPvszCj4CCUKy/tEV5dAr4k\n/ZLJAZ0c7NbdTPHlTMiZAkEB8LcBUXCz9eQiI7owMBxBpth8u3DjDLfTxn0lRz2x\n9svwPj+RJuPpeWgnmoZbLCtCZSTPxSpoPTHtMOuYS+QSug==\n-----END RSA PRIVATE KEY-----\n",
647        "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQqt8/nBJeXYkfMaxEjpk97+WA+A\nK0X51/IrpQIenEdXa1oeaAMbqdtObavk2Wodbz0mcmjP9AgAXxGO/K25mIjRwjRG\ncWayorhJoFqInAYKwNoMX66LVfMJumLnA3QvoDJvLRCwEQIUif9Jd3AZDYlf059S\nKTw579c6aYvaufEO2QIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICXwIBAAKBgQqt8/nBJeXYkfMaxEjpk97+WA+AK0X51/IrpQIenEdXa1oeaAMb\nqdtObavk2Wodbz0mcmjP9AgAXxGO/K25mIjRwjRGcWayorhJoFqInAYKwNoMX66L\nVfMJumLnA3QvoDJvLRCwEQIUif9Jd3AZDYlf059SKTw579c6aYvaufEO2QIDAQAB\nAoGBAlbrTLpwZ/LSvlQNzf9FgqNrfTHRyQmbshS3mEhGaiaPgPWKSawEwONkiTSg\nIGwEU3wZsjZkOmCCcyFE33X6IXWI95RoK+iRaCdtxybFwMvbhNMbvybQpDr0lXF/\nfVKKz+40FWH2/zyuBcV4+EcNloL5wNBy+fYGi1bViA9oK+LFAkEDsNOWL20XVJy/\nyhEpQ0jc8Ofjn4wrxoJPIWS2BtaHhg2uHmMjk8/t9RMigikGni9g5KzX5jOkNgY/\ngjhfSJk3BwJBAuTDLi9Rcmm3ByMJ8AwOMTZffOKLI2uCkS3yOavzlXLPDtYEsCmC\n5TVkxS1qBTl95cBSov3cFB73GJg2NGrrMx8CQQHoSxGdJRYfpnsAJWpb2bZF0rIy\n7LBbAVGAApqIYircPwmzrqzeYWGrfN4iwq0m53l99U4HLL07JnOACz5DONvVAkEA\n65CqGkATW0zqBxl87ciBm+Hny/8lR2YhFvRlpKn0h6sS87pP7xOCImWmUpfZi3ve\n2TcuP/6Bo4s+lgD+0FV1TwJBAS9/gTj5QEBi64WkKSRSCzj1u4hqAZb0i7jc6mD9\nkswCfxjngVijSlxdX4YKD2wEBxp9ATEsBlBi8etIt50cg8s=\n-----END RSA PRIVATE KEY-----\n",
648        "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgRKxf22tLs0Z/0bcE/eGDwng4M+2\nd7OKUlkjBc6vAiwWbbkNBKwp4z990S2fr2bggWu2Pq0mfMfUbBfDe+IUvKKiLXI6\nZOREB0Nrb8llcprvwlVPN2zV3OpoKTeApivznQApSFoWC7ueXcCXLSGlBPUuXuAo\nqkFjMvUQsunP9fcirwIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICXwIBAAKBgRKxf22tLs0Z/0bcE/eGDwng4M+2d7OKUlkjBc6vAiwWbbkNBKwp\n4z990S2fr2bggWu2Pq0mfMfUbBfDe+IUvKKiLXI6ZOREB0Nrb8llcprvwlVPN2zV\n3OpoKTeApivznQApSFoWC7ueXcCXLSGlBPUuXuAoqkFjMvUQsunP9fcirwIDAQAB\nAoGBApXso1YGGDaVWc7NMDqpz9r8HZ8GlZ33X/75KaqJaWG80ZDcaZftp/WWPnJN\nB7TcEfMGXlrpfZaDURIoC5CEuxTyoh69ToidQbnEEy7BlW/KuLsv7QV1iEk2Uixf\n99MyYZBIJOfK3uTguzctJFfPeOK9EoYij/g/EHMc5jyQz/P5AkEEps6Lc1jfppvc\n90JhcAWvtThfXzpYok73SiKowFy3zDjr1Mydmp14mmLND2Dwy5QdNCPJaS76T+Ot\n/ykMR0mjiwJBBATJqAM3H+20xb4588ALAJ5eCKY74eQANc2spQEcxwHPfuvLmfD/\n4Xz9Ckv3vv0t1TaslG23l/28Sr6PKTSbke0CQQOWHI92CqK9UVTHqv13Ils7rNAT\nmue1lI6jMR/M2G+5XHWvp2coS5st5VlXLxXY0ETH64Ohvl+t8sw3fA2EdSlLAkEC\nIZfgZnQhlqq8A/ov7rTnCxXLeH1hes0xu3XHvCNK1wb3xI0hgtHw/5wijc9Blnts\nC6bSwK0RChuFeDHsJF4ssQJBBAHEwMU9RdvbXp2W0P7PQnXfCXS8Sgc2tKdMMmkF\nPvtoas4kBuIsngWN20rlQGJ64v2wgmHo5+S8vJlNqvowXEU=\n-----END RSA PRIVATE KEY-----\n",
649        "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgTERefC8/JudPKMV0A7zDXvdOiz6\n6ZEb/ty5SLOkeC0HMrarRKpL8DdBpkTcAb7D5psBoDPmddis18SSXGsa7DEZBR39\niXYtIV1FR1/8tZ+QgUhiPzcXcVb2robdenxfQ9weH5CCVAWKKEpfBsACF5OofxrF\n/v99yu5pxeUaN4njcwIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICXgIBAAKBgTERefC8/JudPKMV0A7zDXvdOiz66ZEb/ty5SLOkeC0HMrarRKpL\n8DdBpkTcAb7D5psBoDPmddis18SSXGsa7DEZBR39iXYtIV1FR1/8tZ+QgUhiPzcX\ncVb2robdenxfQ9weH5CCVAWKKEpfBsACF5OofxrF/v99yu5pxeUaN4njcwIDAQAB\nAoGBDzqRUfoVnGZsj2ERtdIReUPr7lHhc7vwmaiXu8lr0u3M+4ykPwZag4vIgs6V\nbBN42triUblREfJy9PtH26X7cC0twt93fImTyuAcrKSNXKQIizI0XrhyB/9ewQv8\nkuI8dQugKhVIO+A1Ii2HPT7q9BC1DS9aYZ/PUzUQ68WM7b2BAkEHSSYsERzUcOwl\nZuazcy/AkylGmqGQcdO5wBkGUUxvHSa6oUvqsJcci35hGk95AJ1v6ndpKMolKFsN\n42Q9Gj+McQJBBrweUOlsAr9jbp7qi4mbvr92Ud533UdMPpvCO62BgrYZBMfZffvr\n+x4AEIh4tuZ+QVOR1nlCwrK/m0Q1+IsMsCMCQQO8fqfwqrFDq8bOi5cRhjajAXLk\nz+Asj6Ddo7e6r5D4CSmCmFUl9Ii9/LS9cm4iY5rGSjCSq3/8vx1TNM+lC1vxAkEC\nYqaqKcKjxn3FNGwGOBr9mHqjzJPPv+z1T92fnXh9f1mlI9OYl52hN6L2OB/pSAH3\nyU2iFRjcNMtAhwxGl5lK2QJAZJ1MF7buFyHnctA4mlWcPTzflVDUV8RrA3t0ZBsd\nUhZq+KITyDliBs37pEIvGNb2Hby10hTJcb9IKuuXanNwwg==\n-----END RSA PRIVATE KEY-----\n",
650        "-----BEGIN PUBLIC KEY-----\nMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgVvfDjDTId2lFH+IJAj6aRlUgN+P\ngNP26L9YGFBPNkJ8qbH1VAucZaj2l0z4RHokTZKAIBu0n8u+Y3jRlEzSJ+Iw+W49\nEPgZ3O8nbGSgCypLZwHn0B3l+r3jsemg34L0YxNZzSJmlkf7sXFyRhNO17SXz/+9\nxCtZxzqW7ZAWYhLf9wIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIICXwIBAAKBgVvfDjDTId2lFH+IJAj6aRlUgN+PgNP26L9YGFBPNkJ8qbH1VAuc\nZaj2l0z4RHokTZKAIBu0n8u+Y3jRlEzSJ+Iw+W49EPgZ3O8nbGSgCypLZwHn0B3l\n+r3jsemg34L0YxNZzSJmlkf7sXFyRhNO17SXz/+9xCtZxzqW7ZAWYhLf9wIDAQAB\nAoGBD30enlqqJf0T5KBmOuFE4NFfXNGLzbCd8sx+ZOPF6RWtYmRTBBYdCYxxW7er\ni9AdB+rz/tfH7QivKopi70SrFrMg4Ur3Kkj5av4mKgrkz2XmNekQeQzU7lzqdopL\nJjn35vZ3s/C7a+MrdXR9iQkDbwJk9Y1AHNuhMXFhV6dez2MxAkEKAu+ESNn62LvQ\n0ATIwqqXUe+XIcGw0DI2pUsN+UfLrtWiVe6ejiDUkeoXI/4JRwSpdi6Ir9Fuu1mU\nQSypZtxPnwJBCS02Ln7ToL/Z6f0ObAMBtt8pFZz1DMg7mwz01u6nGmHgArRuCuny\n3mLSW110UtSYuByaxvxYWT1MP7T11y37sKkCQQfHFBCvEDli2zZ0BON66FC6pOnC\nndkhRYFSlKZ8fRxt7SY6oDCptjOuUDA+FANdGvAUEj66aHggMI2OvIW2lX19AkEA\nrix1OAwCwBatBYkbMwHeiB8orhFxGCtrLIO+p8UV7KnKKYx7HKtYF6WXBo/IUGDe\nTaigFjeKrkPH+We8w3kEuQJBBZjRBZ462k9jIHUsCdgF/30fGuDQF67u6c76DX3X\n/3deRLV4Mi9kBdYhHaGVGWZqqH/cTNjIj2tuPWfpYdy7o9A=\n-----END RSA PRIVATE KEY-----\n",
651        "-----BEGIN PUBLIC KEY-----\nMIHfMA0GCSqGSIb3DQEBAQUAA4HNADCByQKBwQDPLNQeNMo6co6ly4r/ZMNtJ73v\nU2TjNv1o0xI8WhlqjChwE+hT1RVtWNFRlUUg+09texertoF3ZZCcV2EZZZ2QKxkG\n7YorEMFVwk0SRSjaue6uN5vqxm5KQReG3Lj9AGLrwDDeEhmgTCqMG33TEx5Na2yu\n4uMaXtQawVCbLvHuKrGDZL5WjKlBwl7MhP+dZDtewaquECog1z9Hm3gP1tqRB1IS\n2erAOgZ02JnrouQx9MRLYVtroiMr1LM7rtc9Yl0CAwEAAQ==\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIIDfgIBAAKBwQDPLNQeNMo6co6ly4r/ZMNtJ73vU2TjNv1o0xI8WhlqjChwE+hT\n1RVtWNFRlUUg+09texertoF3ZZCcV2EZZZ2QKxkG7YorEMFVwk0SRSjaue6uN5vq\nxm5KQReG3Lj9AGLrwDDeEhmgTCqMG33TEx5Na2yu4uMaXtQawVCbLvHuKrGDZL5W\njKlBwl7MhP+dZDtewaquECog1z9Hm3gP1tqRB1IS2erAOgZ02JnrouQx9MRLYVtr\noiMr1LM7rtc9Yl0CAwEAAQKBwQCBIn4tPdZ3zAQiT9caDiLKHSWE0cRm5FXcSwRo\n3fhNs4NZKO99oaozeFMwuQxX3I3LvhgpDh9w3rve15BMlkw6GsME0Hd5FH6OCAim\nRLmMbKzbpwnmszz3x870Xwxnlx7xZblxuoKHiq4tjuoOK2FETNi979bB1jGO0xrA\nd8Oap2AMKBju4OmNpRdzjTKaMVyFavjn7HKHZ2Pp2Y45K/X+hIv0Kx8xx7kkaix7\nyxQLIVKMPjoanViwHTxWls9mUQECYQD8jWwEvsTrmoGSynkAy+U24ui1Gd7PM7JF\nl5jGkJ308XbbfSMZD8criGWnGK+JXxvNkUUpgCdCO2BecKR89YOQqMPoj8jEjosy\n49ohDfvj6IHqVnS2o0jCHpP55V6mXv0CYQDSANReeIqs6mBqQB0EYPh91cECfhLc\nGg11huiTnZz3ibQPUawEQpYd59Icwh4FyDFVwfKqkZM4fP35VstI0VO6JwQG+bu6\nU31Jh9ni+ZQtehTL//6nT+zdqSjSPiWfXuECYQDbFoAveaLw1F81jWn9M+RLgfro\nKGIuk6VCU+mX0BsHQ3WdoOgStKpObIvqsjKNVDGVWkGKZ/8mqMXIB6XaNU4F7zHM\njPdY9GNzKVCwPiZXJvuU451qVyomJEqwjbdXUq0CYQCgoxfP598UI/h6be6EUfTi\ntKZ+VJfym08eToMLn63ZQBFnAm9VluWjnJeBfg9fFuJ+GeyZAuAdfqb7mqPHYK/u\nHjgbad5qycB1haBq2cS6AL91yK0vqJikeegK4pT+0qECYAsh8zXDUzQutEw6okRF\neAwtZVuUAXTK44x8ik5kk8C6n9MDdIJnsIO5p6bLYeQts2K4yYlttwZOAq1a5hWH\n2hW0ZJyQWUkJ/rN9vLZUvrcmjsgB5ai0qjkRvr2IVC8Fvg==\n-----END RSA PRIVATE KEY-----\n",
652        "-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArkXtVgHOxrjMBfgDk1xn\nTdvg11xMCf15UfxrDK7DE6jfOZcMUYv/ul7Wjz8NfyKkAp1BPxrgfk6+nkF3ziPn\n9UBLVp5O4b3PPB+wPvETgC1PhV65tRNLWnyAha3K5vovoUF+w3Y74XGwxit2Dt4j\nwSrZK5gIhMZB9aj6wmva1KAzgaIv4bdUiFCUyCUG1AGaU1ooav6ycbubpZLeGNz2\nAMKu6uVuAvfPefwUzzvcfNhP67v5UMqQMEsiGaeqBjrvosPBmA5WDNZK/neVhbYQ\ndle5V4V+/eYBCYirfeQX/IjY84TE5ucsP5Q+DDHAxKXMNvh52KOsnX1Zhg6q2muD\nuwIDAQAB\n-----END PUBLIC KEY-----\n-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEArkXtVgHOxrjMBfgDk1xnTdvg11xMCf15UfxrDK7DE6jfOZcM\nUYv/ul7Wjz8NfyKkAp1BPxrgfk6+nkF3ziPn9UBLVp5O4b3PPB+wPvETgC1PhV65\ntRNLWnyAha3K5vovoUF+w3Y74XGwxit2Dt4jwSrZK5gIhMZB9aj6wmva1KAzgaIv\n4bdUiFCUyCUG1AGaU1ooav6ycbubpZLeGNz2AMKu6uVuAvfPefwUzzvcfNhP67v5\nUMqQMEsiGaeqBjrvosPBmA5WDNZK/neVhbYQdle5V4V+/eYBCYirfeQX/IjY84TE\n5ucsP5Q+DDHAxKXMNvh52KOsnX1Zhg6q2muDuwIDAQABAoIBAFyN+sxwzVaxEnoh\nDBUZQCwTmMgH1sJ/gg1O17O2pRgt2dAGLp6okbpzX9RYElzxEtXompxfM9chDw+R\niYVLgIe6C8kG7rHpUsSFt97VvhuW9OLKOiq3ApAeC0vzzwz41o7379DzXD4RWWcF\n8f9XbvnKPehvKCcL/D/x7KuRCHlfcePoXxNqn5d8sTvh6/sn+8FRT63/A5FYxhQX\nMt8loVGw8ezKX5U98U/gvoSWCK6lJ4YEcBgdlIewIj0ueWehA7cLMzzPpVxtqp1J\nFEw1ruWhwGiIIPHEgj8tnyAq17lDs6I/Drx0MGJ9eWQNpn0RVRDluALBIuf5RjU1\ntCRJU+ECgYEA7PWuzR5VFf/6y9daKBbG6/SQGM37RjjhhdZqc5a2+AkPgBjH/ZXM\nNLhX3BfwzGUWuxNGq01YLK2te0EDNSOHtwM40IQEfJ2VObZJYgSz3W6kQkmSB77A\nH5ZCh/9jNsOYRlgzaEb1bkaGGIHBAjPSF2vxWl6W3ceAvIaKp30852kCgYEAvEbE\nZPxqxMp4Ow6wijyEG3cvfpsvKLq9WIroheGgxh5IWKD7JawpmZDzW+hRZMJZuhF1\nzdcZJwcTUYSZK2wpt0bdDSyr4UKDX30UjMFhUktKCZRtSLgoRz8c52tstohsNFwD\n4F9B1RtcOpCj8kBzx9dKT+JdnPIcdZYPP8OGMYMCgYEAxzVkVx0A+xXQij3plXpQ\nkV1xJulELaz0K8guhi5Wc/9qAI7U0uN0YX34nxehYLQ7f9qctra3QhhgmBX31Fyi\nY8FZqjLSctEn+vS8jKLXc3jorrGbCtfaPLPeCucxSYD2K21LCoddHfA8G645zNgz\n72zX4tlSi/CE0flp55Tp9sECgYAmWLN/bfnBAwvh22gRf6nYfjnqK2k7fm06L3CU\ndBPuxhQuGPuN/LasVF18hqCtSPhFcXDw77JrxIEmxT79HRaSAZjcKhEH3CgttqgM\n0wYjYLo/oT9w5DEv8abNa4/EzZxcPbF8bWpXIS9zrin2GTJ7rVmxU4WFhbpOKLYK\nYqReSQKBgG84Ums5JQhVNO8+QVqDbt6LhhWKLHy/7MsL2DQwT+xoO6jU9HnEM9Q0\nFuYyaWI86hAHdtha/0AdP/9hDuZUEc47E2PWOpcJ7t5CZHzqVhST1UVwqHnBhoLN\nl3ELliBewxEX1ztfNiI/rdboupDdfA7mHUThYyUeIMf2brMFEXy4\n-----END RSA PRIVATE KEY-----\n",
653        NULL,
654    };
655    struct key_pair {
656        SecKeyRef pubKey, privKey;
657    };
658    key_pair keys[11];
659
660    int i;
661    for(i = 0; i < sizeof(keys)/sizeof(key_pair); i++) {
662        NSAssert(pem_key_bytes[i] != NULL, @"Expected a key");
663        NSLog(@"Importing: %s", pem_key_bytes[i]);
664        CFDataRef pem_data = CFDataCreate(NULL, (UInt8*)(pem_key_bytes[i]), strlen(pem_key_bytes[i]));
665        SecKeyImportExportParameters import_params;
666        bzero(&import_params, sizeof(import_params));
667
668        import_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
669        import_params.keyUsage = CSSM_KEYUSE_ANY;
670        import_params.keyAttributes = CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE;
671        import_params.accessRef = NULL;
672        import_params.passphrase = CFSTR("");
673        import_params.alertPrompt = CFSTR("");
674
675        CFArrayRef keypair = NULL;
676        SecExternalFormat key_format = kSecFormatOpenSSL;
677        SecExternalItemType itemType = kSecItemTypeUnknown;
678        status = SecKeychainItemImport(pem_data, CFSTR(".pem"), &key_format, &itemType, 0, &import_params, tmp_keychain, &keypair);
679        STAssertTrue(status == 0, @"Expected pubkey import to be ok, got err=0x%x", status);
680        NSAssert(keypair != NULL, @"Expected to get some keys back");
681        STAssertNotNil((id)keypair, @"Expected to get some keys back");
682        STAssertTrue(CFArrayGetCount(keypair) == 2, @"Expected 2 keys, got %@", keypair);
683        keys[i].pubKey = (SecKeyRef)CFArrayGetValueAtIndex(keypair, 0);
684        keys[i].privKey = (SecKeyRef)CFArrayGetValueAtIndex(keypair, 1);
685    }
686    STAssertNil((id)pem_key_bytes[i], @"Expected to convert all pem keys, but found at least: %s", pem_key_bytes[i]);
687    CFDataRef encoding_parameters = CFDataCreate(NULL, NULL, 0);
688
689    struct KAT {
690        NSData *message, *seed, *encryptedMessage;
691        NSString *label;
692        key_pair keys;
693    };
694    KAT tests[] = {
695        // This first one is from the spec
696        {
697            .message = [NSData dataWithHexString:@"d436e99569fd32a7c8a05bbc90d32c49"],
698            .seed = [NSData dataWithHexString:@"aafd12f659cae63489b479e5076ddec2f06cb58f"],
699            .encryptedMessage = [NSData dataWithHexString:@"1253e04dc0a5397bb44a7ab87e9bf2a039a33d1e996fc82a94ccd30074c95df763722017069e5268da5d1c0b4f872cf653c11df82314a67968dfeae28def04bb6d84b1c31d654a1970e5783bd6eb96a024c2ca2f4a90fe9f2ef5c9c140e5bb48da9536ad8700c84fc9130adea74e558d51a74ddf85d8b50de96838d6063e0955"],
700            .keys = keys[0],
701            .label = @"From spec",
702        },
703        // The next 60 are from oaep-vect.txt
704        {
705            .message = [NSData dataWithHexString:@"6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34"],
706            .seed = [NSData dataWithHexString:@"18b776ea21069d69776a33e96bad48e1dda0a5ef"],
707            .encryptedMessage = [NSData dataWithHexString:@"354fe67b4a126d5d35fe36c777791a3f7ba13def484e2d3908aff722fad468fb21696de95d0be911c2d3174f8afcc201035f7b6d8e69402de5451618c21a535fa9d7bfc5b8dd9fc243f8cf927db31322d6e881eaa91a996170e657a05a266426d98c88003f8477c1227094a0d9fa1e8c4024309ce1ecccb5210035d47ac72e8a"],
708            .keys = keys[1],
709            .label = @"1-1",
710        },
711        {
712            .message = [NSData dataWithHexString:@"750c4047f547e8e41411856523298ac9bae245efaf1397fbe56f9dd5"],
713            .seed = [NSData dataWithHexString:@"0cc742ce4a9b7f32f951bcb251efd925fe4fe35f"],
714            .encryptedMessage = [NSData dataWithHexString:@"640db1acc58e0568fe5407e5f9b701dff8c3c91e716c536fc7fcec6cb5b71c1165988d4a279e1577d730fc7a29932e3f00c81515236d8d8e31017a7a09df4352d904cdeb79aa583adcc31ea698a4c05283daba9089be5491f67c1a4ee48dc74bbbe6643aef846679b4cb395a352d5ed115912df696ffe0702932946d71492b44"],
715            .keys = keys[1],
716            .label = @"1-2",
717        },
718        {
719            .message = [NSData dataWithHexString:@"d94ae0832e6445ce42331cb06d531a82b1db4baad30f746dc916df24d4e3c2451fff59a6423eb0e1d02d4fe646cf699dfd818c6e97b051"],
720            .seed = [NSData dataWithHexString:@"2514df4695755a67b288eaf4905c36eec66fd2fd"],
721            .encryptedMessage = [NSData dataWithHexString:@"423736ed035f6026af276c35c0b3741b365e5f76ca091b4e8c29e2f0befee603595aa8322d602d2e625e95eb81b2f1c9724e822eca76db8618cf09c5343503a4360835b5903bc637e3879fb05e0ef32685d5aec5067cd7cc96fe4b2670b6eac3066b1fcf5686b68589aafb7d629b02d8f8625ca3833624d4800fb081b1cf94eb"],
722            .keys = keys[1],
723            .label = @"1-3",
724        },
725        {
726            .message = [NSData dataWithHexString:@"52e650d98e7f2a048b4f86852153b97e01dd316f346a19f67a85"],
727            .seed = [NSData dataWithHexString:@"c4435a3e1a18a68b6820436290a37cefb85db3fb"],
728            .encryptedMessage = [NSData dataWithHexString:@"45ead4ca551e662c9800f1aca8283b0525e6abae30be4b4aba762fa40fd3d38e22abefc69794f6ebbbc05ddbb11216247d2f412fd0fba87c6e3acd888813646fd0e48e785204f9c3f73d6d8239562722dddd8771fec48b83a31ee6f592c4cfd4bc88174f3b13a112aae3b9f7b80e0fc6f7255ba880dc7d8021e22ad6a85f0755"],
729            .keys = keys[1],
730            .label = @"1-4",
731        },
732        {
733            .message = [NSData dataWithHexString:@"8da89fd9e5f974a29feffb462b49180f6cf9e802"],
734            .seed = [NSData dataWithHexString:@"b318c42df3be0f83fea823f5a7b47ed5e425a3b5"],
735            .encryptedMessage = [NSData dataWithHexString:@"36f6e34d94a8d34daacba33a2139d00ad85a9345a86051e73071620056b920e219005855a213a0f23897cdcd731b45257c777fe908202befdd0b58386b1244ea0cf539a05d5d10329da44e13030fd760dcd644cfef2094d1910d3f433e1c7c6dd18bc1f2df7f643d662fb9dd37ead9059190f4fa66ca39e869c4eb449cbdc439"],
736            .keys = keys[1],
737            .label = @"1-5",
738        },
739        {
740            .message = [NSData dataWithHexString:@"26521050844271"],
741            .seed = [NSData dataWithHexString:@"e4ec0982c2336f3a677f6a356174eb0ce887abc2"],
742            .encryptedMessage = [NSData dataWithHexString:@"42cee2617b1ecea4db3f4829386fbd61dafbf038e180d837c96366df24c097b4ab0fac6bdf590d821c9f10642e681ad05b8d78b378c0f46ce2fad63f74e0ad3df06b075d7eb5f5636f8d403b9059ca761b5c62bb52aa45002ea70baace08ded243b9d8cbd62a68ade265832b56564e43a6fa42ed199a099769742df1539e8255"],
743            .keys = keys[1],
744            .label = @"1-6",
745        },
746
747        {
748            .message = [NSData dataWithHexString:@"8ff00caa605c702830634d9a6c3d42c652b58cf1d92fec570beee7"],
749            .seed = [NSData dataWithHexString:@"8c407b5ec2899e5099c53e8ce793bf94e71b1782"],
750            .encryptedMessage = [NSData dataWithHexString:@"0181af8922b9fcb4d79d92ebe19815992fc0c1439d8bcd491398a0f4ad3a329a5bd9385560db532683c8b7da04e4b12aed6aacdf471c34c9cda891addcc2df3456653aa6382e9ae59b54455257eb099d562bbe10453f2b6d13c59c02e10f1f8abb5da0d0570932dacf2d0901db729d0fefcc054e70968ea540c81b04bcaefe720e"],
751            .keys = keys[2],
752            .label = @"2-1",
753        },
754        {
755            .message = [NSData dataWithHexString:@"2d"],
756            .seed = [NSData dataWithHexString:@"b600cf3c2e506d7f16778c910d3a8b003eee61d5"],
757            .encryptedMessage = [NSData dataWithHexString:@"018759ff1df63b2792410562314416a8aeaf2ac634b46f940ab82d64dbf165eee33011da749d4bab6e2fcd18129c9e49277d8453112b429a222a8471b070993998e758861c4d3f6d749d91c4290d332c7a4ab3f7ea35ff3a07d497c955ff0ffc95006b62c6d296810d9bfab024196c7934012c2df978ef299aba239940cba10245"],
758            .keys = keys[2],
759            .label = @"2-2",
760        },
761        {
762            .message = [NSData dataWithHexString:@"74fc88c51bc90f77af9d5e9a4a70133d4b4e0b34da3c37c7ef8e"],
763            .seed = [NSData dataWithHexString:@"a73768aeeaa91f9d8c1ed6f9d2b63467f07ccae3"],
764            .encryptedMessage = [NSData dataWithHexString:@"018802bab04c60325e81c4962311f2be7c2adce93041a00719c88f957575f2c79f1b7bc8ced115c706b311c08a2d986ca3b6a9336b147c29c6f229409ddec651bd1fdd5a0b7f610c9937fdb4a3a762364b8b3206b4ea485fd098d08f63d4aa8bb2697d027b750c32d7f74eaf5180d2e9b66b17cb2fa55523bc280da10d14be2053"],
765            .keys = keys[2],
766            .label = @"2-3",
767        },
768        {
769            .message = [NSData dataWithHexString:@"a7eb2a5036931d27d4e891326d99692ffadda9bf7efd3e34e622c4adc085f721dfe885072c78a203b151739be540fa8c153a10f00a"],
770            .seed = [NSData dataWithHexString:@"9a7b3b0e708bd96f8190ecab4fb9b2b3805a8156"],
771            .encryptedMessage = [NSData dataWithHexString:@"00a4578cbc176318a638fba7d01df15746af44d4f6cd96d7e7c495cbf425b09c649d32bf886da48fbaf989a2117187cafb1fb580317690e3ccd446920b7af82b31db5804d87d01514acbfa9156e782f867f6bed9449e0e9a2c09bcecc6aa087636965e34b3ec766f2fe2e43018a2fddeb140616a0e9d82e5331024ee0652fc7641"],
772            .keys = keys[2],
773            .label = @"2-4",
774        },
775        {
776            .message = [NSData dataWithHexString:@"2ef2b066f854c33f3bdcbb5994a435e73d6c6c"],
777            .seed = [NSData dataWithHexString:@"eb3cebbc4adc16bb48e88c8aec0e34af7f427fd3"],
778            .encryptedMessage = [NSData dataWithHexString:@"00ebc5f5fda77cfdad3c83641a9025e77d72d8a6fb33a810f5950f8d74c73e8d931e8634d86ab1246256ae07b6005b71b7f2fb98351218331ce69b8ffbdc9da08bbc9c704f876deb9df9fc2ec065cad87f9090b07acc17aa7f997b27aca48806e897f771d95141fe4526d8a5301b678627efab707fd40fbebd6e792a25613e7aec"],
779            .keys = keys[2],
780            .label = @"2-5",
781        },
782        {
783            .message = [NSData dataWithHexString:@"8a7fb344c8b6cb2cf2ef1f643f9a3218f6e19bba89c0"],
784            .seed = [NSData dataWithHexString:@"4c45cf4d57c98e3d6d2095adc51c489eb50dff84"],
785            .encryptedMessage = [NSData dataWithHexString:@"010839ec20c27b9052e55befb9b77e6fc26e9075d7a54378c646abdf51e445bd5715de81789f56f1803d9170764a9e93cb78798694023ee7393ce04bc5d8f8c5a52c171d43837e3aca62f609eb0aa5ffb0960ef04198dd754f57f7fbe6abf765cf118b4ca443b23b5aab266f952326ac4581100644325f8b721acd5d04ff14ef3a"],
786            .keys = keys[2],
787            .label = @"2-6",
788        },
789
790        {
791            .message = [NSData dataWithHexString:@"087820b569e8fa8d"],
792            .seed = [NSData dataWithHexString:@"8ced6b196290805790e909074015e6a20b0c4894"],
793            .encryptedMessage = [NSData dataWithHexString:@"026a0485d96aebd96b4382085099b962e6a2bdec3d90c8db625e14372de85e2d5b7baab65c8faf91bb5504fb495afce5c988b3f6a52e20e1d6cbd3566c5cd1f2b8318bb542cc0ea25c4aab9932afa20760eaddec784396a07ea0ef24d4e6f4d37e5052a7a31e146aa480a111bbe926401307e00f410033842b6d82fe5ce4dfae80"],
794            .keys = keys[3],
795            .label = @"3-1",
796        },
797        {
798            .message = [NSData dataWithHexString:@"4653acaf171960b01f52a7be63a3ab21dc368ec43b50d82ec3781e04"],
799            .seed = [NSData dataWithHexString:@"b4291d6567550848cc156967c809baab6ca507f0"],
800            .encryptedMessage = [NSData dataWithHexString:@"024db89c7802989be0783847863084941bf209d761987e38f97cb5f6f1bc88da72a50b73ebaf11c879c4f95df37b850b8f65d7622e25b1b889e80fe80baca2069d6e0e1d829953fc459069de98ea9798b451e557e99abf8fe3d9ccf9096ebbf3e5255d3b4e1c6d2ecadf067a359eea86405acd47d5e165517ccafd47d6dbee4bf5"],
801            .keys = keys[3],
802            .label = @"3-2",
803        },
804        {
805            .message = [NSData dataWithHexString:@"d94cd0e08fa404ed89"],
806            .seed = [NSData dataWithHexString:@"ce8928f6059558254008badd9794fadcd2fd1f65"],
807            .encryptedMessage = [NSData dataWithHexString:@"0239bce681032441528877d6d1c8bb28aa3bc97f1df584563618995797683844ca86664732f4bed7a0aab083aaabfb7238f582e30958c2024e44e57043b97950fd543da977c90cdde5337d618442f99e60d7783ab59ce6dd9d69c47ad1e962bec22d05895cff8d3f64ed5261d92b2678510393484990ba3f7f06818ae6ffce8a3a"],
808            .keys = keys[3],
809            .label = @"3-3",
810        },
811        {
812            .message = [NSData dataWithHexString:@"6cc641b6b61e6f963974dad23a9013284ef1"],
813            .seed = [NSData dataWithHexString:@"6e2979f52d6814a57d83b090054888f119a5b9a3"],
814            .encryptedMessage = [NSData dataWithHexString:@"02994c62afd76f498ba1fd2cf642857fca81f4373cb08f1cbaee6f025c3b512b42c3e8779113476648039dbe0493f9246292fac28950600e7c0f32edf9c81b9dec45c3bde0cc8d8847590169907b7dc5991ceb29bb0714d613d96df0f12ec5d8d3507c8ee7ae78dd83f216fa61de100363aca48a7e914ae9f42ddfbe943b09d9a0"],
815            .keys = keys[3],
816            .label = @"3-4",
817        },
818        {
819            .message = [NSData dataWithHexString:@"df5151832b61f4f25891fb4172f328d2eddf8371ffcfdbe997939295f30eca6918017cfda1153bf7a6af87593223"],
820            .seed = [NSData dataWithHexString:@"2d760bfe38c59de34cdc8b8c78a38e66284a2d27"],
821            .encryptedMessage = [NSData dataWithHexString:@"0162042ff6969592a6167031811a239834ce638abf54fec8b99478122afe2ee67f8c5b18b0339805bfdbc5a4e6720b37c59cfba942464c597ff532a119821545fd2e59b114e61daf71820529f5029cf524954327c34ec5e6f5ba7efcc4de943ab8ad4ed787b1454329f70db798a3a8f4d92f8274e2b2948ade627ce8ee33e43c60"],
822            .keys = keys[3],
823            .label = @"3-5",
824        },
825        {
826            .message = [NSData dataWithHexString:@"3c3bad893c544a6d520ab022319188c8d504b7a788b850903b85972eaa18552e1134a7ad6098826254ff7ab672b3d8eb3158fac6d4cbaef1"],
827            .seed = [NSData dataWithHexString:@"f174779c5fd3cfe007badcb7a36c9b55bfcfbf0e"],
828            .encryptedMessage = [NSData dataWithHexString:@"00112051e75d064943bc4478075e43482fd59cee0679de6893eec3a943daa490b9691c93dfc0464b6623b9f3dbd3e70083264f034b374f74164e1a00763725e574744ba0b9db83434f31df96f6e2a26f6d8eba348bd4686c2238ac07c37aac3785d1c7eea2f819fd91491798ed8e9cef5e43b781b0e0276e37c43ff9492d005730"],
829            .label = @"3-6",
830            .keys = keys[3],
831        },
832
833        {
834            .message = [NSData dataWithHexString:@"4a86609534ee434a6cbca3f7e962e76d455e3264c19f605f6e5ff6137c65c56d7fb344cd52bc93374f3d166c9f0c6f9c506bad19330972d2"],
835            .seed = [NSData dataWithHexString:@"1cac19ce993def55f98203f6852896c95ccca1f3"],
836            .encryptedMessage = [NSData dataWithHexString:@"04cce19614845e094152a3fe18e54e3330c44e5efbc64ae16886cb1869014cc5781b1f8f9e045384d0112a135ca0d12e9c88a8e4063416deaae3844f60d6e96fe155145f4525b9a34431ca3766180f70e15a5e5d8e8b1a516ff870609f13f896935ced188279a58ed13d07114277d75c6568607e0ab092fd803a223e4a8ee0b1a8"],
837            .keys = keys[4],
838            .label = @"4-1",
839        },
840        {
841            .message = [NSData dataWithHexString:@"b0adc4f3fe11da59ce992773d9059943c03046497ee9d9f9a06df1166db46d98f58d27ec074c02eee6cbe2449c8b9fc5080c5c3f4433092512ec46aa793743c8"],
842            .seed = [NSData dataWithHexString:@"f545d5897585e3db71aa0cb8da76c51d032ae963"],
843            .encryptedMessage = [NSData dataWithHexString:@"0097b698c6165645b303486fbf5a2a4479c0ee85889b541a6f0b858d6b6597b13b854eb4f839af03399a80d79bda6578c841f90d645715b280d37143992dd186c80b949b775cae97370e4ec97443136c6da484e970ffdb1323a20847821d3b18381de13bb49aaea66530c4a4b8271f3eae172cd366e07e6636f1019d2a28aed15e"],
844            .keys = keys[4],
845            .label = @"4-2",
846        },
847        {
848            .message = [NSData dataWithHexString:@"bf6d42e701707b1d0206b0c8b45a1c72641ff12889219a82bdea965b5e79a96b0d0163ed9d578ec9ada20f2fbcf1ea3c4089d83419ba81b0c60f3606da99"],
849            .seed = [NSData dataWithHexString:@"ad997feef730d6ea7be60d0dc52e72eacbfdd275"],
850            .encryptedMessage = [NSData dataWithHexString:@"0301f935e9c47abcb48acbbe09895d9f5971af14839da4ff95417ee453d1fd77319072bb7297e1b55d7561cd9d1bb24c1a9a37c619864308242804879d86ebd001dce5183975e1506989b70e5a83434154d5cbfd6a24787e60eb0c658d2ac193302d1192c6e622d4a12ad4b53923bca246df31c6395e37702c6a78ae081fb9d065"],
851            .keys = keys[4],
852            .label = @"4-3",
853        },
854        {
855            .message = [NSData dataWithHexString:@"fb2ef112f5e766eb94019297934794f7be2f6fc1c58e"],
856            .seed = [NSData dataWithHexString:@"136454df5730f73c807a7e40d8c1a312ac5b9dd3"],
857            .encryptedMessage = [NSData dataWithHexString:@"02d110ad30afb727beb691dd0cf17d0af1a1e7fa0cc040ec1a4ba26a42c59d0a796a2e22c8f357ccc98b6519aceb682e945e62cb734614a529407cd452bee3e44fece8423cc19e55548b8b994b849c7ecde4933e76037e1d0ce44275b08710c68e430130b929730ed77e09b015642c5593f04e4ffb9410798102a8e96ffdfe11e4"],
858            .keys = keys[4],
859            .label = @"4-4",
860        },
861        {
862            .message = [NSData dataWithHexString:@"28ccd447bb9e85166dabb9e5b7d1adadc4b9d39f204e96d5e440ce9ad928bc1c2284"],
863            .seed = [NSData dataWithHexString:@"bca8057f824b2ea257f2861407eef63d33208681"],
864            .encryptedMessage = [NSData dataWithHexString:@"00dbb8a7439d90efd919a377c54fae8fe11ec58c3b858362e23ad1b8a44310799066b99347aa525691d2adc58d9b06e34f288c170390c5f0e11c0aa3645959f18ee79e8f2be8d7ac5c23d061f18dd74b8c5f2a58fcb5eb0c54f99f01a83247568292536583340948d7a8c97c4acd1e98d1e29dc320e97a260532a8aa7a758a1ec2"],
865            .keys = keys[4],
866            .label = @"4-5",
867        },
868        {
869            .message = [NSData dataWithHexString:@"f22242751ec6b1"],
870            .seed = [NSData dataWithHexString:@"2e7e1e17f647b5ddd033e15472f90f6812f3ac4e"],
871            .encryptedMessage = [NSData dataWithHexString:@"00a5ffa4768c8bbecaee2db77e8f2eec99595933545520835e5ba7db9493d3e17cddefe6a5f567624471908db4e2d83a0fbee60608fc84049503b2234a07dc83b27b22847ad8920ff42f674ef79b76280b00233d2b51b8cb2703a9d42bfbc8250c96ec32c051e57f1b4ba528db89c37e4c54e27e6e64ac69635ae887d9541619a9"],
872            .keys = keys[4],
873            .label = @"4-6",
874        },
875
876        {
877            .message = [NSData dataWithHexString:@"af71a901e3a61d3132f0fc1fdb474f9ea6579257ffc24d164170145b3dbde8"],
878            .seed = [NSData dataWithHexString:@"44c92e283f77b9499c603d963660c87d2f939461"],
879            .encryptedMessage = [NSData dataWithHexString:@"036046a4a47d9ed3ba9a89139c105038eb7492b05a5d68bfd53accff4597f7a68651b47b4a4627d927e485eed7b4566420e8b409879e5d606eae251d22a5df799f7920bfc117b992572a53b1263146bcea03385cc5e853c9a101c8c3e1bda31a519807496c6cb5e5efb408823a352b8fa0661fb664efadd593deb99fff5ed000e5"],
880            .keys = keys[5],
881            .label = @"5-1",
882        },
883        {
884            .message = [NSData dataWithHexString:@"a3b844a08239a8ac41605af17a6cfda4d350136585903a417a79268760519a4b4ac3303ec73f0f87cfb32399"],
885            .seed = [NSData dataWithHexString:@"cb28f5860659fceee49c3eeafce625a70803bd32"],
886            .encryptedMessage = [NSData dataWithHexString:@"03d6eb654edce615bc59f455265ed4e5a18223cbb9be4e4069b473804d5de96f54dcaaa603d049c5d94aa1470dfcd2254066b7c7b61ff1f6f6770e3215c51399fd4e34ec5082bc48f089840ad04354ae66dc0f1bd18e461a33cc1258b443a2837a6df26759aa2302334986f87380c9cc9d53be9f99605d2c9a97da7b0915a4a7ad"],
887            .keys = keys[5],
888            .label = @"5-2",
889        },
890        {
891            .message = [NSData dataWithHexString:@"308b0ecbd2c76cb77fc6f70c5edd233fd2f20929d629f026953bb62a8f4a3a314bde195de85b5f816da2aab074d26cb6acddf323ae3b9c678ac3cf12fbdde7"],
892            .seed = [NSData dataWithHexString:@"2285f40d770482f9a9efa2c72cb3ac55716dc0ca"],
893            .encryptedMessage = [NSData dataWithHexString:@"0770952181649f9f9f07ff626ff3a22c35c462443d905d456a9fd0bff43cac2ca7a9f554e9478b9acc3ac838b02040ffd3e1847de2e4253929f9dd9ee4044325a9b05cabb808b2ee840d34e15d105a3f1f7b27695a1a07a2d73fe08ecaaa3c9c9d4d5a89ff890d54727d7ae40c0ec1a8dd86165d8ee2c6368141016a48b55b6967"],
894            .keys = keys[5],
895            .label = @"5-3",
896        },
897        {
898            .message = [NSData dataWithHexString:@"15c5b9ee1185"],
899            .seed = [NSData dataWithHexString:@"49fa45d3a78dd10dfd577399d1eb00af7eed5513"],
900            .encryptedMessage = [NSData dataWithHexString:@"0812b76768ebcb642d040258e5f4441a018521bd96687e6c5e899fcd6c17588ff59a82cc8ae03a4b45b31299af1788c329f7dcd285f8cf4ced82606b97612671a45bedca133442144d1617d114f802857f0f9d739751c57a3f9ee400912c61e2e6992be031a43dd48fa6ba14eef7c422b5edc4e7afa04fdd38f402d1c8bb719abf"],
901            .keys = keys[5],
902            .label = @"5-4",
903        },
904        {
905            .message = [NSData dataWithHexString:@"21026e6800c7fa728fcaaba0d196ae28d7a2ac4ffd8abce794f0985f60c8a6737277365d3fea11db8923a2029a"],
906            .seed = [NSData dataWithHexString:@"f0287413234cc5034724a094c4586b87aff133fc"],
907            .encryptedMessage = [NSData dataWithHexString:@"07b60e14ec954bfd29e60d0047e789f51d57186c63589903306793ced3f68241c743529aba6a6374f92e19e0163efa33697e196f7661dfaaa47aac6bde5e51deb507c72c589a2ca1693d96b1460381249b2cdb9eac44769f2489c5d3d2f99f0ee3c7ee5bf64a5ac79c42bd433f149be8cb59548361640595513c97af7bc2509723"],
908            .keys = keys[5],
909            .label = @"5-5",
910        },
911        {
912            .message = [NSData dataWithHexString:@"541e37b68b6c8872b84c02"],
913            .seed = [NSData dataWithHexString:@"d9fba45c96f21e6e26d29eb2cdcb6585be9cb341"],
914            .encryptedMessage = [NSData dataWithHexString:@"08c36d4dda33423b2ed6830d85f6411ba1dcf470a1fae0ebefee7c089f256cef74cb96ea69c38f60f39abee44129bcb4c92de7f797623b20074e3d9c2899701ed9071e1efa0bdd84d4c3e5130302d8f0240baba4b84a71cc032f2235a5ff0fae277c3e8f9112bef44c9ae20d175fc9a4058bfc930ba31b02e2e4f444483710f24a"],
915            .keys = keys[5],
916            .label = @"5-6",
917        },
918        {
919            .label = @"6-1",
920            .keys = keys[6],
921            .message = [NSData dataWithHexString:@"4046ca8baa3347ca27f49e0d81f9cc1d71be9ba517d4"],
922            .seed = [NSData dataWithHexString:@"dd0f6cfe415e88e5a469a51fbba6dfd40adb4384"],
923            .encryptedMessage = [NSData dataWithHexString:@"0630eebcd2856c24f798806e41f9e67345eda9ceda386acc9facaea1eeed06ace583709718d9d169fadf414d5c76f92996833ef305b75b1e4b95f662a20faedc3bae0c4827a8bf8a88edbd57ec203a27a841f02e43a615bab1a8cac0701de34debdef62a088089b55ec36ea7522fd3ec8d06b6a073e6df833153bc0aefd93bd1a3"],
924        },
925        {
926            .label = @"6-2",
927            .keys = keys[6],
928            .message = [NSData dataWithHexString:@"5cc72c60231df03b3d40f9b57931bc31109f972527f28b19e7480c7288cb3c92b22512214e4be6c914792ddabdf57faa8aa7"],
929            .seed = [NSData dataWithHexString:@"8d14bd946a1351148f5cae2ed9a0c653e85ebd85"],
930            .encryptedMessage = [NSData dataWithHexString:@"0ebc37376173a4fd2f89cc55c2ca62b26b11d51c3c7ce49e8845f74e7607317c436bc8d23b9667dfeb9d087234b47bc6837175ae5c0559f6b81d7d22416d3e50f4ac533d8f0812f2db9e791fe9c775ac8b6ad0f535ad9ceb23a4a02014c58ab3f8d3161499a260f39348e714ae2a1d3443208fd8b722ccfdfb393e98011f99e63f"],
931        },
932        {
933            .label = @"6-3",
934            .keys = keys[6],
935            .message = [NSData dataWithHexString:@"b20e651303092f4bccb43070c0f86d23049362ed96642fc5632c27db4a52e3d831f2ab068b23b149879c002f6bf3feee97591112562c"],
936            .seed = [NSData dataWithHexString:@"6c075bc45520f165c0bf5ea4c5df191bc9ef0e44"],
937            .encryptedMessage = [NSData dataWithHexString:@"0a98bf1093619394436cf68d8f38e2f158fde8ea54f3435f239b8d06b8321844202476aeed96009492480ce3a8d705498c4c8c68f01501dc81db608f60087350c8c3b0bd2e9ef6a81458b7c801b89f2e4fe99d4900ba6a4b5e5a96d865dc676c7755928794130d6280a8160a190f2df3ea7cf9aa0271d88e9e6905ecf1c5152d65"],
938        },
939        {
940            .label = @"6-4",
941            .keys = keys[6],
942            .message = [NSData dataWithHexString:@"684e3038c5c041f7"],
943            .seed = [NSData dataWithHexString:@"3bbc3bd6637dfe12846901029bf5b0c07103439c"],
944            .encryptedMessage = [NSData dataWithHexString:@"008e7a67cacfb5c4e24bec7dee149117f19598ce8c45808fef88c608ff9cd6e695263b9a3c0ad4b8ba4c95238e96a8422b8535629c8d5382374479ad13fa39974b242f9a759eeaf9c83ad5a8ca18940a0162ba755876df263f4bd50c6525c56090267c1f0e09ce0899a0cf359e88120abd9bf893445b3cae77d3607359ae9a52f8"],
945        },
946        {
947            .label = @"6-5",
948            .keys = keys[6],
949            .message = [NSData dataWithHexString:@"32488cb262d041d6e4dd35f987bf3ca696db1f06ac29a44693"],
950            .seed = [NSData dataWithHexString:@"b46b41893e8bef326f6759383a83071dae7fcabc"],
951            .encryptedMessage = [NSData dataWithHexString:@"00003474416c7b68bdf961c385737944d7f1f40cb395343c693cc0b4fe63b31fedf1eaeeac9ccc0678b31dc32e0977489514c4f09085f6298a9653f01aea4045ff582ee887be26ae575b73eef7f3774921e375a3d19adda0ca31aa1849887c1f42cac9677f7a2f4e923f6e5a868b38c084ef187594dc9f7f048fea2e02955384ab"],
952        },
953        {
954            .label = @"6-6",
955            .keys = keys[6],
956            .message = [NSData dataWithHexString:@"50ba14be8462720279c306ba"],
957            .seed = [NSData dataWithHexString:@"0a2403312a41e3d52f060fbc13a67de5cf7609a7"],
958            .encryptedMessage = [NSData dataWithHexString:@"0a026dda5fc8785f7bd9bf75327b63e85e2c0fdee5dadb65ebdcac9ae1de95c92c672ab433aa7a8e69ce6a6d8897fac4ac4a54de841ae5e5bbce7687879d79634cea7a30684065c714d52409b928256bbf53eabcd5231eb7259504537399bd29164b726d33a46da701360a4168a091ccab72d44a62fed246c0ffea5b1348ab5470"],
959        },
960        {
961            .label = @"7-1",
962            .keys = keys[7],
963            .message = [NSData dataWithHexString:@"47aae909"],
964            .seed = [NSData dataWithHexString:@"43dd09a07ff4cac71caa4632ee5e1c1daee4cd8f"],
965            .encryptedMessage = [NSData dataWithHexString:@"1688e4ce7794bba6cb7014169ecd559cede2a30b56a52b68d9fe18cf1973ef97b2a03153951c755f6294aa49adbdb55845ab6875fb3986c93ecf927962840d282f9e54ce8b690f7c0cb8bbd73440d9571d1b16cd9260f9eab4783cc482e5223dc60973871783ec27b0ae0fd47732cbc286a173fc92b00fb4ba6824647cd93c85c1"],
966        },
967        {
968            .label = @"7-2",
969            .keys = keys[7],
970            .message = [NSData dataWithHexString:@"1d9b2e2223d9bc13bfb9f162ce735db48ba7c68f6822a0a1a7b6ae165834e7"],
971            .seed = [NSData dataWithHexString:@"3a9c3cec7b84f9bd3adecbc673ec99d54b22bc9b"],
972            .encryptedMessage = [NSData dataWithHexString:@"1052ed397b2e01e1d0ee1c50bf24363f95e504f4a03434a08fd822574ed6b9736edbb5f390db10321479a8a139350e2bd4977c3778ef331f3e78ae118b268451f20a2f01d471f5d53c566937171b2dbc2d4bde459a5799f0372d6574239b2323d245d0bb81c286b63c89a361017337e4902f88a467f4c7f244bfd5ab46437ff3b6"],
973        },
974        {
975            .label = @"7-3",
976            .keys = keys[7],
977            .message = [NSData dataWithHexString:@"d976fc"],
978            .seed = [NSData dataWithHexString:@"76a75e5b6157a556cf8884bb2e45c293dd545cf5"],
979            .encryptedMessage = [NSData dataWithHexString:@"2155cd843ff24a4ee8badb7694260028a490813ba8b369a4cbf106ec148e5298707f5965be7d101c1049ea8584c24cd63455ad9c104d686282d3fb803a4c11c1c2e9b91c7178801d1b6640f003f5728df007b8a4ccc92bce05e41a27278d7c85018c52414313a5077789001d4f01910b72aad05d220aa14a58733a7489bc54556b"],
980        },
981        {
982            .label = @"7-4",
983            .keys = keys[7],
984            .message = [NSData dataWithHexString:@"d4738623df223aa43843df8467534c41d013e0c803c624e263666b239bde40a5f29aeb8de79e3daa61dd0370f49bd4b013834b98212aef6b1c5ee373b3cb"],
985            .seed = [NSData dataWithHexString:@"7866314a6ad6f2b250a35941db28f5864b585859"],
986            .encryptedMessage = [NSData dataWithHexString:@"0ab14c373aeb7d4328d0aaad8c094d88b9eb098b95f21054a29082522be7c27a312878b637917e3d819e6c3c568db5d843802b06d51d9e98a2be0bf40c031423b00edfbff8320efb9171bd2044653a4cb9c5122f6c65e83cda2ec3c126027a9c1a56ba874d0fea23f380b82cf240b8cf540004758c4c77d934157a74f3fc12bfac"],
987        },
988        {
989            .label = @"7-5",
990            .keys = keys[7],
991            .message = [NSData dataWithHexString:@"bb47231ca5ea1d3ad46c99345d9a8a61"],
992            .seed = [NSData dataWithHexString:@"b2166ed472d58db10cab2c6b000cccf10a7dc509"],
993            .encryptedMessage = [NSData dataWithHexString:@"028387a318277434798b4d97f460068df5298faba5041ba11761a1cb7316b24184114ec500257e2589ed3b607a1ebbe97a6cc2e02bf1b681f42312a33b7a77d8e7855c4a6de03e3c04643f786b91a264a0d6805e2cea91e68177eb7a64d9255e4f27e713b7ccec00dc200ebd21c2ea2bb890feae4942df941dc3f97890ed347478"],
994        },
995        {
996            .label = @"7-6",
997            .keys = keys[7],
998            .message = [NSData dataWithHexString:@"2184827095d35c3f86f600e8e59754013296"],
999            .seed = [NSData dataWithHexString:@"52673bde2ca166c2aa46131ac1dc808d67d7d3b1"],
1000            .encryptedMessage = [NSData dataWithHexString:@"14c678a94ad60525ef39e959b2f3ba5c097a94ff912b67dbace80535c187abd47d075420b1872152bba08f7fc31f313bbf9273c912fc4c0149a9b0cfb79807e346eb332069611bec0ff9bcd168f1f7c33e77313cea454b94e2549eecf002e2acf7f6f2d2845d4fe0aab2e5a92ddf68c480ae11247935d1f62574842216ae674115"],
1001        },
1002        {
1003            .label = @"8-1",
1004            .keys = keys[8],
1005            .message = [NSData dataWithHexString:@"050b755e5e6880f7b9e9d692a74c37aae449b31bfea6deff83747a897f6c2c825bb1adbf850a3c96994b5de5b33cbc7d4a17913a7967"],
1006            .seed = [NSData dataWithHexString:@"7706ffca1ecfb1ebee2a55e5c6e24cd2797a4125"],
1007            .encryptedMessage = [NSData dataWithHexString:@"09b3683d8a2eb0fb295b62ed1fb9290b714457b7825319f4647872af889b30409472020ad12912bf19b11d4819f49614824ffd84d09c0a17e7d17309d12919790410aa2995699f6a86dbe3242b5acc23af45691080d6b1ae810fb3e3057087f0970092ce00be9562ff4053b6262ce0caa93e13723d2e3a5ba075d45f0d61b54b61"],
1008        },
1009        {
1010            .label = @"8-2",
1011            .keys = keys[8],
1012            .message = [NSData dataWithHexString:@"4eb68dcd93ca9b19df111bd43608f557026fe4aa1d5cfac227a3eb5ab9548c18a06dded23f81825986b2fcd71109ecef7eff88873f075c2aa0c469f69c92bc"],
1013            .seed = [NSData dataWithHexString:@"a3717da143b4dcffbc742665a8fa950585548343"],
1014            .encryptedMessage = [NSData dataWithHexString:@"2ecf15c97c5a15b1476ae986b371b57a24284f4a162a8d0c8182e7905e792256f1812ba5f83f1f7a130e42dcc02232844edc14a31a68ee97ae564a383a3411656424c5f62ddb646093c367be1fcda426cf00a06d8acb7e57776fbbd855ac3df506fc16b1d7c3f2110f3d8068e91e186363831c8409680d8da9ecd8cf1fa20ee39d"],
1015        },
1016        {
1017            .label = @"8-3",
1018            .keys = keys[8],
1019            .message = [NSData dataWithHexString:@"8604ac56328c1ab5ad917861"],
1020            .seed = [NSData dataWithHexString:@"ee06209073cca026bb264e5185bf8c68b7739f86"],
1021            .encryptedMessage = [NSData dataWithHexString:@"4bc89130a5b2dabb7c2fcf90eb5d0eaf9e681b7146a38f3173a3d9cfec52ea9e0a41932e648a9d69344c50da763f51a03c95762131e8052254dcd2248cba40fd31667786ce05a2b7b531ac9dac9ed584a59b677c1a8aed8c5d15d68c05569e2be780bf7db638fd2bfd2a85ab276860f3777338fca989ffd743d13ee08e0ca9893f"],
1022        },
1023        {
1024            .label = @"8-4",
1025            .keys = keys[8],
1026            .message = [NSData dataWithHexString:@"fdda5fbf6ec361a9d9a4ac68af216a0686f438b1e0e5c36b955f74e107f39c0dddcc"],
1027            .seed = [NSData dataWithHexString:@"990ad573dc48a973235b6d82543618f2e955105d"],
1028            .encryptedMessage = [NSData dataWithHexString:@"2e456847d8fc36ff0147d6993594b9397227d577752c79d0f904fcb039d4d812fea605a7b574dd82ca786f93752348438ee9f5b5454985d5f0e1699e3e7ad175a32e15f03deb042ab9fe1dd9db1bb86f8c089ccb45e7ef0c5ee7ca9b7290ca6b15bed47039788a8a93ff83e0e8d6244c71006362deef69b6f416fb3c684383fbd0"],
1029        },
1030        {
1031            .label = @"8-5",
1032            .keys = keys[8],
1033            .message = [NSData dataWithHexString:@"4a5f4914bee25de3c69341de07"],
1034            .seed = [NSData dataWithHexString:@"ecc63b28f0756f22f52ac8e6ec1251a6ec304718"],
1035            .encryptedMessage = [NSData dataWithHexString:@"1fb9356fd5c4b1796db2ebf7d0d393cc810adf6145defc2fce714f79d93800d5e2ac211ea8bbecca4b654b94c3b18b30dd576ce34dc95436ef57a09415645923359a5d7b4171ef22c24670f1b229d3603e91f76671b7df97e7317c97734476d5f3d17d21cf82b5ba9f83df2e588d36984fd1b584468bd23b2e875f32f68953f7b2"],
1036        },
1037        {
1038            .label = @"8-6",
1039            .keys = keys[8],
1040            .message = [NSData dataWithHexString:@"8e07d66f7b880a72563abcd3f35092bc33409fb7f88f2472be"],
1041            .seed = [NSData dataWithHexString:@"3925c71b362d40a0a6de42145579ba1e7dd459fc"],
1042            .encryptedMessage = [NSData dataWithHexString:@"3afd9c6600147b21798d818c655a0f4c9212db26d0b0dfdc2a7594ccb3d22f5bf1d7c3e112cd73fc7d509c7a8bafdd3c274d1399009f9609ec4be6477e453f075aa33db382870c1c3409aef392d7386ae3a696b99a94b4da0589447e955d16c98b17602a59bd736279fcd8fb280c4462d590bfa9bf13fed570eafde97330a2c210"],
1043        },
1044        {
1045            .label = @"9-1",
1046            .keys = keys[9],
1047            .message = [NSData dataWithHexString:@"f735fd55ba92592c3b52b8f9c4f69aaa1cbef8fe88add095595412467f9cf4ec0b896c59eda16210e7549c8abb10cdbc21a12ec9b6b5b8fd2f10399eb6"],
1048            .seed = [NSData dataWithHexString:@"8ec965f134a3ec9931e92a1ca0dc8169d5ea705c"],
1049            .encryptedMessage = [NSData dataWithHexString:@"267bcd118acab1fc8ba81c85d73003cb8610fa55c1d97da8d48a7c7f06896a4db751aa284255b9d36ad65f37653d829f1b37f97b8001942545b2fc2c55a7376ca7a1be4b1760c8e05a33e5aa2526b8d98e317088e7834c755b2a59b12631a182c05d5d43ab1779264f8456f515ce57dfdf512d5493dab7b7338dc4b7d78db9c091ac3baf537a69fc7f549d979f0eff9a94fda4169bd4d1d19a69c99e33c3b55490d501b39b1edae118ff6793a153261584d3a5f39f6e682e3d17c8cd1261fa72"],
1050        },
1051        {
1052            .label = @"9-2",
1053            .keys = keys[9],
1054            .message = [NSData dataWithHexString:@"81b906605015a63aabe42ddf11e1978912f5404c7474b26dce3ed482bf961ecc818bf420c54659"],
1055            .seed = [NSData dataWithHexString:@"ecb1b8b25fa50cdab08e56042867f4af5826d16c"],
1056            .encryptedMessage = [NSData dataWithHexString:@"93ac9f0671ec29acbb444effc1a5741351d60fdb0e393fbf754acf0de49761a14841df7772e9bc82773966a1584c4d72baea00118f83f35cca6e537cbd4d811f5583b29783d8a6d94cd31be70d6f526c10ff09c6fa7ce069795a3fcd0511fd5fcb564bcc80ea9c78f38b80012539d8a4ddf6fe81e9cddb7f50dbbbbcc7e5d86097ccf4ec49189fb8bf318be6d5a0715d516b49af191258cd32dc833ce6eb4673c03a19bbace88cc54895f636cc0c1ec89096d11ce235a265ca1764232a689ae8"],
1057        },
1058        {
1059            .label = @"9-3",
1060            .keys = keys[9],
1061            .message = [NSData dataWithHexString:@"fd326429df9b890e09b54b18b8f34f1e24"],
1062            .seed = [NSData dataWithHexString:@"e89bb032c6ce622cbdb53bc9466014ea77f777c0"],
1063            .encryptedMessage = [NSData dataWithHexString:@"81ebdd95054b0c822ef9ad7693f5a87adfb4b4c4ce70df2df84ed49c04da58ba5fc20a19e1a6e8b7a3900b22796dc4e869ee6b42792d15a8eceb56c09c69914e813cea8f6931e4b8ed6f421af298d595c97f4789c7caa612c7ef360984c21b93edc5401068b5af4c78a8771b984d53b8ea8adf2f6a7d4a0ba76c75e1dd9f658f20ded4a46071d46d7791b56803d8fea7f0b0f8e41ae3f09383a6f9585fe7753eaaffd2bf94563108beecc207bbb535f5fcc705f0dde9f708c62f49a9c90371d3"],
1064        },
1065        {
1066            .label = @"9-4",
1067            .keys = keys[9],
1068            .message = [NSData dataWithHexString:@"f1459b5f0c92f01a0f723a2e5662484d8f8c0a20fc29dad6acd43bb5f3effdf4e1b63e07fdfe6628d0d74ca19bf2d69e4a0abf86d293925a796772f8088e"],
1069            .seed = [NSData dataWithHexString:@"606f3b99c0b9ccd771eaa29ea0e4c884f3189ccc"],
1070            .encryptedMessage = [NSData dataWithHexString:@"bcc35f94cde66cb1136625d625b94432a35b22f3d2fa11a613ff0fca5bd57f87b902ccdc1cd0aebcb0715ee869d1d1fe395f6793003f5eca465059c88660d446ff5f0818552022557e38c08a67ead991262254f10682975ec56397768537f4977af6d5f6aaceb7fb25dec5937230231fd8978af49119a29f29e424ab8272b47562792d5c94f774b8829d0b0d9f1a8c9eddf37574d5fa248eefa9c5271fc5ec2579c81bdd61b410fa61fe36e424221c113addb275664c801d34ca8c6351e4a858"],
1071        },
1072        {
1073            .label = @"9-5",
1074            .keys = keys[9],
1075            .message = [NSData dataWithHexString:@"53e6e8c729d6f9c319dd317e74b0db8e4ccca25f3c8305746e137ac63a63ef3739e7b595abb96e8d55e54f7bd41ab433378ffb911d"],
1076            .seed = [NSData dataWithHexString:@"fcbc421402e9ecabc6082afa40ba5f26522c840e"],
1077            .encryptedMessage = [NSData dataWithHexString:@"232afbc927fa08c2f6a27b87d4a5cb09c07dc26fae73d73a90558839f4fd66d281b87ec734bce237ba166698ed829106a7de6942cd6cdce78fed8d2e4d81428e66490d036264cef92af941d3e35055fe3981e14d29cbb9a4f67473063baec79a1179f5a17c9c1832f2838fd7d5e59bb9659d56dce8a019edef1bb3accc697cc6cc7a778f60a064c7f6f5d529c6210262e003de583e81e3167b89971fb8c0e15d44fffef89b53d8d64dd797d159b56d2b08ea5307ea12c241bd58d4ee278a1f2e"],
1078        },
1079        {
1080            .label = @"9-6",
1081            .keys = keys[9],
1082            .message = [NSData dataWithHexString:@"b6b28ea2198d0c1008bc64"],
1083            .seed = [NSData dataWithHexString:@"23aade0e1e08bb9b9a78d2302a52f9c21b2e1ba2"],
1084            .encryptedMessage = [NSData dataWithHexString:@"438cc7dc08a68da249e42505f8573ba60e2c2773d5b290f4cf9dff718e842081c383e67024a0f29594ea987b9d25e4b738f285970d195abb3a8c8054e3d79d6b9c9a8327ba596f1259e27126674766907d8d582ff3a8476154929adb1e6d1235b2ccb4ec8f663ba9cc670a92bebd853c8dbf69c6436d016f61add836e94732450434207f9fd4c43dec2a12a958efa01efe2669899b5e604c255c55fb7166de5589e369597bb09168c06dd5db177e06a1740eb2d5c82faeca6d92fcee9931ba9f"],
1085        },
1086        {
1087            .label = @"10-1",
1088            .keys = keys[10],
1089            .message = [NSData dataWithHexString:@"8bba6bf82a6c0f86d5f1756e97956870b08953b06b4eb205bc1694ee"],
1090            .seed = [NSData dataWithHexString:@"47e1ab7119fee56c95ee5eaad86f40d0aa63bd33"],
1091            .encryptedMessage = [NSData dataWithHexString:@"53ea5dc08cd260fb3b858567287fa91552c30b2febfba213f0ae87702d068d19bab07fe574523dfb42139d68c3c5afeee0bfe4cb7969cbf382b804d6e61396144e2d0e60741f8993c3014b58b9b1957a8babcd23af854f4c356fb1662aa72bfcc7e586559dc4280d160c126785a723ebeebeff71f11594440aaef87d10793a8774a239d4a04c87fe1467b9daf85208ec6c7255794a96cc29142f9a8bd418e3c1fd67344b0cd0829df3b2bec60253196293c6b34d3f75d32f213dd45c6273d505adf4cced1057cb758fc26aeefa441255ed4e64c199ee075e7f16646182fdb464739b68ab5daff0e63e9552016824f054bf4d3c8c90a97bb6b6553284eb429fcc"],
1092        },
1093        {
1094            .label = @"10-2",
1095            .keys = keys[10],
1096            .message = [NSData dataWithHexString:@"e6ad181f053b58a904f2457510373e57"],
1097            .seed = [NSData dataWithHexString:@"6d17f5b4c1ffac351d195bf7b09d09f09a4079cf"],
1098            .encryptedMessage = [NSData dataWithHexString:@"a2b1a430a9d657e2fa1c2bb5ed43ffb25c05a308fe9093c01031795f5874400110828ae58fb9b581ce9dddd3e549ae04a0985459bde6c626594e7b05dc4278b2a1465c1368408823c85e96dc66c3a30983c639664fc4569a37fe21e5a195b5776eed2df8d8d361af686e750229bbd663f161868a50615e0c337bec0ca35fec0bb19c36eb2e0bbcc0582fa1d93aacdb061063f59f2ce1ee43605e5d89eca183d2acdfe9f81011022ad3b43a3dd417dac94b4e11ea81b192966e966b182082e71964607b4f8002f36299844a11f2ae0faeac2eae70f8f4f98088acdcd0ac556e9fccc511521908fad26f04c64201450305778758b0538bf8b5bb144a828e629795"],
1099        },
1100        {
1101            .label = @"10-3",
1102            .keys = keys[10],
1103            .message = [NSData dataWithHexString:@"510a2cf60e866fa2340553c94ea39fbc256311e83e94454b4124"],
1104            .seed = [NSData dataWithHexString:@"385387514deccc7c740dd8cdf9daee49a1cbfd54"],
1105            .encryptedMessage = [NSData dataWithHexString:@"9886c3e6764a8b9a84e84148ebd8c3b1aa8050381a78f668714c16d9cfd2a6edc56979c535d9dee3b44b85c18be8928992371711472216d95dda98d2ee8347c9b14dffdff84aa48d25ac06f7d7e65398ac967b1ce90925f67dce049b7f812db0742997a74d44fe81dbe0e7a3feaf2e5c40af888d550ddbbe3bc20657a29543f8fc2913b9bd1a61b2ab2256ec409bbd7dc0d17717ea25c43f42ed27df8738bf4afc6766ff7aff0859555ee283920f4c8a63c4a7340cbafddc339ecdb4b0515002f96c932b5b79167af699c0ad3fccfdf0f44e85a70262bf2e18fe34b850589975e867ff969d48eabf212271546cdc05a69ecb526e52870c836f307bd798780ede"],
1106        },
1107        {
1108            .label = @"10-4",
1109            .keys = keys[10],
1110            .message = [NSData dataWithHexString:@"bcdd190da3b7d300df9a06e22caae2a75f10c91ff667b7c16bde8b53064a2649a94045c9"],
1111            .seed = [NSData dataWithHexString:@"5caca6a0f764161a9684f85d92b6e0ef37ca8b65"],
1112            .encryptedMessage = [NSData dataWithHexString:@"6318e9fb5c0d05e5307e1683436e903293ac4642358aaa223d7163013aba87e2dfda8e60c6860e29a1e92686163ea0b9175f329ca3b131a1edd3a77759a8b97bad6a4f8f4396f28cf6f39ca58112e48160d6e203daa5856f3aca5ffed577af499408e3dfd233e3e604dbe34a9c4c9082de65527cac6331d29dc80e0508a0fa7122e7f329f6cca5cfa34d4d1da417805457e008bec549e478ff9e12a763c477d15bbb78f5b69bd57830fc2c4ed686d79bc72a95d85f88134c6b0afe56a8ccfbc855828bb339bd17909cf1d70de3335ae07039093e606d655365de6550b872cd6de1d440ee031b61945f629ad8a353b0d40939e96a3c450d2a8d5eee9f678093c8"],
1113        },
1114        {
1115            .label = @"10-5",
1116            .keys = keys[10],
1117            .message = [NSData dataWithHexString:@"a7dd6c7dc24b46f9dd5f1e91ada4c3b3df947e877232a9"],
1118            .seed = [NSData dataWithHexString:@"95bca9e3859894b3dd869fa7ecd5bbc6401bf3e4"],
1119            .encryptedMessage = [NSData dataWithHexString:@"75290872ccfd4a4505660d651f56da6daa09ca1301d890632f6a992f3d565cee464afded40ed3b5be9356714ea5aa7655f4a1366c2f17c728f6f2c5a5d1f8e28429bc4e6f8f2cff8da8dc0e0a9808e45fd09ea2fa40cb2b6ce6ffff5c0e159d11b68d90a85f7b84e103b09e682666480c657505c0929259468a314786d74eab131573cf234bf57db7d9e66cc6748192e002dc0deea930585f0831fdcd9bc33d51f79ed2ffc16bcf4d59812fcebcaa3f9069b0e445686d644c25ccf63b456ee5fa6ffe96f19cdf751fed9eaf35957754dbf4bfea5216aa1844dc507cb2d080e722eba150308c2b5ff1193620f1766ecf4481bafb943bd292877f2136ca494aba0"],
1120        },
1121        {
1122            .label = @"10-6",
1123            .keys = keys[10],
1124            .message = [NSData dataWithHexString:@"eaf1a73a1b0c4609537de69cd9228bbcfb9a8ca8c6c3efaf056fe4a7f4634ed00b7c39ec6922d7b8ea2c04ebac"],
1125            .seed = [NSData dataWithHexString:@"9f47ddf42e97eea856a9bdbc714eb3ac22f6eb32"],
1126            .encryptedMessage = [NSData dataWithHexString:@"2d207a73432a8fb4c03051b3f73b28a61764098dfa34c47a20995f8115aa6816679b557e82dbee584908c6e69782d7deb34dbd65af063d57fca76a5fd069492fd6068d9984d209350565a62e5c77f23038c12cb10c6634709b547c46f6b4a709bd85ca122d74465ef97762c29763e06dbc7a9e738c78bfca0102dc5e79d65b973f28240caab2e161a78b57d262457ed8195d53e3c7ae9da021883c6db7c24afdd2322eac972ad3c354c5fcef1e146c3a0290fb67adf007066e00428d2cec18ce58f9328698defef4b2eb5ec76918fde1c198cbb38b7afc67626a9aefec4322bfd90d2563481c9a221f78c8272c82d1b62ab914e1c69f6af6ef30ca5260db4a46"],
1127        },
1128
1129    };
1130
1131	int max_cycles = 100;
1132	if (!getenv("SUBMISSION_TEST")) {
1133		max_cycles = 10;
1134		CFfprintf(stderr, "Running the far faster but far less reliable fast test.\nSet the SUBMISSION_TEST environment variable for full testing\n");
1135	}
1136
1137    for (int j = 0; j < max_cycles; j++) {
1138        NSLog(@"Cycle %d", j);
1139        for (i = 0; i < sizeof(tests)/sizeof(KAT); i++) {
1140            NSLog(@"test#%d %@ L(IN)=%lu, L(OUT)=%lu", i, tests[i].label, (unsigned long)[tests[i].message length], (unsigned long)[tests[i].encryptedMessage length]);
1141            CFErrorRef err = NULL;
1142
1143            SecTransformRef encryptor = SecEncryptTransformCreate(tests[i].keys.pubKey, &err);
1144
1145            SecTransformSetAttribute(encryptor, kSecTransformInputAttributeName, tests[i].message, &err);
1146            SecTransformSetAttribute(encryptor, kSecPaddingKey, kSecPaddingOAEPKey, &err);
1147            SecTransformSetAttribute(encryptor, kSecOAEPEncodingParametersAttributeName, encoding_parameters, &err);
1148            SecTransformSetAttribute(encryptor, CFSTR("FixedSeedForOAEPTesting"), tests[i].seed, &err);
1149
1150            CFTypeRef encryptedData = SecTransformExecute(encryptor, &err);
1151            STAssertNotNil((id)encryptedData, @"Expected to get encrypted data");
1152            STAssertNil((NSError*)err, @"Expected no error, got err=%@", err);
1153			// Can't support "seed" with commoncrypto, just check round trip.
1154            //STAssertEqualObjects((id)encryptedData, (id)tests[i].encryptedMessage, @"encrypted data should have matched test vector (%@) data", tests[i].label);
1155            CFRelease(encryptor);
1156
1157            SecTransformRef decryptor = SecDecryptTransformCreate(tests[i].keys.privKey, NULL);
1158            SecTransformSetAttribute(decryptor, kSecTransformInputAttributeName, tests[i].encryptedMessage, NULL);
1159			// XXX: totally round trip, not even partial KAT (KAT can't really be done on OAEP
1160			// without supporitng settign the seed externally)
1161            SecTransformSetAttribute(decryptor, kSecTransformInputAttributeName, encryptedData, NULL);
1162            SecTransformSetAttribute(decryptor, kSecPaddingKey, kSecPaddingOAEPKey, NULL);
1163            SecTransformSetAttribute(decryptor, kSecOAEPEncodingParametersAttributeName, encoding_parameters, NULL);
1164            CFTypeRef decryptedData = SecTransformExecute(decryptor, &err);
1165            STAssertNil((id)err, @"Expected no error, got: %@", err);
1166            STAssertNotNil((id)decryptedData, @"Expected to get decrypted data");
1167            STAssertEqualObjects((id)decryptedData, tests[i].message, @"Expected decrypted data to match original message (%@)", tests[i].label);
1168            CFRelease(decryptor);
1169        }
1170    }
1171
1172    return;
1173}
1174
1175-(void)testNoSignKeyMakesError
1176{
1177    NSData *data = [NSData dataWithBytes:"" length:1];
1178
1179    struct test_case {
1180        NSString *name;
1181        CFErrorRef createError;
1182        SecTransformRef transform;
1183    } test_cases[] = {
1184        {
1185            .name = @"Sign",
1186            .createError = NULL,
1187            .transform = SecSignTransformCreate(NULL, &(test_cases[0].createError))
1188        },
1189        {
1190            .name = @"Verify",
1191            .createError = NULL,
1192            .transform = SecVerifyTransformCreate(NULL, (CFDataRef)data, &(test_cases[1].createError))
1193        }
1194    };
1195
1196    for(int i = 0; i < sizeof(test_cases) / sizeof(test_case); i++) {
1197        struct test_case *test = test_cases + i;
1198        STAssertNil((id)test->createError, @"Testing %@, unexpected error: %@", test->name, test->createError);
1199        STAssertNotNil((id)test->transform, @"Didn't manage to create transform for %@", test->name);
1200        if (!test->transform) {
1201            continue;
1202        }
1203
1204        __block CFErrorRef err = NULL;
1205        SecTransformSetAttribute(test->transform, kSecTransformInputAttributeName, data, &err);
1206        STAssertNil((id)err, @"Error setting input for %@: %@", test->name, err);
1207
1208        dispatch_group_t execute_done = dispatch_group_create();
1209        dispatch_group_enter(execute_done);
1210
1211        SecTransformExecuteAsync(test->transform, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
1212            if (error) {
1213                err = error;
1214            }
1215            if (isFinal) {
1216                dispatch_group_leave(execute_done);
1217            }
1218        });
1219
1220        STAssertFalse(dispatch_group_wait(execute_done, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5)), @"Timeout waiting for %@ transform", test->name);
1221        STAssertErrorHas((id)err, @"missing required attributes?:.*KEY", @"Unexpected error during %@ test, expected one about missing keys: %@", test->name, err);
1222        dispatch_group_notify(execute_done, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^(void) {
1223            dispatch_release(execute_done);
1224        });
1225    }
1226}
1227
1228-(void)testHMAC
1229{
1230	// make the data for the key and the data to be HMAC'd
1231	CFDataRef hmacData = CFDataCreate(NULL, (u_int8_t*) gHMACText, strlen(gHMACText));
1232	CFDataRef hmacKey = CFDataCreate(NULL, (u_int8_t*) gHMACKey, strlen(gHMACKey));
1233	SecTransformRef hmacRef;
1234	CFErrorRef error = NULL;
1235	CFDataRef result;
1236	CFDataRef rightAnswer;
1237	CFComparisonResult ok;
1238
1239	// create the object
1240	hmacRef = SecDigestTransformCreate(kSecDigestHMACSHA1, 20, &error);
1241	STAssertNil((id) error, @"Unexpected error returned.");
1242
1243	// set the key
1244	SecTransformSetAttribute(hmacRef, kSecDigestHMACKeyAttribute, hmacKey, &error);
1245	STAssertNil((id) error, @"Unexpected error returned.");
1246
1247	// digest the data
1248	SecTransformSetAttribute(hmacRef, kSecTransformInputAttributeName, hmacData, &error);
1249	STAssertNil((id) error, @"Unexpected error returned.");
1250
1251	result = (CFDataRef) SecTransformExecute(hmacRef, &error);
1252	if (error)
1253	{
1254		CFShow(error);
1255		STAssertNil((id) error, @"Unexpected error returned.");
1256		CFRelease(error);
1257	}
1258
1259	STAssertNotNil((id) result, @"No data returned for SHA1");
1260
1261	// check to make sure we got the right answer
1262	rightAnswer = CFDataCreate(NULL, gSHA1HMAC, sizeof(gSHA1HMAC));
1263	ok = CFEqual(rightAnswer, result);
1264	CFRelease(rightAnswer);
1265	CFRelease(hmacRef);
1266	CFRelease(result);
1267
1268	if (error)
1269	{
1270		CFRelease(error);
1271	}
1272
1273	STAssertTrue(ok, @"Digest returned incorrect HMACSHA1 result.");
1274
1275	//+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+
1276
1277	// create the object
1278	hmacRef = SecDigestTransformCreate(kSecDigestHMACSHA2, 256, &error);
1279	STAssertNil((id) error, @"Unexpected error returned.");
1280
1281	// set the key
1282	SecTransformSetAttribute(hmacRef, kSecDigestHMACKeyAttribute, hmacKey, &error);
1283	STAssertNil((id) error, @"Unexpected error returned.");
1284
1285	// digest the data
1286	SecTransformSetAttribute(hmacRef, kSecTransformInputAttributeName, hmacData, &error);
1287	STAssertNil((id) error, @"Unexpected error returned.");
1288
1289	result = (CFDataRef) SecTransformExecute(hmacRef, &error);
1290	if (error != nil)
1291	{
1292		CFShow(error);
1293		STAssertNil((id) error, @"Unexpected error returned.");
1294		CFRelease(error);
1295	}
1296
1297	STAssertNotNil((id) result, @"No data returned for SHA256");
1298
1299	rightAnswer = CFDataCreate(NULL, gSHA256HMAC, sizeof(gSHA256HMAC));
1300	ok = CFEqual(result, rightAnswer);
1301
1302	CFRelease(rightAnswer);
1303	CFRelease(hmacRef);
1304
1305	CFRelease(hmacData);
1306	CFRelease(hmacKey);
1307	CFRelease(result);
1308
1309	STAssertTrue(ok, @"Digest returned incorrect HMACSHA256 result.");
1310}
1311
1312
1313
1314-(void)echoParams:(NSString*)p1 p2:(NSString*)p2
1315{
1316}
1317
1318-(void)testReadStreamTransform
1319{
1320	// point to our test data
1321	CFURLRef url = CFURLCreateWithFileSystemPath(NULL, CFSTR("/usr/share/dict/words"), kCFURLPOSIXPathStyle, false);
1322    FSRef force_resolve;
1323    STAssertTrue(CFURLGetFSRef(url, &force_resolve), @"Expected to create FSRef from %@", url);
1324    CFURLRef resolved_url = CFURLCreateFromFSRef(NULL, &force_resolve);
1325	CFNumberRef size_on_disk = NULL;
1326    CFURLCopyResourcePropertyForKey(resolved_url, kCFURLFileSizeKey, &size_on_disk, NULL);
1327    STAssertNotNil((id)size_on_disk, @"Expected to fetch size");
1328
1329	CFReadStreamRef readStreamRef = CFReadStreamCreateWithFile(NULL, url);
1330	SecTransformRef transform = SecTransformCreateReadTransformWithReadStream(readStreamRef);
1331	STAssertNotNil((id) transform, @"Returned transform should not be nil.");
1332
1333	dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
1334	dispatch_queue_t queue = dispatch_queue_create("ReadStream queue", NULL);
1335    __block ssize_t bytes_presented = 0;
1336
1337	SecTransformExecuteAsync(transform, queue,
1338	^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
1339	{
1340		if (message)
1341        {
1342            bytes_presented += CFDataGetLength((CFDataRef)message);
1343        }
1344        if (isFinal)
1345		{
1346			STAssertNil((id)error, @"Unexpected error!");
1347			dispatch_semaphore_signal(waitSemaphore);
1348		}
1349	});
1350
1351	dispatch_semaphore_wait(waitSemaphore, DISPATCH_TIME_FOREVER);
1352	NSNumber *size_via_stream = [NSNumber numberWithLongLong:bytes_presented];
1353    STAssertEqualObjects(size_via_stream, (NSNumber*)size_on_disk, @"Expected sizes to match");
1354    CFRelease(size_on_disk);
1355
1356	dispatch_release(queue);
1357	dispatch_release(waitSemaphore);
1358	CFRelease(transform);
1359	CFRelease(readStreamRef);
1360	CFRelease(url);
1361    CFRelease(resolved_url);
1362}
1363
1364-(void)testMGF
1365{
1366    UInt8 raw_seed[] = {0xaa, 0xfd, 0x12, 0xf6, 0x59, 0xca, 0xe6, 0x34, 0x89, 0xb4, 0x79, 0xe5, 0x07, 0x6d, 0xde, 0xc2, 0xf0, 0x6c, 0xb5, 0x8f};
1367    UInt8 raw_mgf107[] = {0x06, 0xe1, 0xde, 0xb2, 0x36, 0x9a, 0xa5, 0xa5, 0xc7, 0x07, 0xd8, 0x2c, 0x8e, 0x4e, 0x93, 0x24, 0x8a, 0xc7, 0x83, 0xde, 0xe0, 0xb2, 0xc0, 0x46, 0x26, 0xf5, 0xaf, 0xf9, 0x3e, 0xdc, 0xfb, 0x25, 0xc9, 0xc2, 0xb3, 0xff, 0x8a, 0xe1, 0x0e, 0x83, 0x9a, 0x2d, 0xdb, 0x4c, 0xdc, 0xfe, 0x4f, 0xf4, 0x77, 0x28, 0xb4, 0xa1, 0xb7, 0xc1, 0x36, 0x2b, 0xaa, 0xd2, 0x9a, 0xb4, 0x8d, 0x28, 0x69, 0xd5, 0x02, 0x41, 0x21, 0x43, 0x58, 0x11, 0x59, 0x1b, 0xe3, 0x92, 0xf9, 0x82, 0xfb, 0x3e, 0x87, 0xd0, 0x95, 0xae, 0xb4, 0x04, 0x48, 0xdb, 0x97, 0x2f, 0x3a, 0xc1, 0x4e, 0xaf, 0xf4, 0x9c, 0x8c, 0x3b, 0x7c, 0xfc, 0x95, 0x1a, 0x51, 0xec, 0xd1, 0xdd, 0xe6, 0x12, 0x64};
1368    CFDataRef seed = CFDataCreate(NULL, raw_seed, sizeof(raw_seed));
1369    CFDataRef mgf107 = CFDataCreate(NULL, raw_mgf107, sizeof(raw_mgf107));
1370    CFErrorRef err = NULL;
1371
1372    SecTransformRef mgfTransform = SecCreateMaskGenerationFunctionTransform(NULL, 107, &err);
1373    STAssertNotNil((id)mgfTransform, @"Expected to create a MGF transform e=%@", err);
1374    err = NULL;
1375    SecTransformSetAttribute(mgfTransform, kSecTransformInputAttributeName, seed, &err);
1376    STAssertNil((id)err, @"Expected no error setting MGF's input, got %@", err);
1377    err = NULL;
1378    CFDataRef mgfOutput = (CFDataRef)SecTransformExecute(mgfTransform, &err);
1379    STAssertNotNil((id)mgfOutput, @"Expected output from mgf, got error %@", err);
1380    STAssertEqualObjects((id)mgfOutput, (id)mgf107, @"Expected matching output");
1381
1382    CFRelease(mgfTransform);
1383    // XXX: leak test??
1384
1385    UInt8 raw_maskedDB[] = {0xdc, 0xd8, 0x7d, 0x5c, 0x68, 0xf1, 0xee, 0xa8, 0xf5, 0x52, 0x67, 0xc3, 0x1b, 0x2e, 0x8b, 0xb4, 0x25, 0x1f, 0x84, 0xd7, 0xe0, 0xb2, 0xc0, 0x46, 0x26, 0xf5, 0xaf, 0xf9, 0x3e, 0xdc, 0xfb, 0x25, 0xc9, 0xc2, 0xb3, 0xff, 0x8a, 0xe1, 0x0e, 0x83, 0x9a, 0x2d, 0xdb, 0x4c, 0xdc, 0xfe, 0x4f, 0xf4, 0x77, 0x28, 0xb4, 0xa1, 0xb7, 0xc1, 0x36, 0x2b, 0xaa, 0xd2, 0x9a, 0xb4, 0x8d, 0x28, 0x69, 0xd5, 0x02, 0x41, 0x21, 0x43, 0x58, 0x11, 0x59, 0x1b, 0xe3, 0x92, 0xf9, 0x82, 0xfb, 0x3e, 0x87, 0xd0, 0x95, 0xae, 0xb4, 0x04, 0x48, 0xdb, 0x97, 0x2f, 0x3a, 0xc1, 0x4f, 0x7b, 0xc2, 0x75, 0x19, 0x52, 0x81, 0xce, 0x32, 0xd2, 0xf1, 0xb7, 0x6d, 0x4d, 0x35, 0x3e, 0x2d};
1386
1387    UInt8 raw_mgf20[] = {0x41, 0x87, 0x0b, 0x5a, 0xb0, 0x29, 0xe6, 0x57, 0xd9, 0x57, 0x50, 0xb5, 0x4c, 0x28, 0x3c, 0x08, 0x72, 0x5d, 0xbe, 0xa9};
1388    CFDataRef maskedDB = CFDataCreate(NULL, raw_maskedDB, sizeof(raw_maskedDB));
1389    CFDataRef mgf20 = CFDataCreate(NULL, raw_mgf20, sizeof(raw_mgf20));
1390    err = NULL;
1391
1392    mgfTransform = SecCreateMaskGenerationFunctionTransform(kSecDigestSHA1, 20, &err);
1393    STAssertNotNil((id)mgfTransform, @"Expected to create a MGF transform e=%@", err);
1394    err = NULL;
1395    SecTransformSetAttribute(mgfTransform, kSecTransformInputAttributeName, maskedDB, &err);
1396    STAssertNil((id)err, @"Expected no error setting MGF's input, got %@", err);
1397    err = NULL;
1398    mgfOutput = (CFDataRef)SecTransformExecute(mgfTransform, &err);
1399    STAssertNotNil((id)mgfOutput, @"Expected output from mgf, got error %@", err);
1400    STAssertEqualObjects((id)mgfOutput, (id)mgf20, @"Expected matching output");
1401}
1402
1403-(void)testAbortParams
1404{
1405	// make a simple transform
1406	SecTransformRef a = SecNullTransformCreate();
1407
1408	// try to abort the transform
1409	CFErrorRef errorRef = NULL;
1410	STAssertFalse(SecTransformSetAttribute(a, CFSTR("ABORT"), NULL, &errorRef), @"SecTransformSetAttribute should have returned FALSE");
1411	STAssertNotNil((id) errorRef, @"SecTransformSetAttribute should have had an error.");
1412	if (errorRef != NULL)
1413	{
1414		CFRelease(errorRef);
1415	}
1416
1417	CFRelease(a);
1418
1419	// We have instant end of stream, it is wired directly to  null_abort's ABORT.   It is wired to the final drain via a delay and some other
1420	// things.   If the end of stream makes it to the final drain we get an empty CFData.   If the abort triggers then abort has invalidly
1421	// triggered off of a NULL value.
1422	SecGroupTransformRef test_null_abort_via_connection = SecTransformCreateGroupTransform();
1423	SecTransformRef pass_through = SecNullTransformCreate();
1424	SecTransformRef null_abort = SecNullTransformCreate();
1425
1426	CFURLRef dev_null_url = CFURLCreateWithFileSystemPath(NULL, CFSTR("/dev/null"), kCFURLPOSIXPathStyle, NO);
1427	CFReadStreamRef dev_null_stream = CFReadStreamCreateWithFile(NULL, dev_null_url);
1428	CFReadStreamOpen(dev_null_stream);
1429	CFRelease(dev_null_url);
1430
1431	SecTransformSetAttribute(pass_through, kSecTransformInputAttributeName, dev_null_stream, NULL);
1432	SecTransformConnectTransforms(pass_through, kSecTransformOutputAttributeName, null_abort, kSecTransformAbortAttributeName, test_null_abort_via_connection, NULL);
1433
1434	SecTransformRef delay_null = delay_transform(NSEC_PER_SEC / 10);
1435	SecTransformConnectTransforms(pass_through, kSecTransformOutputAttributeName, delay_null, kSecTransformInputAttributeName, test_null_abort_via_connection, NULL);
1436	SecTransformConnectTransforms(delay_null, kSecTransformOutputAttributeName, null_abort, kSecTransformInputAttributeName, test_null_abort_via_connection, NULL);
1437
1438
1439	CFErrorRef err = NULL;
1440	CFTypeRef not_null = SecTransformExecute(test_null_abort_via_connection, &err);
1441
1442	STAssertNotNil((id)not_null, @"aborted via a NULL from a connection?  err=%@", err);
1443
1444	if (err)
1445	{
1446		CFRelease(err);
1447	}
1448
1449	CFRelease(test_null_abort_via_connection);
1450	CFRelease(pass_through);
1451	CFRelease(null_abort);
1452	CFRelease(delay_null);
1453
1454	CFReadStreamClose(dev_null_stream);
1455	CFRelease(dev_null_stream);
1456}
1457
1458
1459-(void)testDisconnect
1460{
1461	SecTransformRef a = SecNullTransformCreate();
1462	SecTransformRef b = SecNullTransformCreate();
1463	SecTransformRef c = SecNullTransformCreate();
1464	SecGroupTransformRef g = SecTransformCreateGroupTransform();
1465
1466	SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName, g, NULL);
1467	SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, c, kSecTransformInputAttributeName, g, NULL);
1468
1469	SecTransformDisconnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName);
1470	STAssertTrue(SecGroupTransformHasMember(g, a), @"A should still be in the group, but isn't");
1471
1472	SecTransformDisconnectTransforms(a, kSecTransformOutputAttributeName, c, kSecTransformInputAttributeName);
1473	STAssertFalse(SecGroupTransformHasMember(g, a), @"A should no longer be in the group, but is");
1474
1475	CFRelease(g);
1476	CFRelease(c);
1477	CFRelease(b);
1478	CFRelease(a);
1479}
1480
1481
1482-(void)testAbort
1483{
1484	CFStringRef abort_test_name = CFSTR("com.apple.security.unit-test.abortTest");
1485
1486	SecTransformCreateBlock setupBlock =
1487	^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params)
1488	{
1489		params->send(kSecTransformInputAttributeName, kSecTransformMetaAttributeDeferred, kCFBooleanTrue);
1490
1491		params->overrideAttribute(kSecTransformActionAttributeNotification, CFSTR("PB"), ^(SecTransformAttributeRef ah, CFTypeRef value) {
1492			// Makes sure we can shut down (via ABORT) a transform that has a pending pushback
1493			params->pushback(ah, value);
1494			return (CFTypeRef)NULL;
1495		});
1496	};
1497
1498
1499	SecTransformRef a;
1500	SecTransformRef dt;
1501	SecGroupTransformRef group = SecTransformCreateGroupTransform();
1502
1503	// make two of these transforms and link them together
1504	a = custom_transform(abort_test_name, setupBlock);
1505	STAssertNotNil((id) a, @"SecCustomTransformCreate failed");
1506
1507	dt = delay_transform(NSEC_PER_SEC / 10);
1508	STAssertNotNil((id) dt, @"SecCustomTransformCreate failed");
1509
1510	// connect the two transforms
1511	CFErrorRef error;
1512
1513	// hook the output up so that the abort automatically fires.
1514	SecTransformConnectTransforms(dt, kSecTransformOutputAttributeName, a, CFSTR("ABORT"), group, &error);
1515	STAssertNil((id) error, @"SecTransformConnectTransforms failed.");
1516
1517	// also hook it up to the input because the input attribute is required on a null transform
1518	SecTransformConnectTransforms(dt, CFSTR("NOVALUES"), a, kSecTransformInputAttributeName, group, &error);
1519	STAssertNil((id) error, @"SecTransformConnectTransforms failed.");
1520
1521	// pass a plain piece of data down the transform just for fun...
1522	const u_int8_t data[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
1523
1524	CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL, data, sizeof(data), kCFAllocatorNull);
1525	SecTransformSetAttribute(dt, kSecTransformInputAttributeName, dataRef, NULL);
1526
1527	CFStringRef str = CFStringCreateMutable(NULL, 0);
1528	SecTransformSetAttribute(a, CFSTR("PB"), str, NULL);
1529
1530	CFTypeRef er = SecTransformExecute(a, &error);
1531
1532	STAssertNil((id)er, @"Didn't expect an result from aborted transform");
1533	STAssertNotNil((id)error, @"Expected error from execute");
1534
1535	if (error)
1536	{
1537		CFShow(error);
1538
1539		// while we are at it, make sure that the user dictionary has the originating transform
1540		CFDictionaryRef userDictionary = CFErrorCopyUserInfo(error);
1541		STAssertNotNil((id) CFDictionaryGetValue(userDictionary, kSecTransformAbortOriginatorKey), @"Originating transform not listed.");
1542		CFRelease(error);
1543	}
1544
1545	CFRelease(a);
1546	CFRelease(dt);
1547	CFRelease(group);
1548	CFRelease(dataRef);
1549
1550/*
1551	// XXX: these should both be 1, not 3 or 4.    WTF?   Is this an abort issue, or a generic leak?
1552	// STAssertEquals(rc0, CFGetRetainCount(str), @"The value we sent to PB hasn't been released (value retained by pushback)");
1553	// STAssertEquals(rc0, CFGetRetainCount(dataRef), @"The value we sent to INPUT hasn't been released");
1554*/
1555}
1556
1557-(void)testPreAbort {
1558	CFErrorRef error = NULL;
1559	SecTransformRef prebort = SecNullTransformCreate();
1560    SecTransformSetAttribute(prebort, kSecTransformInputAttributeName, CFSTR("quux"), NULL);
1561	SecTransformSetAttribute(prebort, CFSTR("ABORT"), CFSTR("OOPS"), NULL);
1562	CFTypeRef er = SecTransformExecute(prebort, &error);
1563	STAssertNil((id)er, @"Didn't expect an result from pre-aborted transform");
1564	STAssertNotNil((id)error, @"Expected error from execute of pre-aborted transform");
1565	CFRelease(error);
1566}
1567
1568#ifdef DEBUG
1569-(void)testFireAndForget
1570{
1571	bool isGC = false;
1572	NSGarbageCollector* gc = [NSGarbageCollector defaultCollector];
1573	if (gc)
1574	{
1575		isGC = [gc isEnabled];
1576	}
1577
1578	CFIndex retCount = 0;
1579
1580	// make transforms
1581	SecNullTransformRef a = SecNullTransformCreate();
1582	SecNullTransformRef b = SecNullTransformCreate();
1583	SecGroupTransformRef group = SecTransformCreateGroupTransform();
1584	SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName, group, NULL);
1585
1586	if (!isGC)
1587	{
1588		retCount = CFGetRetainCount(group);
1589	}
1590
1591	// set up a blob of data to fire
1592	const u_int8_t data[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
1593	CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL, data, sizeof(data), kCFAllocatorNull);
1594	SecTransformSetAttribute(a, kSecTransformInputAttributeName, dataRef, NULL);
1595	CFRelease(dataRef);
1596
1597	// make dispatch related stuff
1598	dispatch_queue_t queue = dispatch_queue_create("ffqueue", NULL);
1599	// semaphore0's job is to be signaled when we know ExecuteAsync is actually executing (so we don't sample the retain
1600	// count too soone), semaphore signals when we are about done with ExecuteAsync (I'm not sure why we need to know),
1601	// and semaphore2 is signaled to let the execute block know we are done sampling retain counts.
1602	dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
1603	dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
1604
1605	// launch the chain
1606	SecTransformExecuteAsync(group, queue,
1607						^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
1608						{
1609							CFfprintf(stderr, "message %p, final %d\n", message, isFinal ? 1 : 0);
1610							STAssertEquals(queue, const_cast<const dispatch_queue_t>(dispatch_get_current_queue()), @"Expected to be executing on own queue, got %s", dispatch_queue_get_label(dispatch_get_current_queue()));
1611							if (isFinal)
1612							{
1613								fprintf(stderr, "Final message received.\n");
1614								dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER); // make sure that the other chain has released its material
1615								dispatch_semaphore_signal(semaphore);
1616							}
1617						});
1618	CFRelease(a);
1619	CFRelease(b);
1620	CFRelease(group);
1621	dispatch_semaphore_signal(semaphore2);
1622	dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
1623
1624	// no crash?  Life is good.
1625}
1626#endif
1627
1628-(void)testExternalSource
1629{
1630	CFErrorRef err = NULL;
1631	SecTransformRef xs = SecExternalSourceTransformCreate(&err);
1632	SecTransformRef tee = SecNullTransformCreate();
1633	SecTransformRef group = SecTransformCreateGroupTransform();
1634
1635	SecTransformConnectTransforms(xs, kSecTransformOutputAttributeName, tee, kSecTransformInputAttributeName, group, &err);
1636
1637	dispatch_queue_t q = dispatch_queue_create("com.apple.security.unit-tests.test-external-source", 0);
1638	dispatch_group_t dg = dispatch_group_create();
1639	dispatch_group_enter(dg);
1640	__block bool got_ping = false;
1641
1642	SecTransformExecuteAsync(group, q, ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
1643		CFfprintf(stderr, "B: message %@, e %p, f %d\n", message ? message : (CFTypeRef)CFSTR("(NULL)"), error, isFinal);
1644
1645		if (message) {
1646			if (CFEqual(message, CFSTR("PING"))) {
1647				got_ping = true;
1648			} else {
1649				STFail(@"expected ping, got: %@", message);
1650			}
1651		}
1652
1653		if (error) {
1654			STFail(@"unexpected error: %@", error);
1655		}
1656
1657		if (isFinal) {
1658			if (!got_ping) {
1659				STFail(@"never got ping");
1660			}
1661			dispatch_group_leave(dg);
1662		}
1663	});
1664
1665	SecExternalSourceSetValue(tee, CFSTR("PONG"), &err);
1666	STAssertNotNil((id)err, @"Expected error setting tee");
1667	STAssertErrorHas((id)err, @"ExternalSource", @"Error should note what should be passed in: %@", err);
1668	CFRelease(err);
1669	err = NULL;
1670	SecExternalSourceSetValue(xs, CFSTR("PING"), &err);
1671	STAssertNil((id)err, @"unexpected error setting xs: %@", err);
1672	SecExternalSourceSetValue(xs, NULL, &err);
1673	STAssertNil((id)err, @"unexpected error setting xs: %@", err);
1674	dispatch_group_wait(dg, DISPATCH_TIME_FOREVER);
1675
1676	dispatch_release(dg);
1677	dispatch_release(q);
1678	CFRelease(xs);
1679	CFRelease(tee);
1680	CFRelease(group);
1681}
1682
1683-(void)testFindLastAndMonitor
1684{
1685	SecNullTransformRef a = delay_transform(NSEC_PER_SEC / 10);
1686	SecNullTransformRef b = SecNullTransformCreate();
1687
1688	SecGroupTransformRef groupRef = SecTransformCreateGroupTransform();
1689	CFErrorRef error = NULL;
1690	SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName, groupRef, &error);
1691	STAssertNil((id)error, @"An error was returned when none was expected.");
1692	SecTransformSetAttribute(a, kSecTransformInputAttributeName, kCFNull, &error);
1693	STAssertNil((id)error, @"An error was returned when none was expected.");
1694
1695
1696	// get the last transform in the chain (unexecuted).  It had better be b...
1697	SecTransformRef tr = SecGroupTransformFindLastTransform(groupRef);
1698	STAssertNotNil((id)tr, @"FindLastTransform returned NULL");
1699	STAssertTrue(tr == b, @"FindLastTransform returned incorrect result");
1700	STAssertFalse(tr == a, @"FindLastTransform returned the head of the chain");
1701
1702	// execute the transform. This should attach an output monitor
1703	dispatch_queue_t queue = dispatch_queue_create("test delivery queue", NULL);
1704	dispatch_semaphore_t last_block_run = dispatch_semaphore_create(0L);
1705	dispatch_semaphore_t last_assert_run = dispatch_semaphore_create(0L);
1706	SecTransformExecuteAsync(groupRef, queue,
1707		^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
1708		{
1709			if (isFinal)
1710			{
1711				dispatch_semaphore_signal(last_block_run);
1712				dispatch_semaphore_wait(last_assert_run, DISPATCH_TIME_FOREVER);
1713				dispatch_release(last_assert_run);
1714			}
1715
1716		});
1717
1718	dispatch_semaphore_wait(last_block_run, DISPATCH_TIME_FOREVER);
1719
1720	// see if the returned transform is the same now
1721	tr = SecGroupTransformFindLastTransform(groupRef);
1722	STAssertNotNil((id) tr, @"FindLastTransform returned NULL");
1723	STAssertTrue(tr == b, @"FindLastTransform returned incorrect result");
1724	STAssertFalse(tr == a, @"FindLastTransform returned the head of the chain");
1725
1726	// get the monitor, it had better not be a or b
1727	tr = SecGroupTransformFindMonitor(groupRef);
1728	STAssertNotNil((id) tr, @"FindMonitor returned NULL");
1729	STAssertFalse(tr == a, @"FindLastTransform returned the head of the chain");
1730	STAssertFalse(tr == b, @"FindLastTransform returned the head of the chain");
1731
1732	dispatch_semaphore_signal(last_assert_run);
1733	dispatch_release(queue);
1734	dispatch_release(last_block_run);
1735	CFRelease(a);
1736	CFRelease(b);
1737	CFRelease(groupRef);
1738}
1739
1740
1741-(void)testConnectUnsetAttributes /* <rdar://problem/7769955> Can't connect transform attributes with no setting */
1742{
1743	SecNullTransformRef a = SecNullTransformCreate();
1744	SecNullTransformRef b = SecNullTransformCreate();
1745
1746	SecGroupTransformRef group = SecTransformCreateGroupTransform();
1747	CFErrorRef error = NULL;
1748	SecTransformConnectTransforms(a, CFSTR("RANDOM_NAME"), b, CFSTR("RANDOM_DESTINATION"), group, &error);
1749	CFRelease(group);
1750	CFRelease(b);
1751	CFRelease(a);
1752	STAssertNil((id) error, @"An error was returned when none was expected.");
1753}
1754
1755-(void)testNoDataFlowPriorToInit /* <rdar://problem/8163542> Monitor must be attached before the data flow becomes active */
1756{
1757    CFStringRef name = CFSTR("com.apple.security.unit-test.flow-check");
1758	SecTransformCreateBlock cb = ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params) {
1759		__block bool inited = false;
1760        __block bool saw_x_start = false;
1761        __block bool saw_null = false;
1762		__block int post_send_left = 8;
1763		SecTransformAttributeRef out_ah = params->get(kSecTransformOutputAttributeName, kSecTransformMetaAttributeRef);
1764		params->send(out_ah, kSecTransformMetaAttributeValue, CFSTR("create"));
1765
1766		params->overrideTransform(kSecTransformActionStartingExecution, ^{
1767			params->send(out_ah, kSecTransformMetaAttributeValue, CFSTR("x-start"));
1768			inited = true;
1769			return (CFTypeRef)NULL;
1770		});
1771
1772		params->overrideTransform(kSecTransformActionCanExecute, ^{
1773			params->send(out_ah, kSecTransformMetaAttributeValue, CFSTR("can-x"));
1774			return (CFTypeRef)NULL;
1775		});
1776
1777		params->overrideAttribute(kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^(SecTransformAttributeRef ah, CFTypeRef value) {
1778			if (inited) {
1779				if (value) {
1780                    if (!saw_x_start) {
1781                        saw_x_start = CFStringHasPrefix((CFStringRef)value, CFSTR("x-start"));
1782                    }
1783                    if (saw_null) {
1784                        params->send(kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateGenericErrorRef(name, 88, "saw %@ after NULL", value));
1785                    }
1786					if (post_send_left--) {
1787						params->send(out_ah, kSecTransformMetaAttributeValue, CFSTR("post-init"));
1788					}
1789				} else {
1790                    saw_null = true;
1791                    // The FIRST flow transform should not see x-start (it is the OUTPUT of the flow transform
1792                    // before you in the chain), but all the other transforms should see it.
1793                    if (params->get(CFSTR("FIRST"), kSecTransformMetaAttributeValue)) {
1794                        if (saw_x_start) {
1795                            params->send(kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateGenericErrorRef(name, 42, "saw bogus x-start on FIRST flow transform"));
1796                        } else {
1797                            params->send(out_ah, kSecTransformMetaAttributeValue, value);
1798                        }
1799                    } else {
1800                        if (saw_x_start) {
1801                            params->send(out_ah, kSecTransformMetaAttributeValue, value);
1802                        } else {
1803                            params->send(kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateGenericErrorRef(name, 42, "never saw x-start before EOS"));
1804                            return (CFTypeRef)kCFNull;
1805                        }
1806                    }
1807				}
1808			} else {
1809				// attempting to put the value in the error string sometimes blows up, so I've left it out.
1810				params->send(kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateGenericErrorRef(name, 42, "got: value before init"));
1811                return (CFTypeRef)kCFNull;
1812			}
1813			return (CFTypeRef)value;
1814		});
1815	};
1816
1817	// Reliably reproduces with 100000 transforms in our group, but
1818	// not at 1000...doesn't even seem to do it at 50000.
1819	// Likely a timing issue triggered by heavy swapping.
1820	int n_transforms = 100000;
1821
1822	if (!getenv("SUBMISSION_TEST")) {
1823		n_transforms = 10000;
1824		CFfprintf(stderr, "Running the far faster but far less reliable fast test.\nSet the SUBMISSION_TEST environment variable for full testing\n");
1825	}
1826
1827	int i;
1828	CFMutableArrayRef ta = CFArrayCreateMutable(NULL, n_transforms, &kCFTypeArrayCallBacks);
1829	CFErrorRef err = NULL;
1830
1831	for(i = 0; i < n_transforms; ++i) {
1832		SecTransformRef tr = custom_transform(name, cb);
1833		STAssertNil((id)err, @"Failure %@ creating %@ transform", err, name);
1834		CFStringRef l = CFStringCreateWithFormat(NULL, NULL, CFSTR("flow-%d"), i);
1835		SecTransformSetAttribute(tr, kSecTransformTransformName, l, &err);
1836		if (0 == i % 1000) {
1837			CFfprintf(stderr, "Created %@ of %d\n", l, n_transforms);
1838		}
1839		CFRelease(l);
1840		STAssertNil((id)err, @"Can't set name %@", err);
1841		CFArrayAppendValue(ta, tr);
1842		assert(CFArrayGetCount(ta));
1843		assert(CFArrayGetCount(ta) == i+1);
1844	}
1845
1846	SecTransformRef prev_tr = NULL;
1847	SecTransformRef group = SecTransformCreateGroupTransform();
1848	CFIndex cnt;
1849
1850	while ((cnt = CFArrayGetCount(ta))) {
1851		CFIndex r = arc4random() % cnt;
1852		SecTransformRef tr = CFArrayGetValueAtIndex(ta, r);
1853		if (prev_tr) {
1854			SecTransformConnectTransforms(tr, kSecTransformOutputAttributeName, prev_tr, kSecTransformInputAttributeName, group, &err);
1855			STAssertNil((id)err, @"Can't connect %@ to %@", tr, prev_tr);
1856			STAssertNotNil((id)group, @"nil group after connect");
1857			CFRelease(prev_tr);
1858		}
1859		prev_tr = tr;
1860		CFArrayRemoveValueAtIndex(ta, r);
1861
1862		if (0 == cnt % 1000) {
1863			CFfprintf(stderr, "%d left to hook up\n", cnt);
1864		}
1865	}
1866
1867	CFTypeRef ptl = SecTransformGetAttribute(prev_tr, kSecTransformTransformName);
1868	CFfprintf(stderr, "Setting INPUT for %@\n", ptl);
1869	SecTransformSetAttribute(prev_tr, kSecTransformInputAttributeName, CFSTR("First!"), &err);
1870	STAssertNil((id)err, @"Can't set first's input?  %@", err);
1871	SecTransformSetAttribute(prev_tr, CFSTR("FIRST"), kCFBooleanTrue, &err);
1872	STAssertNil((id)err, @"Can't set FIRST?  %@", err);
1873	CFTypeRef r = SecTransformExecute(group, &err);
1874	STAssertNil((id)err, @"execution error: %@", err);
1875	STAssertNotNil((id)r, @"result expected from execute");
1876	CFRelease(group);
1877	CFRelease(prev_tr);
1878}
1879
1880-(void)testNoDataDescription /* <rdar://problem/7791122> CFShow(SecCustomTransformNoData()) crashes */
1881{
1882	CFStringRef result = CFCopyDescription(SecTransformNoData()); // this is called under the hood in CFShow, and it doesn't dump output
1883	STAssertNotNil((id)result, @"SecTransformNoData can be formatted");
1884	CFRelease(result);
1885}
1886
1887static SecTransformInstanceBlock KnownProblemPlumbing(CFStringRef name,
1888							SecTransformRef newTransform,
1889							SecTransformImplementationRef ref)
1890{
1891	SecTransformInstanceBlock instanceBlock =
1892	^{
1893		CFErrorRef result = NULL;
1894		// At the moment fully disconnecting a transform from a group leaves it in the group, so it can't have any
1895		// kSecTransformMetaAttributeRequiresOutboundConnection=TRUE attributes (by default OUTPUT requires an outbound connection)
1896		SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
1897			kSecTransformMetaAttributeRequiresOutboundConnection, kCFBooleanFalse);
1898
1899		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName,
1900			^(SecTransformAttributeRef ah, CFTypeRef value)
1901			{
1902				return (CFTypeRef)CFErrorCreate(NULL, kCFErrorDomainPOSIX, EOPNOTSUPP, NULL);
1903			});
1904		return result;
1905	};
1906
1907	return Block_copy(instanceBlock);
1908}
1909
1910-(void)knownProblemPlumbing // note, this test has been disconnected!
1911{
1912	SecNullTransformRef a = SecNullTransformCreate();
1913	CFStringRef name = CFSTR("com.apple.security.unit-test.error+outputless");
1914	CFErrorRef err = NULL;
1915
1916	SecTransformRegister(name, &KnownProblemPlumbing, &err);
1917
1918	SecTransformRef b = SecTransformCreate(name, NULL);
1919	SecGroupTransformRef group = SecTransformCreateGroupTransform();
1920
1921	SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName, group, NULL);
1922	SecTransformDisconnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName);
1923
1924
1925	CFStringRef data = CFSTR("Test");
1926
1927	SecTransformSetAttribute(a, kSecTransformInputAttributeName, data, NULL);
1928	CFTypeRef result = SecTransformExecute(a, &err);
1929
1930	STAssertEqualObjects((id)data, (id)result, @"Plumbing disconnect failed.");
1931	STAssertNil((id)err, @"Unexpected error=%@", err);
1932	CFRelease(a);
1933	CFRelease(b);
1934	CFRelease(group);
1935}
1936
1937-(void)testUnknownEncodeType {
1938	CFErrorRef err1 = NULL;
1939	CFStringRef invalid_encode_type = CFSTR("no such encoding type ☃✪");
1940	SecTransformRef not_created = SecEncodeTransformCreate(invalid_encode_type, &err1);
1941	STAssertNil((id)not_created, @"Created encode transform with bad type");
1942	STAssertErrorHas((NSError*)err1, @"☃✪", @"Error mentions bad encoding type: %@", err1);
1943	fprintf(stderr, "Err1 = %p\n", err1);
1944	if (err1) CFRelease(err1);
1945	err1 = NULL;
1946
1947	CFErrorRef err2 = NULL;
1948	SecTransformRef no_type_at_create = SecEncodeTransformCreate(NULL, &err2);
1949	fprintf(stderr, "Err2 = %p\n", err2);
1950	STAssertNotNil((id)no_type_at_create, @"should be able to create encoder with unset type, but got error %@", err2);
1951
1952	if (no_type_at_create) {
1953		CFErrorRef err3 = NULL;
1954        SecTransformSetAttribute(no_type_at_create, kSecTransformInputAttributeName, NULL, &err3);
1955        STAssertNil((id)err3, @"Can't set INPUT: %@", err3);
1956		if (err3) {
1957            CFRelease(err3);
1958            err3 = NULL;
1959        }
1960		STAssertTrue(SecTransformSetAttribute(no_type_at_create, kSecEncodeTypeAttribute, kSecBase32Encoding, &err3), @"Can't set encode to valid type error: %@", err3);
1961		STAssertFalse(SecTransformSetAttribute(no_type_at_create, kSecEncodeTypeAttribute, invalid_encode_type, &err3), @"Set encode to invalid type, no error signaled", err3);
1962		fprintf(stderr, "Err3 = %p\n", err3);
1963		if (err3) CFRelease(err3);
1964
1965		CFErrorRef err4 = NULL;
1966		CFTypeRef no_result = SecTransformExecute(no_type_at_create, &err4);
1967		STAssertNil((id)no_result, @"Got result when none expected %@");
1968		STAssertNotNil((id)err4, @"No error when one expected");
1969		fprintf(stderr, "Err4 = %p\n", err4);
1970		if (err4) CFRelease(err4);
1971	} else {
1972		STFail(@"Unable to run some tests");
1973	}
1974
1975	CFRelease(no_type_at_create);
1976	CFRelease(invalid_encode_type);
1977}
1978
1979-(void)testNoUnderscores
1980{
1981	SecTransformRef zt = SecEncodeTransformCreate(kSecZLibEncoding, NULL);
1982	CFErrorRef err = NULL;
1983	SecTransformSetAttribute(zt, CFSTR("_FAIL"), kCFBooleanTrue, &err);
1984	STAssertNotNil((id)err, @"Expeced an error setting _FAIL");
1985	STAssertErrorHas((id)err, @"_FAIL", @"Expected error to contain _FAIL");
1986	STAssertErrorHas((id)err, @"Encoder", @"Expecting error to name offending transform", err);
1987	CFTypeRef v = SecTransformGetAttribute(zt, CFSTR("_FAIL"));
1988	STAssertNil((id)v, @"Expected nil result, got v=%p", v);
1989	CFRelease(err);
1990	CFRelease(zt);
1991}
1992
1993-(void)testCanFetchDigestSizes
1994{
1995    NSDictionary *digests = [NSDictionary dictionaryWithObjectsAndKeys:
1996                             [NSNumber numberWithInt:128/8], kSecDigestMD2,
1997                             [NSNumber numberWithInt:128/8], kSecDigestMD4,
1998                             [NSNumber numberWithInt:128/8], kSecDigestMD5,
1999                             [NSNumber numberWithInt:160/8], kSecDigestSHA1,
2000                             [NSNumber numberWithInt:512/8], kSecDigestSHA2,
2001                             nil];
2002    NSData *zero = [NSData dataWithBytes:"" length:1];
2003
2004    for (NSString *digestType in digests) {
2005        CFErrorRef err = NULL;
2006        SecTransformRef digest = SecDigestTransformCreate(digestType, 0, &err);
2007        STAssertNotNil((id)digest, @"Expected to make digest (err=%@)", err);
2008        STAssertNil((id)err, @"Unexpected error: %@", err);
2009        NSNumber *actualLength = (NSNumber*)SecTransformGetAttribute(digest, kSecDigestLengthAttribute);
2010        STAssertTrue([actualLength intValue] != 0, @"Got zero length back");
2011        STAssertNotNil(actualLength, @"Expected to get a length");
2012        STAssertEqualObjects(actualLength, [digests objectForKey:digestType], @"Expected lengths to match for %@", digestType);
2013
2014        SecTransformSetAttribute(digest, kSecTransformInputAttributeName, zero, &err);
2015        STAssertNil((id)err, @"Unexpected error: %@", err);
2016
2017        NSData *output = (NSData *)SecTransformExecute(digest, &err);
2018        STAssertNil((id)err, @"Unexpected error: %@", err);
2019        STAssertNotNil((id)output, @"No output");
2020
2021        STAssertEquals([actualLength intValue], (int)[output length], @"Actual output not expected length");
2022
2023        [output release];
2024        CFRelease(digest);
2025    }
2026}
2027
2028-(void)testBadTransformTypeNames
2029{
2030    CFErrorRef error = NULL;
2031    Boolean ok = SecTransformRegister(CFSTR("Not valid: has a col..co...double dot thing"), DelayTransformBlock, &error);
2032    STAssertFalse(ok, @"Register of name with : fails");
2033    STAssertErrorHas((id)error, @":", @"Error mentions invalid character (error=%@)", error);
2034
2035    ok = SecTransformRegister(CFSTR("Not/valid has a slash"), DelayTransformBlock, &error);
2036    STAssertFalse(ok, @"Register of name with / fails");
2037    STAssertErrorHas((id)error, @"/", @"Error mentions invalid character (error=%@)", error);
2038
2039    ok = SecTransformRegister(CFSTR("https://NOT/VALID"), DelayTransformBlock, &error);
2040    STAssertFalse(ok, @"Register of name with : and / fails");
2041    STAssertErrorHas((id)error, @"[:/]", @"Error mentions invalid character (error=%@)", error);
2042
2043    ok = SecTransformRegister(CFSTR("_not valid at start"), DelayTransformBlock, &error);
2044    STAssertFalse(ok, @"Register of _name fails");
2045    STAssertErrorHas((id)error, @"_", @"Error mentions invalid character (error=%@)", error);
2046
2047    ok = SecTransformRegister(CFSTR("it is ok to have a _ after start"), DelayTransformBlock, &error);
2048    STAssertTrue(ok, @"Register of _ IN should have worked (error=%@)", error);
2049}
2050
2051-(void)testExecuteBlock {
2052	unsigned char *raw_data = (unsigned char *)"Just some bytes, you know";
2053	//NSData *empty = [NSData dataWithBytes:NULL length:0];
2054	NSData *data = [NSData dataWithBytes:raw_data length:strlen((const char *)raw_data)];
2055	//NSUInteger ecnt = [empty retainCount];
2056
2057	SecTransformRef zt = SecEncodeTransformCreate(kSecZLibEncoding, NULL);
2058	SecTransformSetAttribute(zt, kSecTransformInputAttributeName, data, NULL);
2059	dispatch_queue_t q = dispatch_queue_create("com.apple.security.testingQ", NULL);
2060	dispatch_queue_t q_sync = dispatch_queue_create("com.apple.security.testingQ_sync", NULL);
2061	dispatch_suspend((dispatch_object_t)q_sync);
2062	__block BOOL ran_block = NO;
2063
2064	SecTransformExecuteAsync(zt, q, ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
2065//		if ([empty length]) {
2066//			NSLog(@"Empty data not so empty");
2067//		}
2068		STAssertTrue(dispatch_get_current_queue() == q, @"block dispatched to proper queue");
2069
2070		if (!ran_block) {
2071			usleep(200);
2072			ran_block = YES;
2073		}
2074
2075		if (message == NULL) {
2076			dispatch_resume((dispatch_object_t)q_sync);
2077		}
2078	});
2079
2080	//STAssertTrue(ecnt < [empty retainCount], @"SecTransformExecute retained block");
2081	dispatch_sync(q_sync, ^{ });
2082	STAssertTrue(ran_block, @"Block executed");
2083
2084	dispatch_release(q_sync);
2085	dispatch_release(q);
2086	CFRelease(zt);
2087
2088	// test for 7735698
2089	// STAssertTrue(ecnt == [empty retainCount], @"SecTransformExecute released block");
2090}
2091
2092static SecTransformInstanceBlock ConnectionCheck(CFStringRef name,
2093							SecTransformRef newTransform,
2094							SecTransformImplementationRef ref)
2095{
2096	SecTransformInstanceBlock instanceBlock =
2097	^{
2098		CFErrorRef result = NULL;
2099		__block SecTransformMetaAttributeType dir = kSecTransformMetaAttributeValue;
2100
2101		SecTransformAttributeRef out_ah =
2102			SecTranformCustomGetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeRef);
2103
2104		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("DIRECTION"),
2105			^(SecTransformAttributeRef ah, CFTypeRef value)
2106			{
2107				if (CFEqual(value, CFSTR("<")))
2108				{
2109					dir = kSecTransformMetaAttributeHasInboundConnection;
2110				}
2111				else if (CFEqual(value, CFSTR(">")))
2112				{
2113					dir = kSecTransformMetaAttributeHasOutboundConnections;
2114				}
2115				else
2116				{
2117					return (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Unsupported direction %@, expected < or >", value);
2118				}
2119				return value;
2120			});
2121
2122		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"),
2123			^(SecTransformAttributeRef ah, CFTypeRef value)
2124			{
2125				if (dir != kSecTransformMetaAttributeValue)
2126				{
2127					if (value)
2128					{
2129						SecTransformCustomSetAttribute(ref, out_ah, kSecTransformMetaAttributeValue,
2130									SecTranformCustomGetAttribute(ref, value, dir));
2131					}
2132					else
2133					{
2134						SecTransformCustomSetAttribute(ref, out_ah, kSecTransformMetaAttributeValue, NULL);
2135					}
2136				}
2137				else
2138				{
2139					SecTransformPushbackAttribute(ref, ah, value);
2140				}
2141
2142				return value;
2143			});
2144
2145		return result;
2146	};
2147
2148	return Block_copy(instanceBlock);
2149}
2150
2151-(void)testConnectionChecks {
2152
2153	CFStringRef name = CFSTR("com.apple.security.unit-test.connection-checks");
2154	CFErrorRef error = NULL;
2155	SecTransformRegister(name, &*ConnectionCheck, &error);
2156
2157	struct test_case {
2158		NSString *attr, *dir;
2159		CFBooleanRef expect;
2160	} cases[] = {
2161		{@"INPUT", @"<", kCFBooleanTrue},
2162		{@"OUTPUT", @">", kCFBooleanTrue},
2163		{@"INPUT", @">", kCFBooleanFalse},
2164		{@"OUTPUT", @"<", kCFBooleanFalse},
2165		{@"DIRECTION", @"<", kCFBooleanFalse},
2166		{@"DIRECTION", @">", kCFBooleanFalse},
2167	};
2168
2169	CFIndex i, n = sizeof(cases)/sizeof(test_case);
2170	for(i = 0; i < n; ++i)
2171	{
2172		test_case *t = cases + i;
2173
2174		SecTransformRef cct = SecTransformCreate(name, NULL);
2175		SecTransformRef tee0 = SecNullTransformCreate();
2176		SecTransformRef tee1 = SecNullTransformCreate();
2177		SecTransformRef group = SecTransformCreateGroupTransform();
2178
2179		SecTransformSetAttribute(cct, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
2180		SecTransformSetAttribute(tee0, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
2181		SecTransformSetAttribute(tee1, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
2182
2183		SecTransformSetAttribute(tee0, CFSTR("NAME"), CFSTR("tee0"), NULL);
2184		SecTransformSetAttribute(tee1, CFSTR("NAME"), CFSTR("tee1"), NULL);
2185
2186		SecTransformConnectTransforms(cct, CFSTR("OUTPUT"), tee1, CFSTR("INPUT"), group, NULL);
2187		SecTransformConnectTransforms(tee0, CFSTR("OUTPUT"), cct, CFSTR("INPUT"), group, NULL);
2188
2189		SecTransformSetAttribute(cct, CFSTR("DIRECTION"), t->dir, NULL);
2190		SecTransformSetAttribute(tee0, CFSTR("INPUT"), t->attr, NULL);
2191		CFErrorRef err = NULL;
2192		CFTypeRef r = SecTransformExecute(group, &err);
2193		STAssertNil((id)err, @"Error=%@ for case#%d", err, i);
2194		STAssertNotNil((id)r, @"Nil result for case#%d", i);
2195		STAssertEqualObjects((id)(t->expect), (id)r, @"Expected result for case#%d %@%@", i, t->dir, t->attr);
2196
2197		CFRelease(cct);
2198		CFRelease(tee0);
2199		CFRelease(tee1);
2200		CFRelease(group);
2201	}
2202
2203}
2204
2205static SecTransformInstanceBlock PushBackTest(CFStringRef name,
2206							SecTransformRef newTransform,
2207							SecTransformImplementationRef ref)
2208{
2209	SecTransformInstanceBlock instanceBlock =
2210	^{
2211		CFErrorRef result = NULL;
2212		__block CFStringRef input_d = NULL;
2213		__block CFStringRef data_d = NULL;
2214
2215		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("DATA"),
2216			^(SecTransformAttributeRef ah, CFTypeRef value)
2217			{
2218				if (!input_d)
2219				{
2220					SecTransformPushbackAttribute(ref, ah, value);
2221				}
2222				else
2223				{
2224					if (data_d)
2225					{
2226						CFRelease(data_d);
2227					}
2228					data_d = (CFStringRef)CFRetain(value);
2229				}
2230				return value;
2231			});
2232
2233		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"),
2234			^(SecTransformAttributeRef ah, CFTypeRef value)
2235			{
2236				if (value)
2237				{
2238					if (input_d)
2239					{
2240						CFRelease(input_d);
2241					}
2242					input_d = (CFStringRef)CFRetain(value);
2243					if (!data_d)
2244					{
2245						SecTransformPushbackAttribute(ref, ah, value);
2246						return value;
2247					}
2248				}
2249				else
2250				{
2251					if (data_d)
2252					{
2253						SecTransformPushbackAttribute(ref, ah, NULL);
2254						value = data_d;
2255						data_d = NULL;
2256					}
2257				}
2258				SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, value);
2259				return value;
2260			});
2261
2262		return result;
2263	};
2264
2265	return Block_copy(instanceBlock);
2266}
2267
2268-(void)testPushback {
2269
2270	CFStringRef name = CFSTR("com.apple.security.unit-test.basic-pushback");
2271	CFStringRef one = CFSTR("1");
2272	CFStringRef two = CFSTR("2");
2273	CFStringRef expect = CFSTR("12");
2274
2275	// This unit test makes pushback look very complex, but that is because we are abusing it for test purposes.
2276	// normally it is a simple "if I need X before I can go on, and X isn't here yet pushback".   Here we attempt
2277	// to carefully sequence 2 attributes to be the inverse of the normal order AND test pushback of NULL as well
2278	// as normal values.
2279
2280	CFErrorRef error = NULL;
2281	SecTransformRegister(name, &PushBackTest, &error);
2282
2283	SecTransformRef pt = SecTransformCreate(name, NULL);
2284
2285	SecTransformSetAttribute(pt, CFSTR("DATA"), two, NULL);
2286	SecTransformSetAttribute(pt, CFSTR("INPUT"), one, NULL);
2287
2288	CFTypeRef result = SecTransformExecute(pt, NULL);
2289
2290	STAssertEqualObjects((id)result, (id)expect, @"Testing pushback");
2291
2292	CFRelease(pt);
2293
2294	// NOTE: we want to test doing a double pushback, but that sets the Abort attribute which currently causes an abort not an orderly shutdown
2295
2296}
2297
2298static SecTransformInstanceBlock CustomExternalization(CFStringRef name,
2299							SecTransformRef newTransform,
2300							SecTransformImplementationRef ref)
2301{
2302	SecTransformInstanceBlock instanceBlock =
2303	^{
2304		CFErrorRef result = NULL;
2305		// Create a non-attribute 'instance' variable which will contain
2306		// the version number of this class
2307
2308		__block float versionNumber = 1.0;
2309
2310		// Register the custom externalize override
2311		SecTransformActionBlock ExternalizeExtraDataOverride =
2312		^{
2313			CFStringRef key = CFSTR("VersionNumber");
2314			CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &versionNumber);
2315
2316			CFDictionaryRef result = CFDictionaryCreate(kCFAllocatorDefault,
2317				(const void **)&key, (const void **)&value,
2318				1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
2319
2320			CFRelease(value);
2321
2322
2323			return (CFTypeRef)result;
2324
2325		};
2326		SecTransformSetTransformAction(ref, kSecTransformActionExternalizeExtraData, ExternalizeExtraDataOverride);
2327
2328		// Register the custom internalize override
2329		SecTransformDataBlock InternalizeExtraDataOverride =
2330			^(CFTypeRef d)
2331		{
2332			CFTypeRef internalizeResult = NULL;
2333			//id testObj = (id)d;
2334
2335			//STAssertNotNil(testObj, @"Internalize did NOT get a dictionary!");
2336
2337			if (CFDictionaryGetTypeID() == CFGetTypeID(d))
2338			{
2339				CFStringRef key = CFSTR("VersionNumber");
2340				CFDictionaryRef dict = (CFDictionaryRef)d;
2341				CFNumberRef varsNum = (CFNumberRef)CFDictionaryGetValue(dict, key);
2342			//	STAssertNotNil((NSNumber *)varsNum,
2343			//		@"Unable to retrieve the dictionary when the internalized data override");
2344				if (NULL != varsNum)
2345				{
2346					Boolean numResult = CFNumberGetValue(varsNum, kCFNumberFloatType, &versionNumber);
2347			//		STAssertTrue(numResult, @"Could not get the version number from the CFNumberRef");
2348					if (numResult)
2349					{
2350						//float knownVersion = 1.0;
2351			//			STAssertTrue(knownVersion == versionNumber, @"Versions do not Match!");
2352					}
2353				}
2354			}
2355			return internalizeResult;
2356		};
2357		SecTransformSetDataAction(ref, kSecTransformActionInternalizeExtraData,
2358			InternalizeExtraDataOverride);
2359
2360		return result;
2361	};
2362
2363	return Block_copy(instanceBlock);
2364}
2365
2366/* --------------------------------------------------------------------------
2367	method:			testCustomExternalization
2368	description:	Test the ability to write out custom external data
2369   -------------------------------------------------------------------------- */
2370- (void)testCustomExternalization
2371{
2372	NSString* ctName = @"com.apple.security.unit-test-customExternalization";
2373	NSError* error = nil;
2374
2375	CFStringRef aName = (CFStringRef)ctName;
2376	CFErrorRef* anError = (CFErrorRef *)&error;
2377
2378	Boolean registerResult = SecTransformRegister(aName, &CustomExternalization, anError);
2379	STAssertTrue(registerResult, @"Unable to register the custom externalization transform");
2380
2381	SecTransformRef externalTrans = SecTransformCreate((CFStringRef)ctName,
2382		(CFErrorRef *)&error);
2383
2384	STAssertNotNil((id)externalTrans, @"Could not create the custom externalization transform");
2385
2386	CFDictionaryRef externalData = SecTransformCopyExternalRepresentation(externalTrans);
2387	STAssertNotNil((NSDictionary *)externalData, @"Did not get a dictionary from SecTransformCopyExternalRepresentation");
2388
2389	CFRelease(externalTrans);
2390
2391	externalTrans = NULL;
2392	externalTrans = SecTransformCreateFromExternalRepresentation(externalData, (CFErrorRef *)&error);
2393	STAssertNotNil((id)externalTrans, @"Could not create the custom external representation");
2394	CFRelease(externalData);
2395	if (NULL != externalTrans)
2396	{
2397		CFRelease(externalTrans);
2398	}
2399}
2400
2401static SecTransformInstanceBlock TestString(CFStringRef name,
2402							SecTransformRef newTransform,
2403							SecTransformImplementationRef ref)
2404{
2405	SecTransformInstanceBlock instanceBlock =
2406	^{
2407		CFErrorRef result = NULL;
2408		SecTransformSetDataAction(ref, kSecTransformActionProcessData,
2409			^(CFTypeRef value)
2410			{
2411				CFDataRef d = (CFDataRef)value;
2412				if (d) {
2413					return (CFTypeRef)CFStringCreateWithBytes(NULL, CFDataGetBytePtr(d), CFDataGetLength(d), kCFStringEncodingMacRoman, FALSE);
2414				} else {
2415					return (CFTypeRef)d;
2416				}
2417			});
2418		return result;
2419	};
2420
2421	return Block_copy(instanceBlock);
2422}
2423
2424-(void)testStringResults {
2425	CFStringRef name = CFSTR("com.apple.security.unit-test.string-converter");
2426	CFErrorRef error = NULL;
2427	SecTransformRegister(name, &TestString, &error);
2428
2429	SecTransformRef sr = SecTransformCreate(name, NULL);
2430
2431	unsigned char *msg = (unsigned char *)"This is a test message, it isn't large, but it will get broken into parts by the encode/decode transforms...";
2432	CFDataRef data = CFDataCreate(NULL, msg, strlen((const char *)msg));
2433	NSString *ns_msg = [NSString stringWithCString:(const char *)msg encoding:NSMacOSRomanStringEncoding];
2434
2435	SecTransformRef er = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
2436	SecTransformRef dr = SecDecodeTransformCreate(kSecBase32Encoding, NULL);
2437
2438	SecTransformSetAttribute(er, kSecTransformInputAttributeName, data, NULL);
2439
2440	SecGroupTransformRef group = SecTransformCreateGroupTransform();
2441	SecTransformConnectTransforms(er, kSecTransformOutputAttributeName, dr, kSecTransformInputAttributeName, group, NULL);
2442	SecTransformConnectTransforms(dr, kSecTransformOutputAttributeName, sr, kSecTransformInputAttributeName, group, NULL);
2443
2444
2445	CFStringRef result = (CFStringRef)SecTransformExecute(sr, NULL);
2446	STAssertEqualObjects(ns_msg, (NSString *)result, @"string results");
2447
2448	CFRelease(result);
2449	CFRelease(group);
2450	CFRelease(dr);
2451	CFRelease(er);
2452	CFRelease(sr);
2453	if (error)
2454	{
2455		CFRelease(error);
2456	}
2457}
2458
2459static CFNumberRef MakeNumber1(long n)
2460{
2461	return CFNumberCreate(NULL, kCFNumberLongType, &n);
2462}
2463
2464
2465
2466static SecTransformInstanceBlock TestRegisterCreate(CFStringRef name,
2467							SecTransformRef newTransform,
2468							SecTransformImplementationRef ref)
2469{
2470
2471
2472	SecTransformInstanceBlock instanceBlock =
2473	^{
2474		__block long count = 0;
2475
2476		__block CFNumberRef countNum = MakeNumber1(count);;
2477		SecTransformCustomSetAttribute(ref, CFSTR("Count"), kSecTransformMetaAttributeValue, countNum);
2478		CFRelease(countNum);
2479		fprintf(stderr, "countNum = %p\n", countNum);
2480
2481		CFErrorRef result = NULL;
2482		SecTransformSetDataAction(ref, kSecTransformActionProcessData,
2483			^(CFTypeRef value)
2484			{
2485				CFDataRef d = (CFDataRef)value;
2486				if (d)
2487				{
2488					count += CFDataGetLength(d);
2489
2490					CFNumberRef countNum2 = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &count);
2491					SecTransformCustomSetAttribute(ref, CFSTR("Count"), kSecTransformMetaAttributeValue, countNum2);
2492					CFRelease(countNum2);
2493					fprintf(stderr, "countNum = %p\n", countNum);
2494
2495				} else
2496				{
2497					SecTransformCustomSetAttribute(ref, CFSTR("Count"), kSecTransformMetaAttributeValue, NULL);
2498				}
2499				return value;
2500			});
2501
2502
2503		return result;
2504	};
2505
2506	return Block_copy(instanceBlock);
2507}
2508
2509- (void)testRegisterCreate
2510{
2511	CFStringRef name = CFSTR("com.apple.security.unit-test.novel-unique-at-least-unusual-name");
2512	long count = 0;
2513	CFNumberRef countNum = NULL;
2514	CFErrorRef error = NULL;
2515	Boolean ok = SecTransformRegister(name, &TestRegisterCreate, &error);
2516	STAssertTrue(ok, @"Successful register");
2517
2518	SecTransformRef tr = SecTransformCreate(name, NULL);
2519	STAssertNotNil((NSObject *)tr, @"newly created custom transform");
2520	SecTransformSetAttribute(tr, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
2521
2522	char *data_bytes = (char *)"It was the best of transforms, it was the worst of transforms.";
2523	CFDataRef data = CFDataCreate(NULL, (const UInt8 *)data_bytes, strlen(data_bytes));
2524
2525	SecTransformSetAttribute(tr, kSecTransformInputAttributeName, data, NULL);
2526
2527	SecTransformRef nt = SecNullTransformCreate();
2528	SecTransformRef tg = SecTransformCreateGroupTransform();
2529	SecTransformConnectTransforms(tr, CFSTR("OUTPUT"), nt, CFSTR("DISCARD"), tg, &error);
2530	STAssertNil((id)error, @"Connected tr's output to nt's discard: %@", error);
2531	SecTransformConnectTransforms(tr, CFSTR("Count"), nt, CFSTR("INPUT"), tg, &error);
2532	STAssertNil((id)error, @"Connected tr's count to nt's input: %@", error);
2533
2534	SecTransformSetAttribute(nt, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
2535
2536	usleep(100);
2537	countNum = (CFNumberRef)SecTransformGetAttribute(tr, CFSTR("Count"));
2538	CFNumberGetValue(countNum, kCFNumberLongType, &count);
2539	CFRelease(countNum);
2540	STAssertTrue(count == 0, @"length unchanged before execute");
2541
2542	countNum = (CFNumberRef)SecTransformExecute(tg, NULL);
2543	STAssertNotNil((id)countNum, @"Got result from execute");
2544	STAssertEquals(CFGetTypeID(countNum), CFNumberGetTypeID(), @"expected a number from execute");
2545	CFNumberGetValue(countNum, kCFNumberLongType, &count);
2546	CFRelease(countNum);
2547
2548	STAssertTrue(count == CFDataGetLength(data), @"Wrong data length");
2549
2550	CFRelease(tg);
2551	CFRelease(nt);
2552	CFRelease(tr);
2553	CFRelease(data);
2554}
2555
2556
2557static SecTransformInstanceBlock CountTransformTest(CFStringRef name,
2558							SecTransformRef newTransform,
2559							SecTransformImplementationRef ref)
2560{
2561	SecTransformInstanceBlock instanceBlock =
2562	^{
2563		CFErrorRef result = NULL;
2564		SecTransformSetAttributeAction(ref,kSecTransformActionAttributeNotification, CFSTR("INPUT"),
2565			^(SecTransformAttributeRef ah, CFTypeRef value)
2566			{
2567				if (value) {
2568					if (CFGetTypeID(value) != CFNumberGetTypeID()) {
2569						SecTransformCustomSetAttribute(ref, CFSTR("ABORT"), kSecTransformMetaAttributeValue, CFSTR("Bad type"));
2570						return value;
2571					}
2572					CFNumberRef nr = (CFNumberRef)value;
2573					int max, i;
2574					CFNumberGetValue(nr, kCFNumberIntType, &max);
2575					for(i = 0; i < max; ++i) {
2576						nr = CFNumberCreate(NULL, kCFNumberIntType, &i);
2577						SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, nr);
2578						CFRelease(nr);
2579					}
2580				} else {
2581					SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, value);
2582				}
2583
2584				return value;
2585			});
2586
2587		return result;
2588	};
2589
2590	return Block_copy(instanceBlock);
2591}
2592
2593static SecTransformRef count_transform(int n) {
2594	CFStringRef name = CFSTR("com.apple.security.unit-test.count");
2595	static dispatch_once_t once;
2596
2597	dispatch_once(&once,
2598	^{
2599		SecTransformRegister(name, &CountTransformTest, NULL);
2600	});
2601
2602	SecTransformRef ct = SecTransformCreate(name, NULL);
2603	CFNumberRef num = CFNumberCreate(NULL, kCFNumberIntType, &n);
2604	SecTransformSetAttribute(ct, CFSTR("INPUT"), num, NULL);
2605	CFRelease(num);
2606
2607	return ct;
2608}
2609
2610
2611static SecTransformInstanceBlock StallTest(CFStringRef name,
2612							SecTransformRef newTransform,
2613							SecTransformImplementationRef ref)
2614{
2615	SecTransformInstanceBlock instanceBlock =
2616	^{
2617		CFErrorRef result = NULL;
2618		__block bool go = false;
2619
2620		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("GO"),
2621			^(SecTransformAttributeRef ah, CFTypeRef value)
2622			{
2623				go = true;
2624				return value;
2625			});
2626
2627		SecTransformAttributeRef in_ah = SecTransformCustomGetAttribute(ref, CFSTR("INPUT"), kSecTransformMetaAttributeRef);
2628		SecTransformAttributeRef out_ah = SecTransformCustomGetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeRef);
2629
2630		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, NULL,
2631			^(SecTransformAttributeRef ah, CFTypeRef value)
2632			{
2633				if (!go) {
2634					SecTransformPushbackAttribute(ref, ah, value);
2635				} else {
2636					if (ah == in_ah) {
2637						SecTransformCustomSetAttribute(ref, out_ah, kSecTransformMetaAttributeValue, value);
2638					}
2639				}
2640				return value;
2641			});
2642
2643		return result;
2644	};
2645
2646	return Block_copy(instanceBlock);
2647}
2648
2649-(void)testStall {
2650	CFStringRef name = CFSTR("com.apple.security.unit-test.stall");
2651
2652	(void)SecTransformRegister(name, &StallTest, NULL);
2653
2654	SecTransformRef stall = SecTransformCreate(name, NULL);
2655	SecTransformRef seven = count_transform(7);
2656	SecTransformRef group = SecTransformCreateGroupTransform();
2657	SecTransformRef delay = delay_transform(NSEC_PER_SEC / 10);
2658
2659	SecTransformConnectTransforms(seven, CFSTR("OUTPUT"), stall, CFSTR("FOO"), group, NULL);
2660	SecTransformConnectTransforms(seven, CFSTR("OUTPUT"), stall, CFSTR("BAR"), group, NULL);
2661	SecTransformConnectTransforms(seven, CFSTR("OUTPUT"), stall, CFSTR("BAZ"), group, NULL);
2662	SecTransformConnectTransforms(seven, CFSTR("OUTPUT"), stall, CFSTR("INPUT"), group, NULL);
2663	SecTransformConnectTransforms(delay, CFSTR("OUTPUT"), stall, CFSTR("GO"), group, NULL);
2664
2665	SecTransformSetAttribute(delay, CFSTR("INPUT"), (CFNumberRef)[NSNumber numberWithInt:42], NULL);
2666
2667	CFErrorRef err = NULL;
2668	CFTypeRef r = SecTransformExecute(group, &err);
2669
2670	STAssertNotNil((id)r, @"Results from testStall");
2671	STAssertNil((id)err, @"Got %@ error from testStall", err);
2672	NSArray *array_seven = [NSArray arrayWithObjects:[NSNumber numberWithInt:0], [NSNumber numberWithInt:1], [NSNumber numberWithInt:2], [NSNumber numberWithInt:3], [NSNumber numberWithInt:4], [NSNumber numberWithInt:5], [NSNumber numberWithInt:6], NULL];
2673	STAssertEqualObjects((id)r, array_seven, @"Correct stall test results");
2674
2675	CFRelease(delay);
2676	CFRelease(group);
2677	CFRelease(seven);
2678	CFRelease(stall);
2679}
2680
2681-(void)testInappropriateExecution
2682{
2683	// We want to have more then enough work for all the CPUs to help force a race, so twice
2684	// the number of logical CPUs should do it.   NOTE: the completion blocks got to a low
2685	// priority concurrent queue to encourage them to finish out of order and put more
2686	// stress on the system we are testing.
2687
2688	int logical_cpus = 1;
2689	size_t int_size = sizeof(logical_cpus);
2690	int return_code = sysctlbyname("hw.logicalcpu_max", &logical_cpus, &int_size, NULL, 0);
2691	int e = errno; // Save this value so it doesn't get trashed by any subsequent syscalls
2692	STAssertEquals(return_code, 0, @"sysctlbyname failed %s (%d), assuming 1 CPU", strerror(e), e);
2693
2694	SecTransformRef count_a_bunch = count_transform(logical_cpus * 2);
2695	CFErrorRef err = NULL;
2696	dispatch_group_t wait_for_async_to_complete = dispatch_group_create();
2697	dispatch_group_t outstanding_executions = dispatch_group_create();
2698	SecTransformRef count_group = SecTransformCreateGroupTransform();
2699
2700	SecTransformConnectTransforms(count_a_bunch, CFSTR("kludge1"), count_a_bunch, CFSTR("kludge2"), count_group, &err);
2701	STAssertNil((id)err, @"Error (%@) connecting count transform to itself", err);
2702
2703	dispatch_group_enter(wait_for_async_to_complete);
2704	SecTransformExecuteAsync(count_a_bunch, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
2705	{
2706		dispatch_group_enter(outstanding_executions);
2707
2708		CFErrorRef err = NULL;
2709		CFTypeRef no_result = SecTransformExecute(count_a_bunch, &err);
2710		STAssertNil((id)no_result, @"Attempting to execute an already executing transform should fail, not provide results: %@", no_result);
2711		STAssertNotNil((id)err, @"Attempting to execute an already executing transform should produce some sort of error");
2712		CFRelease(err);
2713
2714		dispatch_group_leave(outstanding_executions);
2715
2716		if (isFinal)
2717		{
2718			// Give any pending executions time to get to the group_enter
2719			usleep(100);
2720			dispatch_group_wait(outstanding_executions, DISPATCH_TIME_FOREVER);
2721			dispatch_release(outstanding_executions);
2722			dispatch_group_leave(wait_for_async_to_complete);
2723		}
2724	});
2725
2726	// Before that SecTransformExecuteAsync completes, we do some more work at >low priority to help
2727	// keep the completion blocks landing out of order.    In particular we run a transform to
2728	// completion, and then confirm that we can't run it again.
2729
2730	SecTransformRef no_work = SecNullTransformCreate();
2731	SecTransformRef no_work_group = SecTransformCreateGroupTransform();
2732	SecTransformConnectTransforms(no_work, CFSTR("kludge1"), no_work, CFSTR("kludge2"), no_work_group, &err);
2733	STAssertNil((id)err, @"Can't connect no_work to itself (to make no_work_group), err=%@", err);
2734
2735	SecTransformSetAttribute(no_work, CFSTR("INPUT"), CFSTR("value"), NULL);
2736	CFTypeRef no_result = SecTransformExecute(no_work_group, &err);
2737	STAssertNil((id)err, @"First execute of Null Transform should be ok, got e=%@", err);
2738	STAssertNotNil((id)no_result, @"First execute of Null Transform should produce a value");
2739
2740	no_result = SecTransformExecute(no_work_group, &err);
2741
2742	STAssertNotNil((id)err, @"Second execute of Null Transform should fail!");
2743	STAssertNil((id)no_result, @"Second execute of Null Transform shouldn't produce a value, got r=%@", no_result);
2744	CFRelease(err);
2745
2746	// Now we wait for that first batch of tests to finish, we don't want to call STFail after self goes away.
2747
2748	dispatch_group_wait(wait_for_async_to_complete, DISPATCH_TIME_FOREVER);
2749	dispatch_release(wait_for_async_to_complete);
2750
2751	if (no_result) CFRelease(no_result);
2752	if (no_work) CFRelease(no_work);
2753	if (no_work_group) CFRelease(no_work_group);
2754	if (count_group) CFRelease(count_group);
2755	if (count_a_bunch) CFRelease(count_a_bunch);
2756}
2757
2758static SecTransformInstanceBlock ConnectionReqTest(CFStringRef name,
2759							SecTransformRef newTransform,
2760							SecTransformImplementationRef ref)
2761{
2762	SecTransformInstanceBlock instanceBlock =
2763	^{
2764		CFErrorRef result = NULL;
2765		SecTransformAttributeRef xah =
2766			(SecTransformAttributeRef)SecTranformCustomGetAttribute(ref, CFSTR("XYZZY"), kSecTransformMetaAttributeRef);
2767
2768		SecTransformCustomSetAttribute(ref, xah, kSecTransformMetaAttributeRequiresOutboundConnection, kCFBooleanTrue);
2769		SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeRequiresOutboundConnection, kCFBooleanFalse);
2770		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"),
2771			^(SecTransformAttributeRef ah, CFTypeRef value)
2772			{
2773				SecTransformCustomSetAttribute(ref, xah, kSecTransformMetaAttributeValue, value);
2774				return value;
2775			});
2776
2777		return result;
2778	};
2779
2780	return Block_copy(instanceBlock);
2781}
2782
2783
2784-(void)testConnectionReq {
2785
2786	CFStringRef req_xyzzy_name = CFSTR("com.apple.security.unit-test.req_xyzzy");
2787
2788	(void)SecTransformRegister(req_xyzzy_name, &ConnectionReqTest, NULL);
2789
2790	SecTransformRef tr_req_xyzzy = SecTransformCreate(req_xyzzy_name, NULL);
2791
2792	CFTypeRef in_value = (CFTypeRef)@"Fnord";
2793	SecTransformSetAttribute(tr_req_xyzzy, CFSTR("INPUT"), in_value, NULL);
2794
2795	CFErrorRef err = NULL;
2796	CFTypeRef r = SecTransformExecute(tr_req_xyzzy, &err);
2797
2798	STAssertNil((id)r, @"Execute of tr_req_xyzzy with no xyzzy r=%@", r);
2799	STAssertErrorHas((id)err, @"req_xyzzy", @"Error failed to refer to the transform by name (%@)", err);
2800	STAssertErrorHas((id)err, @"XYZZY", @"Error failed to refer to missing attribute by name (%@)", err);
2801	STAssertErrorHas((id)err, @"requires.*outbound connection", @"Error failed to diagnose invalid condition (%@)", err);
2802
2803	CFRelease(err);
2804	CFRelease(tr_req_xyzzy);
2805	if (r) CFRelease(r);
2806
2807	/*
2808
2809	Note For Josh:
2810
2811	To make this work we need Josh's fix for FindLastTransform!
2812
2813	CFRelease(tr_req_xyzzy);
2814	tr_req_xyzzy = SecTransformCreate(req_xyzzy_name, NULL);
2815	SecTransformSetAttribute(tr_req_xyzzy, CFSTR("INPUT"), in_value, NULL);
2816
2817	SecTransformRef group = SecTransformCreateGroupTransform();
2818	SecTransformRef tee = SecNullTransformCreate();
2819	SecTransformConnectTransforms(tr_req_xyzzy, CFSTR("XYZZY"), tee, kSecTransformInputAttributeName, group, &err);
2820	STAssertNil((id)err, @"err=%@ from connect", err);
2821	STAssertNotNil((id)group, @"No group after connect");
2822	r = SecTransformExecute(group, &err);
2823	STAssertNil((id)err, @"Execute err=%@");
2824	STAssertEqualObjects((id)in_value, (id)r, @"Execution Result");
2825
2826	*/
2827}
2828
2829static SecTransformInstanceBlock DeferredTest(CFStringRef name,
2830							SecTransformRef newTransform,
2831							SecTransformImplementationRef ref)
2832{
2833	SecTransformInstanceBlock instanceBlock =
2834	^{
2835		CFErrorRef result = NULL;
2836		SecTransformCustomSetAttribute(ref, CFSTR("LATE"), kSecTransformMetaAttributeDeferred, kCFBooleanTrue);
2837		SecTransformCustomSetAttribute(ref, CFSTR("INPUT"), kSecTransformMetaAttributeDeferred, kCFBooleanFalse);
2838
2839		__block CFTypeRef in_v = NULL, late_v = NULL;
2840
2841		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"),
2842			^(SecTransformAttributeRef ah, CFTypeRef value)
2843			{
2844				if (NULL != late_v) {
2845					SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue,
2846												   CreateGenericErrorRef(CFSTR("FAIL"), 1, "LATE (%@) should process after INPUT (%@)", late_v, value));
2847				}
2848				in_v = value;
2849				return value;
2850			});
2851
2852		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("LATE"),
2853			^(SecTransformAttributeRef ah, CFTypeRef value)
2854			{
2855				if (NULL == in_v) {
2856					SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue,
2857												   CreateGenericErrorRef(CFSTR("FAIL"), 1, "INPUT (%@) should process before LATE (%@)", in_v, value));
2858				}
2859
2860				late_v = value;
2861				SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, NULL);
2862				return value;
2863			});
2864
2865		return result;
2866	};
2867
2868	return Block_copy(instanceBlock);
2869}
2870
2871
2872-(void)testDeferred {
2873	CFStringRef deferred_name = CFSTR("com.apple.security.unit-test.deferred");
2874
2875	(void)SecTransformRegister(deferred_name, &DeferredTest, NULL);
2876
2877	SecTransformRef dt = SecTransformCreate(deferred_name, NULL);
2878
2879	// these set attribute calls are failing, but we're ignoring the failures
2880	SecTransformSetAttribute(dt, CFSTR("INPUT"), (CFTypeRef)CFSTR("BLAH"), NULL);
2881	SecTransformSetAttribute(dt, CFSTR("LATE"), (CFTypeRef)CFSTR("QUUX"), NULL);
2882	CFErrorRef err = NULL;
2883	SecTransformExecute(dt, &err);
2884	STAssertNil((id)err, @"Error from execute err=%@", err);
2885
2886	if (err) CFRelease(err);
2887	// CFRelease(dt);
2888}
2889
2890static SecTransformInstanceBlock SaveRestoreTest(CFStringRef name,
2891							SecTransformRef newTransform,
2892							SecTransformImplementationRef ref)
2893{
2894	SecTransformInstanceBlock instanceBlock =
2895	^{
2896		CFErrorRef result = NULL;
2897		SecTransformCustomSetAttribute(ref, CFSTR("Needed"), kSecTransformMetaAttributeRequired, kCFBooleanTrue);
2898		SecTransformCustomSetAttribute(ref, CFSTR("NoSaves"), kSecTransformMetaAttributeExternalize, kCFBooleanFalse);
2899
2900		return result;
2901	};
2902
2903	return Block_copy(instanceBlock);
2904}
2905
2906-(void)testSaveRestore
2907{
2908
2909	unsigned char raw_data[] = "Val-U-Sav, nw wth lss vwls!";
2910	CFDataRef data = CFDataCreate(NULL, (const UInt8*)&raw_data, sizeof(raw_data));
2911	CFErrorRef err = NULL;
2912
2913	CFStringRef name = CFSTR("com.apple.security.unit-test.SaveRestoreTest");
2914
2915	(void)SecTransformRegister(name, &SaveRestoreTest, NULL);
2916
2917	SecTransformRef tr1 = SecTransformCreate(name, NULL);
2918
2919	SecTransformSetAttribute(tr1, CFSTR("Optional"), CFSTR("42"), NULL);
2920	SecTransformSetAttribute(tr1, CFSTR("Needed"), CFSTR("and provided"), NULL);
2921	SecTransformSetAttribute(tr1, CFSTR("NoSaves"), CFSTR("42"), NULL);
2922
2923	CFDictionaryRef xr = SecTransformCopyExternalRepresentation(tr1);
2924	STAssertNotNil((NSDictionary *)xr, @"external rep");
2925	SecTransformRef tr2 = SecTransformCreateFromExternalRepresentation(xr, NULL);
2926	SecTransformSetAttribute(tr2, kSecTransformInputAttributeName, data, NULL);
2927	CFTypeRef none = SecTransformGetAttribute(tr2, CFSTR("NoSaves"));
2928	STAssertNil((id)none, @"Expected %@ to be nil", none);
2929	CFTypeRef forty_two = SecTransformGetAttribute(tr2, CFSTR("Optional"));
2930	STAssertEqualObjects((id)forty_two, @"42", @"restored incorrect value");
2931
2932	CFDataRef d = (CFDataRef)SecTransformExecute((NSObject *)tr2,  &err);
2933
2934	STAssertNotNil((NSData * )d, @"execute result (err=%@)", err);
2935
2936	if (err) {
2937		CFRelease(err);
2938	}
2939
2940	CFRelease(tr1);
2941	CFRelease(tr2);
2942	CFRelease(xr);
2943
2944	tr1 = SecTransformCreate(name, NULL);
2945	SecTransformSetAttribute(tr1, CFSTR("Needed"), CFSTR("and provided"), NULL);
2946	SecTransformSetAttribute(tr1, CFSTR("NoSaves"), CFSTR("42"), NULL);
2947	xr = SecTransformCopyExternalRepresentation(tr1);
2948	tr2 = SecTransformCreateFromExternalRepresentation(xr, NULL);
2949
2950
2951
2952	//tr2 = SecTransformCreateFromExternalRepresentation(xr, NULL);
2953	SecTransformRef tga = SecTransformCreateGroupTransform();
2954	SecTransformSetAttribute(tr1, kSecTransformInputAttributeName, data, NULL);
2955
2956	// XXX did not swap
2957	SecTransformConnectTransforms(tr1, CFSTR("OUTPUT"), tr2, CFSTR("INPUT"), tga, NULL);
2958	CFStringRef has1 = CFSTR("I has one!");
2959	CFStringRef has2 = CFSTR("I has two of them!");
2960	SecTransformSetAttribute(tr1, CFSTR("Needed"), has1, NULL);
2961	SecTransformSetAttribute(tr2, CFSTR("Needed"), has2, NULL);
2962	xr = SecTransformCopyExternalRepresentation(tr1);
2963	STAssertNotNil((NSDictionary *)xr, @"external rep for 2");
2964	NSLog(@"xr=%@", xr);
2965
2966	SecTransformRef tgb = SecTransformCreateFromExternalRepresentation(xr, &err);
2967	STAssertNil((id)tgb, @"made transform group with duplicate labels");
2968	STAssertErrorHas((id)err, (NSString*)name, @"Error failed to identify the transform (%@)", err);
2969	STAssertErrorHas((id)err, @"damage|duplicate", @"Error failed to diagnose the invalid condition (%@)", err);
2970
2971	CFStringRef new_name2 = CFSTR("SaveRestoreTestThingie#2");
2972	CFStringRef fetched_name;
2973	int attempts;
2974
2975	for(attempts = 0; attempts < 20; ++attempts)
2976	{
2977		SecTransformSetAttribute(tr2, CFSTR("NAME"), new_name2, &err);
2978		fetched_name = (CFStringRef)SecTransformGetAttribute(tr2, CFSTR("NAME"));
2979
2980		STAssertNil((id)err, @"Error from setting tr2's name: %@", err);
2981		STAssertEqualObjects((id)fetched_name, (id)new_name2, @"Set tr2's name, attempt %d", attempts);
2982		if (CFEqual(fetched_name, new_name2))
2983		{
2984			break;
2985		}
2986		if (attempts > 10)
2987		{
2988			usleep(1000);
2989		}
2990	}
2991
2992	xr = SecTransformCopyExternalRepresentation(tr1);
2993	STAssertNotNil((NSDictionary *)xr, @"external rep for 2, take 2");
2994	NSLog(@"xr=%@", xr);
2995
2996	tgb = SecTransformCreateFromExternalRepresentation(xr, &err);
2997	STAssertNotNil((id)tgb, @"made transform group (take 2)");
2998	STAssertNil((id)err, @"error from make 2 take 2 (err=%@)", err);
2999
3000	SecTransformRef tr1b = SecTransformFindByName(tgb, (CFStringRef)SecTransformGetAttribute(tr1, CFSTR("NAME")));
3001	STAssertNotNil((id)tr1b, @"Found tr1b");
3002	SecTransformRef tr2b = SecTransformFindByName(tgb, (CFStringRef)SecTransformGetAttribute(tr2, CFSTR("NAME")));
3003	STAssertNotNil((id)tr2b, @"Found tr2b");
3004
3005	CFStringRef has1b = (CFStringRef)SecTransformGetAttribute(tr1b, CFSTR("Needed"));
3006	STAssertNotNil((id)tr1b, @"tr1b's name");
3007	CFStringRef has2b = (CFStringRef)SecTransformGetAttribute(tr2b, CFSTR("Needed"));
3008	STAssertNotNil((id)tr2b, @"tr1b's name");
3009
3010	STAssertEqualObjects((id)has1, (id)has1b, @"has1 == has1b");
3011	STAssertEqualObjects((id)has2, (id)has2b, @"has2 == has2b");
3012
3013}
3014
3015-(void)testRequiredAttributes
3016{
3017	CFStringRef name = CFSTR("com.apple.security.unit-test.requiresStuffThings");
3018	CFErrorRef error;
3019	// In addition to testing required attributes, this also does a  partial "lifecycle" test, making sure we
3020	// pass through the stages, don't regress stages, and don't receive the wrong events in the wrong stages.
3021	typedef enum { S_INITED = 0, S_STARTED, S_RUN, S_EOS, S_GONE } state_t;
3022
3023	__block state_t state = S_INITED;
3024	dispatch_group_t leave_on_finalize = dispatch_group_create();
3025
3026	SecTransformCreateBlock required_attributes_create_block = ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params) {
3027		params->overrideAttribute(kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^(SecTransformAttributeRef attribute, CFTypeRef value) {
3028			// NOTE: this is for testing with a single data value, not a series.
3029			if (value)
3030			{
3031				STAssertTrue(state == S_STARTED, @"Init'ed for data (state=%d)", state);
3032				state = S_RUN;
3033			} else {
3034				STAssertTrue(state == S_RUN, @"In run state at EOS (state=%d)", state);
3035				state = S_EOS;
3036			}
3037			params->send(kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, value);
3038			return value;
3039		});
3040
3041		params->send(CFSTR("Stuff"), kSecTransformMetaAttributeRequired, kCFBooleanTrue);
3042		params->send(CFSTR("Things"), kSecTransformMetaAttributeRequired, kCFBooleanTrue);
3043
3044		params->overrideTransform(kSecTransformActionStartingExecution, ^{
3045			STAssertTrue(state == S_INITED, @"Inited (state=%d)");
3046			state = S_STARTED;
3047			return (CFTypeRef)NULL;
3048		});
3049
3050		params->overrideTransform(kSecTransformActionFinalize, ^{
3051			state = S_GONE;
3052			dispatch_group_leave(leave_on_finalize);
3053			return (CFTypeRef)NULL;
3054		});
3055	};
3056
3057	dispatch_group_enter(leave_on_finalize);
3058	SecTransformRef tr = custom_transform(name, required_attributes_create_block);
3059	STAssertNotNil((NSObject *)tr, @"newly created custom transform");
3060
3061	char *data_bytes = (char *)"It was the best of transforms, it was the worst of transforms.";
3062	CFDataRef data = CFDataCreate(NULL, (const UInt8*)data_bytes, strlen(data_bytes));
3063	SecTransformSetAttribute(tr, kSecTransformInputAttributeName, data, NULL);
3064	usleep(100);
3065	STAssertTrue(state == S_INITED, @"not run yet");
3066	CFDataRef rdata = (CFDataRef)SecTransformExecute((NSObject *)tr, &error);
3067
3068	STAssertTrue(rdata == NULL, @"Expected no result, but got: %@", rdata);
3069	STAssertErrorHas((id)error, @"missing required attributes?", @"Error describes condition (%@)", error);
3070	STAssertErrorHas((id)error, @" Things[ ,)]", @"Missing attributes named (%@)", error);
3071	STAssertErrorHas((id)error, @" Stuff[ ,)]", @"Missing attributes named (%@)", error);
3072	STAssertErrorHas((id)error, @"requiresStuffThings", @"Name of erroring Transform in message (%@)", error);
3073
3074	if (error) {
3075		CFRelease(error);
3076	}
3077	CFRelease(tr);
3078
3079	STAssertFalse(dispatch_group_wait(leave_on_finalize, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"group was ready");
3080	STAssertTrue(state == S_GONE, @"Transform should be gone, state=%d", state);
3081
3082	dispatch_group_enter(leave_on_finalize);
3083	state = S_INITED;
3084	tr = custom_transform(name, required_attributes_create_block);
3085	STAssertNotNil((NSObject *)tr, @"newly created custom transform");
3086
3087	error = NULL;
3088	SecTransformSetAttribute(tr, kSecTransformInputAttributeName, data, NULL);
3089	SecTransformSetAttribute(tr, CFSTR("Things"), CFSTR("grubby things"), NULL);
3090	SecTransformSetAttribute(tr, CFSTR("Stuff"), CFSTR("Cool stuff"), NULL);
3091	rdata = (CFDataRef)SecTransformExecute(tr,  &error);
3092
3093	STAssertNotNil((NSData *)rdata, @"Got data back");
3094	STAssertEqualObjects((NSData *)rdata, (NSData *)data, @"Data unchanged");
3095	STAssertTrue(state == S_EOS, @"Transform hit EOS");
3096	STAssertTrue(error == NULL, @"Error not set");
3097
3098	CFRelease(tr);
3099	STAssertFalse(dispatch_group_wait(leave_on_finalize, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"group was ready");
3100	STAssertTrue(state == S_GONE, @"Transform gone (state=%d)", state);
3101	dispatch_group_notify(leave_on_finalize, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
3102		dispatch_release(leave_on_finalize);
3103	});
3104}
3105
3106static SecTransformInstanceBlock AttributeNotificationTest(CFStringRef name,
3107							SecTransformRef newTransform,
3108							SecTransformImplementationRef ref)
3109{
3110	SecTransformInstanceBlock instanceBlock =
3111	^{
3112		CFErrorRef result = NULL;
3113
3114
3115		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, NULL,
3116			^(SecTransformStringOrAttributeRef ah, CFTypeRef value)
3117			{
3118				SecTransformCustomSetAttribute(ref, CFSTR("Generic"), kSecTransformMetaAttributeValue, kCFBooleanTrue);
3119				return value;
3120			});
3121
3122		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("Specific"),
3123			^(SecTransformStringOrAttributeRef ah, CFTypeRef value)
3124			{
3125				SecTransformCustomSetAttribute(ref, CFSTR("Specific"), kSecTransformMetaAttributeValue, kCFBooleanTrue);
3126				return value;
3127
3128				});
3129
3130		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("AlsoSpecific"),
3131			^(SecTransformStringOrAttributeRef ah, CFTypeRef value)
3132			{
3133				SecTransformCustomSetAttribute(ref, CFSTR("AlsoSpecific"), kSecTransformMetaAttributeValue, kCFBooleanTrue);
3134
3135				return value;
3136			});
3137
3138		return result;
3139	};
3140
3141	return Block_copy(instanceBlock);
3142}
3143
3144-(void)testAttributeNotifications
3145{
3146	NSString *name = @"com.apple.security.unit-test.testAttributeNotifications";
3147	Boolean generic_called = NO;
3148	Boolean specific_called = NO;
3149	Boolean also_specific_called = NO;
3150
3151	Boolean ok = SecTransformRegister((CFStringRef)name, &AttributeNotificationTest, NULL);
3152
3153	STAssertTrue(ok, @"Successful register");
3154
3155	SecTransformRef tr = SecTransformCreate((CFStringRef)name, NULL);
3156
3157	CFStringRef aNameStr = ((CFStringRef)name);
3158	SecTransformSetAttribute(tr, CFSTR("Generic"), aNameStr, NULL);
3159	SecTransformSetAttribute(tr, CFSTR("Specific"), aNameStr, NULL);
3160	SecTransformSetAttribute(tr, CFSTR("AlsoSpecific"), aNameStr, NULL);
3161
3162	generic_called = (kCFBooleanTrue == (CFBooleanRef)SecTransformGetAttribute(tr, CFSTR("Generic")));
3163	specific_called = (kCFBooleanTrue == (CFBooleanRef)SecTransformGetAttribute(tr, CFSTR("Specific")));
3164	also_specific_called = (kCFBooleanTrue == (CFBooleanRef)SecTransformGetAttribute(tr, CFSTR("AlsoSpecific")));
3165
3166
3167	STAssertTrue(generic_called, @"generic called");
3168	STAssertTrue(specific_called, @"specific called");
3169	STAssertTrue(also_specific_called, @"also specific called");
3170
3171	CFRelease(tr);
3172}
3173
3174-(void)testEncryptAndDecryptTransforms
3175{
3176	NSAutoreleasePool *pool = [NSAutoreleasePool new];
3177
3178	// generate a symmetrical key for testing
3179	OSStatus err = errSecSuccess;
3180
3181	NSString* algNames[] =
3182	{
3183		@"AES 128",
3184		@"AES 192",
3185		@"AES 256",
3186		@"DES",
3187		@"3DES",
3188		@"CAST",
3189		@"RC4"
3190	};
3191
3192	CSSM_ALGORITHMS symmetricalAlgos[] =
3193	{
3194		CSSM_ALGID_AES,
3195		CSSM_ALGID_AES,
3196		CSSM_ALGID_AES,
3197		CSSM_ALGID_DES,
3198		CSSM_ALGID_3DES_3KEY_EDE,
3199		CSSM_ALGID_CAST,
3200		CSSM_ALGID_RC4
3201	};
3202
3203	uint32 keySizes[] =
3204	{
3205		128,
3206		192,
3207		256,
3208		64,
3209		192,
3210		40,
3211		8
3212	};
3213
3214	CSSM_KEYUSE keyUse = CSSM_KEYUSE_ANY;
3215	CSSM_KEYATTR_FLAGS keyAttrFlags = CSSM_KEYATTR_RETURN_DEFAULT;
3216	SecAccessRef accessRef = NULL;
3217	CSSM_CC_HANDLE handle = ((CSSM_CC_HANDLE)0);
3218
3219	NSString* dataStr = @"At the round earth's imagined corners blow\
3220	Your trumpets, angels, and arise, arise\
3221	From death, you numberless infinities\
3222	Of souls, and to your scattered bodies go,\
3223	All whom the flood did, and fire shall, overthrow,\
3224	All whom war, dearth, age, agues, tyrannies,\
3225	Despair, law, chance, hath slain, and you whose eyes\
3226	Shall behold God, and never taste death's woe.\
3227	But let them sleep, Lord, and me mourn a space,\
3228	For, if above all these my sins abound,\
3229	'Tis late to ask abundance of Thy grace,\
3230	When we are there. Here on this lowly ground\
3231	Teach me how to repent; for that's as good\
3232	As if Thou'dst sealed my pardon, with Thy blood.";
3233
3234	NSData* testData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
3235	int numItems = (sizeof(symmetricalAlgos) / sizeof(CSSM_ALGORITHMS));
3236	int iCnt = 0;
3237
3238	for (iCnt = 0; iCnt < numItems; iCnt++)
3239	{
3240		SecKeyRef testKey = NULL;
3241		CSSM_ALGORITHMS algoToUse = symmetricalAlgos[iCnt];
3242		uint32 keySizeInBits = keySizes[iCnt];
3243
3244		err = SecKeyGenerate(NULL, algoToUse, keySizeInBits, handle, keyUse, keyAttrFlags, accessRef, &testKey);
3245		STAssertTrue(err == errSecSuccess, [NSString stringWithFormat:@"Unable to create a symmetrical key %@", algNames[iCnt]]);
3246		if (errSecSuccess != err)
3247		{
3248			continue;
3249		}
3250		__block CFErrorRef error = NULL;
3251
3252		SecTransformRef encryptSymRef = NULL;
3253		encryptSymRef = SecEncryptTransformCreate(testKey, &error);
3254		if (NULL != error)
3255		{
3256			CFRelease(testKey);
3257			STAssertTrue(NO, [NSString stringWithFormat:@"Unable to create the encrypt transform for key %@", algNames[iCnt]]);
3258			continue;
3259		}
3260
3261		SecTransformRef decryptSymRef = SecDecryptTransformCreate(testKey, &error);
3262		if (NULL != error)
3263		{
3264			CFRelease(testKey);
3265			CFRelease(encryptSymRef);
3266			STAssertTrue(NO, [NSString stringWithFormat:@"Unable to create the decrypt transform for key %@", algNames[iCnt]]);
3267			continue;
3268		}
3269
3270		// connect the output of the encryption to the input of the decryption transform
3271
3272		SecGroupTransformRef group = SecTransformCreateGroupTransform();
3273		(void)SecTransformConnectTransforms(encryptSymRef, kSecTransformOutputAttributeName,
3274						decryptSymRef, kSecTransformInputAttributeName,
3275						group, &error);
3276		if (NULL != error)
3277		{
3278			CFRelease(testKey);
3279			CFRelease(encryptSymRef);
3280			CFRelease(decryptSymRef);
3281			STAssertTrue(NO, [NSString stringWithFormat:@"Unable to connect transforms for key %@", algNames[iCnt]]);
3282			continue;
3283		}
3284
3285
3286		NSInputStream* dataStream = [NSInputStream inputStreamWithData:testData];
3287		[dataStream open];
3288
3289		SecTransformSetAttribute(encryptSymRef, kSecTransformInputAttributeName, (CFTypeRef)dataStream, &error);
3290		if (NULL != error)
3291		{
3292			CFRelease(testKey);
3293			CFRelease(encryptSymRef);
3294			CFRelease(decryptSymRef);
3295			STAssertTrue(NO, [NSString stringWithFormat:@"Unable to set the input for key %@", algNames[iCnt]]);
3296			continue;
3297		}
3298
3299		CFTypeRef transformResult = SecTransformExecute(encryptSymRef, &error);
3300		CFRelease(group);
3301		CFRelease(encryptSymRef);
3302		CFRelease(decryptSymRef);
3303
3304		if (NULL != error)
3305		{
3306			STAssertTrue(NO, [NSString stringWithFormat:@"returned an error for algo %@", algNames[iCnt]]);
3307			continue;
3308			CFRelease(error);
3309		}
3310
3311		if (NULL == transformResult || 0 == [(NSData*)transformResult length])
3312		{
3313			STAssertTrue(NO, [NSString stringWithFormat:@"transformResult was NULL or empty for %@", algNames[iCnt]]);
3314			continue;
3315		}
3316
3317		NSData* resultData = nil;
3318		if (CFGetTypeID(transformResult) == CFDataGetTypeID())
3319		{
3320			resultData = (NSData*)transformResult;
3321			[resultData autorelease];
3322		}
3323
3324		CFRelease(testKey);
3325
3326		STAssertTrue([testData isEqualToData:resultData], @"The output of the decrypt transform does NOT match the original input!");
3327	}
3328
3329
3330	SecKeyRef publicKey = NULL;
3331	SecKeyRef privateKey = NULL;
3332
3333	keyAttrFlags = CSSM_KEYATTR_RETURN_REF;
3334
3335	const uint32 publicKeyAttributes = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF;
3336	const uint32 privateKeyAttributes = CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE;
3337
3338	CSSM_KEYUSE pubKeyUse = CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_WRAP;
3339	CSSM_KEYUSE privKeyUse = CSSM_KEYUSE_SIGN | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_UNWRAP;
3340
3341
3342	err = SecKeyCreatePair(NULL, CSSM_ALGID_RSA, 2048, ((CSSM_CC_HANDLE)0),
3343						   pubKeyUse, publicKeyAttributes,
3344						   privKeyUse, privateKeyAttributes,
3345						   NULL, &publicKey, &privateKey);
3346
3347	STAssertTrue(errSecSuccess == err, @"Unable to create a key pair");
3348	if (errSecSuccess != err)
3349	{
3350		cssmPerror(NULL, err);
3351		return;
3352	}
3353
3354	CFErrorRef error = NULL;
3355	SecTransformRef encryptSymRef = SecEncryptTransformCreate(publicKey , &error);
3356	if (NULL != error)
3357	{
3358		CFRelease(publicKey);
3359		CFRelease(privateKey);
3360		STAssertTrue(NO, [NSString stringWithFormat:@"Unable to create the encrypt transform for key RSA"]);
3361		return;
3362	}
3363
3364	SecTransformRef decryptSymRef = SecDecryptTransformCreate(privateKey, &error);
3365	if (NULL != error)
3366	{
3367		CFRelease(publicKey);
3368		CFRelease(privateKey);
3369		CFRelease(encryptSymRef);
3370		STAssertTrue(NO, [NSString stringWithFormat:@"Unable to create the decrypt transform for key RSA"]);
3371		return;
3372	}
3373
3374	// connect the output of the encryption to the input of the decryption transform
3375
3376	SecGroupTransformRef group = SecTransformCreateGroupTransform();
3377	(void)SecTransformConnectTransforms( encryptSymRef, kSecTransformOutputAttributeName,
3378							decryptSymRef, kSecTransformInputAttributeName,
3379							group, &error);
3380	if (NULL != error)
3381	{
3382		CFRelease(publicKey);
3383		CFRelease(privateKey);
3384		CFRelease(encryptSymRef);
3385		CFRelease(decryptSymRef);
3386		STAssertTrue(NO, [NSString stringWithFormat:@"Unable to connect transforms for key RSA"]);
3387		return;
3388	}
3389
3390	NSInputStream* dataStream = [NSInputStream inputStreamWithData:testData];
3391	[dataStream open];
3392
3393	SecTransformSetAttribute(encryptSymRef, kSecTransformInputAttributeName, (CFTypeRef)dataStream, &error);
3394	if (NULL != error)
3395	{
3396		CFRelease(publicKey);
3397		CFRelease(privateKey);
3398		CFRelease(encryptSymRef);
3399		CFRelease(decryptSymRef);
3400		STAssertTrue(NO, [NSString stringWithFormat:@"Unable to set the input for key RSA"]);
3401		return;
3402	}
3403	CFTypeRef transformResult = SecTransformExecute(encryptSymRef, &error);
3404	if (NULL != error)
3405	{
3406		STAssertTrue(NO, [NSString stringWithFormat:@"returned an error for RSA"]);
3407		CFRelease(error);
3408		return;
3409	}
3410
3411	if (NULL == transformResult || 0 == [(NSData*)transformResult length])
3412	{
3413		STAssertTrue(NO, [NSString stringWithFormat:@"transformResult was NULL or empty for RSA"]);
3414		return;
3415	}
3416
3417	NSData* resultData = nil;
3418	if (CFGetTypeID(transformResult) == CFDataGetTypeID())
3419	{
3420		resultData = (NSData*)transformResult;
3421		[resultData autorelease];
3422	}
3423
3424	CFRelease(publicKey);
3425	CFRelease(privateKey);
3426	CFRelease(encryptSymRef);
3427	CFRelease(decryptSymRef);
3428
3429	STAssertTrue([testData isEqualToData:resultData], @"(RSA)The output of the decrypt transform does NOT match the original input!");
3430
3431	[pool drain];
3432}
3433
3434// NOTE: this test is largely the same as testEncryptAndDecryptTransforms, but
3435// we make a single key and use it from many threads at once.   This uncovered
3436// some locking issues, so makes a good regression test.
3437-(void)testMultiEncryptWithSameKey {
3438	// generate a symmetrical key for testing
3439	CSSM_KEYUSE keyUse = CSSM_KEYUSE_ANY;
3440	CSSM_KEYATTR_FLAGS keyAttrFlags = CSSM_KEYATTR_RETURN_DEFAULT;
3441	SecAccessRef accessRef = NULL;
3442	CSSM_CC_HANDLE handle = ((CSSM_CC_HANDLE)0);
3443
3444	NSString* dataStr = @"Reduce, reuse, recycle.   No crashes please.";
3445	NSData* testData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
3446
3447	SecKeyRef testKey = NULL;
3448	{
3449		OSStatus err;
3450		err = SecKeyGenerate(NULL, CSSM_ALGID_AES, 256, handle, keyUse, keyAttrFlags, accessRef, &testKey);
3451		STAssertTrue(err == errSecSuccess, @"Unable to create a symmetrical key err=%x", err);
3452	}
3453
3454	// The number of iterations is somewhat arbitrary.   When we use to have failures they were
3455	// within 2*#logicalCPUs iterations, but nothing says we won't have a regression that happens
3456	// outside that window.
3457	dispatch_apply(128, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
3458		__block CFErrorRef error = NULL;
3459
3460		SecTransformRef encryptSymRef = NULL;
3461		encryptSymRef = SecEncryptTransformCreate(testKey, &error);
3462		if (NULL != error)
3463		{
3464			STFail(@"Unable to create the encrypt transform iteration#%d error=%@", i, error);
3465			return;
3466		}
3467		if (NULL == encryptSymRef) {
3468			STFail(@"Unable to create the encrypt transform iteration#%d, error=NULL", i);
3469			return;
3470		}
3471
3472		SecTransformRef decryptSymRef = SecDecryptTransformCreate(testKey, &error);
3473		if (NULL != error)
3474		{
3475			CFRelease(encryptSymRef);
3476			STFail(@"Unable to create the decrypt transform iteration#%d error=%@", i, error);
3477			return;
3478		}
3479		if (NULL == decryptSymRef) {
3480			CFRelease(encryptSymRef);
3481			STFail(@"Unable to create the decrypt transform iteration#%d, error=NULL", i);
3482			return;
3483		}
3484
3485		// connect the output of the encryption to the input of the decryption transform
3486
3487		SecGroupTransformRef group = SecTransformCreateGroupTransform();
3488		(void)SecTransformConnectTransforms(encryptSymRef, kSecTransformOutputAttributeName,
3489											decryptSymRef, kSecTransformInputAttributeName,
3490											group, &error);
3491		if (NULL != error)
3492		{
3493			CFRelease(encryptSymRef);
3494			CFRelease(decryptSymRef);
3495			STFail(@"Unable to connect transforms on iteration %d error=%@", i, error);
3496			return;
3497		}
3498
3499
3500		NSInputStream* dataStream = [NSInputStream inputStreamWithData:testData];
3501		[dataStream open];
3502
3503		SecTransformSetAttribute(encryptSymRef, kSecTransformInputAttributeName, (CFTypeRef)dataStream, &error);
3504		if (NULL != error)
3505		{
3506			CFRelease(encryptSymRef);
3507			CFRelease(decryptSymRef);
3508			STFail(@"Unable to set the input on iteration %d error=%@", i, error);
3509			return;
3510		}
3511
3512		CFTypeRef transformResult = SecTransformExecute(encryptSymRef, &error);
3513		CFRelease(group);
3514
3515		if (NULL != error)
3516		{
3517			STFail(@"returned an error on iteration %d error=%@", i, error);
3518			CFRelease(error);
3519			return;
3520		}
3521
3522		if (NULL == transformResult || 0 == [(NSData*)transformResult length])
3523		{
3524			STFail(@"transformResult was NULL or empty for iteration %d", i);
3525			return;
3526		}
3527
3528		NSData* resultData = nil;
3529		if (CFGetTypeID(transformResult) == CFDataGetTypeID())
3530		{
3531			resultData = (NSData*)transformResult;
3532			[resultData autorelease];
3533		}
3534
3535		CFRelease(encryptSymRef);
3536		CFRelease(decryptSymRef);
3537
3538		STAssertEqualObjects(testData, resultData, @"The output of the decrypt transform does NOT match the original input iteration %d", i);
3539	});
3540
3541	CFRelease(testKey);
3542}
3543
3544static SecTransformInstanceBlock RoundTripCheck(CFStringRef name,
3545							SecTransformRef newTransform,
3546							SecTransformImplementationRef ref)
3547{
3548	SecTransformInstanceBlock instanceBlock =
3549	^{
3550		CFErrorRef result = NULL;
3551		__block CFDataRef remainder = NULL;
3552		__block SecTransformStringOrAttributeRef ahead = NULL;
3553		__block int eof_count = 0;
3554		__block bool drain = false;
3555
3556		SecTransformCustomSetAttribute(ref, CFSTR("INPUT2"), kSecTransformMetaAttributeDeferred, kCFBooleanTrue);
3557		SecTransformCustomSetAttribute(ref, CFSTR("INPUT2"), kSecTransformMetaAttributeStream, kCFBooleanTrue);
3558
3559		dispatch_block_t not_equal =
3560		^{
3561			// not equal
3562			SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, kCFBooleanFalse);
3563			SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, NULL);
3564			drain = true;
3565		};
3566
3567		SecTransformAttributeActionBlock action =
3568		^(SecTransformAttributeRef ah, CFTypeRef value)
3569		{
3570            if (drain)
3571			{
3572				return (CFTypeRef)NULL;
3573			}
3574
3575			if (ahead == ah)
3576			{
3577				SecTransformPushbackAttribute(ref, ah, value);
3578			}
3579			else if (value)
3580			{
3581				CFDataRef d = (CFDataRef)value;
3582				if (remainder)
3583				{
3584					CFIndex compare_length;
3585					CFIndex remainder_length = CFDataGetLength(remainder);
3586					CFIndex d_length = CFDataGetLength(d);
3587					CFDataRef new_remainder = NULL;
3588					SecTransformAttributeRef new_ahead = NULL;
3589
3590					if (remainder_length == d_length)
3591					{
3592						compare_length = d_length;
3593					}
3594					else if (remainder_length < d_length)
3595					{
3596						new_remainder = CFDataCreate(NULL, CFDataGetBytePtr(d) + remainder_length, d_length - remainder_length);
3597						compare_length = remainder_length;
3598						new_ahead = ah;
3599					} else
3600					{
3601						new_remainder = CFDataCreate(NULL, CFDataGetBytePtr(remainder) + d_length, remainder_length - d_length);
3602						compare_length = d_length;
3603						new_ahead = ahead;
3604					}
3605
3606					if (bcmp(CFDataGetBytePtr(d), CFDataGetBytePtr(remainder), compare_length)) {
3607						not_equal();
3608					} else
3609					{
3610						// same, keep going
3611						CFRelease(remainder);
3612						remainder = new_remainder;
3613						ahead = new_ahead;
3614					}
3615				}
3616				else
3617				{
3618					if (!eof_count)
3619					{
3620						ahead = ah;
3621						remainder = CFDataCreateCopy(NULL, d);
3622					}
3623					else
3624					{
3625						if (CFDataGetLength(d))
3626						{
3627							not_equal();
3628						}
3629					}
3630				}
3631			}
3632			else
3633			{ // EOF case
3634				ahead = NULL;
3635				if (++eof_count == 2)
3636				{
3637					if (remainder)
3638					{
3639						SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
3640								kSecTransformMetaAttributeValue,
3641								CFDataGetLength(remainder) ? kCFBooleanFalse : kCFBooleanTrue);
3642
3643						CFRelease(remainder);
3644					}
3645					else
3646					{
3647						SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
3648									kSecTransformMetaAttributeValue, kCFBooleanTrue);
3649					}
3650
3651					SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName,
3652									kSecTransformMetaAttributeValue, NULL);
3653				}
3654			}
3655
3656			return (CFTypeRef)NULL;
3657		};
3658
3659		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT2"), action);
3660		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, action);
3661
3662		return result;
3663	};
3664
3665	return Block_copy(instanceBlock);
3666}
3667
3668static BOOL RoundTrip(CFStringRef fname, SecTransformRef in, SecTransformRef out, BOOL share)
3669{
3670	static dispatch_once_t once;
3671	CFStringRef name = CFSTR("com.apple.examples.cmp");
3672	// concepts: pushback, SecTransformSetAttributeAction vs. ProcessData, ah==, & send value to output
3673
3674	dispatch_once(&once,
3675		^{
3676			SecTransformRegister(name, &RoundTripCheck, NULL);
3677		});
3678
3679	SecTransformRef cmp = SecTransformCreate(name, NULL);
3680	SecTransformRef group = SecTransformCreateGroupTransform();
3681	CFErrorRef err = NULL;
3682	SecTransformConnectTransforms(in, kSecTransformOutputAttributeName, out, kSecTransformInputAttributeName, group, NULL);
3683	SecTransformConnectTransforms(out, kSecTransformOutputAttributeName, cmp, kSecTransformInputAttributeName, group, NULL);
3684	NSInputStream *is = [NSInputStream inputStreamWithFileAtPath:(NSString *)fname];
3685	// XXX: failure to do this seem to crash SecTransformExecute when it releases the error, track down & fix or file radar
3686	[is open];
3687
3688	NSInputStream *is2 = nil;
3689
3690	if (share)
3691	{
3692		SecTransformRef tee = SecNullTransformCreate();
3693		SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, in, kSecTransformInputAttributeName, group, NULL);
3694		SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, cmp, CFSTR("INPUT2"), group, NULL);
3695		SecTransformSetAttribute(tee, kSecTransformInputAttributeName, (CFTypeRef)is, NULL);
3696		CFRelease(tee);
3697	} else {
3698		is2 = [NSInputStream inputStreamWithFileAtPath:(NSString *)fname];
3699		[is2 open];
3700		SecTransformSetAttribute(in, kSecTransformInputAttributeName, (CFTypeRef)is, &err);
3701		SecTransformSetAttribute(cmp, CFSTR("INPUT2"), (CFTypeRef)is2, &err);
3702	}
3703
3704	assert(err == NULL);
3705	CFTypeRef r = SecTransformExecute(group, &err);
3706
3707	if (err)
3708	{
3709		CFRelease(err);
3710	}
3711
3712	CFRelease(group);
3713	CFRelease(cmp);
3714
3715	if (is2)
3716	{
3717		[is2 close];
3718	}
3719
3720	if (is)
3721	{
3722		[is close];
3723	}
3724
3725	if (r)
3726	{
3727		return r == kCFBooleanTrue;
3728	}
3729	else
3730	{
3731		CFfprintf(stderr, "round trip error: %@", err);
3732		return NO;
3733	}
3734}
3735
3736static SecTransformInstanceBlock LineLengthCheck(CFStringRef name, SecTransformRef newTransform, SecTransformImplementationRef ref)
3737{
3738	SecTransformInstanceBlock instanceBlock = ^{
3739		CFErrorRef result = NULL;
3740		__block int bytesPastLastEOL = 0;
3741		__block int max = 0;
3742
3743		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("MAX"), ^(SecTransformAttributeRef ah, CFTypeRef value) {
3744			CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &max);
3745			return value;
3746		});
3747
3748		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^(SecTransformAttributeRef ah, CFTypeRef value) {
3749			if (NULL != value) {
3750				CFDataRef d = (CFDataRef)value;
3751				CFIndex len = CFDataGetLength(d);
3752				const UInt8 *bytes = CFDataGetBytePtr(d);
3753
3754				for(int i = 0; i < len; i++) {
3755					if (bytes[i] == '\n') {
3756						bytesPastLastEOL = 0;
3757					} else {
3758						bytesPastLastEOL++;
3759						if (bytesPastLastEOL > max) {
3760							SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "MAX line length of %d exceeded", max));
3761							break;
3762						}
3763					}
3764				}
3765			}
3766			SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, value);
3767			return value;
3768		});
3769
3770		return result;
3771	};
3772
3773	return Block_copy(instanceBlock);
3774}
3775
3776-(void)testLargeChunkEncode
3777{
3778	NSError *err = NULL;
3779	NSData *d = [NSData dataWithContentsOfFile:@"/usr/share/dict/web2a" options:NSDataReadingMapped error: &err];
3780	STAssertNil(err, @"dataWithContentsOfFile %@", err);
3781	CFStringRef types[] = {kSecZLibEncoding, kSecBase64Encoding, kSecBase32Encoding, NULL};
3782
3783	dispatch_group_t dg = dispatch_group_create();
3784
3785	CFStringRef lengthCheckName = CFSTR("com.apple.security.unit-test.lineLengthCheck");
3786	SecTransformRegister(lengthCheckName, LineLengthCheck, (CFErrorRef *)&err);
3787	STAssertNil(err, @"Expected to register %@", lengthCheckName);
3788
3789	for(int i = 0; types[i]; i++) {
3790		int max_j = 80;
3791		CFStringRef etype = types[i];
3792
3793		void (^trial)(NSString *testName, id lineLength, int maxLineLength) = ^(NSString *testName, id lineLength, int maxLineLength) {
3794			SecGroupTransformRef group = SecTransformCreateGroupTransform();
3795
3796			SecTransformRef et = SecEncodeTransformCreate(etype, (CFErrorRef *)&err);
3797			SecTransformRef dt = SecDecodeTransformCreate(etype, (CFErrorRef *)&err);
3798
3799			SecTransformRef lineLengthChecker = (etype == kSecZLibEncoding) ? SecNullTransformCreate() : SecTransformCreate(lengthCheckName, NULL);
3800			STAssertNotNil((id)lineLengthChecker, @"Expected to create line length checker");
3801			SecTransformSetAttribute(lineLengthChecker, CFSTR("MAX"), [NSNumber numberWithInt:maxLineLength], NULL);
3802
3803			SecTransformConnectTransforms(et, kSecTransformOutputAttributeName, lineLengthChecker, kSecTransformInputAttributeName, group, (CFErrorRef *)&err);
3804			SecTransformConnectTransforms(lineLengthChecker, kSecTransformOutputAttributeName, dt, kSecTransformInputAttributeName, group, (CFErrorRef *)&err);
3805
3806			SecTransformSetAttribute(et, kSecTransformInputAttributeName, (CFDataRef)d, (CFErrorRef *)&err);
3807			SecTransformSetAttribute(et, kSecEncodeLineLengthAttribute, lineLength, (CFErrorRef *)&err);
3808			SecTransformSetAttribute(et, CFSTR("NAME"), (CFStringRef)testName, (CFErrorRef *)&err);
3809
3810			dispatch_group_async(dg, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
3811				CFDataRef result = (CFDataRef)SecTransformExecute(group, (CFErrorRef *)&err);
3812
3813				STAssertNil(err, @"execute for %@ got %@", testName, err);
3814				STAssertNotNil((id)result, @"No result from execute of %@", testName);
3815
3816				if (result) {
3817					STAssertEqualObjects(d, (id)result, @"test %@ failed", testName);
3818				}
3819			});
3820		};
3821
3822		for(int j = max_j; j > 70; --j) {
3823			if (etype == kSecZLibEncoding && j != max_j) {
3824				break;
3825			}
3826			trial([NSString stringWithFormat:@"%@-%d", etype, j], [NSNumber numberWithInt:j], j);
3827		}
3828
3829		if (etype != kSecZLibEncoding) {
3830			trial([NSString stringWithFormat:@"%@-LL64", etype], (id)kSecLineLength64, 64);
3831			trial([NSString stringWithFormat:@"%@-LL76", etype], (id)kSecLineLength76, 76);
3832		}
3833	}
3834
3835	dispatch_group_wait(dg, DISPATCH_TIME_FOREVER);
3836}
3837
3838-(void)testZLib {
3839	SecTransformRef et = SecEncodeTransformCreate(kSecZLibEncoding, NULL);
3840	SecTransformRef dt = SecDecodeTransformCreate(kSecZLibEncoding, NULL);
3841
3842	// using a tee would require >10 buffered items (we need to buffer about 64K), so we pass share=NO
3843	STAssertTrue(RoundTrip((CFStringRef)@"/usr/share/dict/web2a", et, dt, NO), @"Roundtrip /usr/share/dict/web2a");
3844
3845	CFRelease(et);
3846	CFRelease(dt);
3847
3848	/*
3849	If we want this we need a 'new' custom transform that will get receive the ratio data and be able to
3850	query that data.
3851
3852	CFNumberRef r1 = (CFNumberRef)SecTransformGetAttribute(et, kSecCompressionRatio);
3853	CFNumberRef r2 = (CFNumberRef)SecTransformGetAttribute(dt, kSecCompressionRatio);
3854
3855	STAssertNotNil((NSNumber *)r1, @"encode ratio");
3856	STAssertNotNil((NSNumber *)r2, @"decode ratio");
3857	STAssertEqualObjects((NSNumber *)r1, (NSNumber *)r2, @"same ratios");
3858	*/
3859}
3860
3861static SecTransformInstanceBlock CycleCheckTest(CFStringRef name,
3862							SecTransformRef newTransform,
3863							SecTransformImplementationRef ref)
3864{
3865	SecTransformInstanceBlock instanceBlock =
3866	^{
3867		CFErrorRef result = NULL;
3868		int zero = 0;
3869		__block CFNumberRef feedback = CFNumberCreate(NULL, kCFNumberIntType, &zero);
3870
3871		SecTransformCustomSetAttribute(ref, CFSTR("FEEDBACK"), kSecTransformMetaAttributeCanCycle, kCFBooleanTrue);
3872
3873		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"),
3874			^(SecTransformAttributeRef ah, CFTypeRef value)
3875			{
3876				if (value == NULL) {
3877					SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, value);
3878					return value;
3879				}
3880				if (feedback == NULL) {
3881					SecTransformPushbackAttribute(ref, ah, value);
3882					return (CFTypeRef)NULL;
3883				}
3884
3885				int x, y;
3886				CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &x);
3887				CFNumberGetValue(feedback, kCFNumberIntType, &y);
3888				x ^= y;
3889				CFNumberRef res = CFNumberCreate(NULL, kCFNumberIntType, &x);
3890				SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, res);
3891				CFRelease(res);
3892				CFRelease(feedback);
3893				feedback = NULL;
3894				return (CFTypeRef)NULL;
3895			});
3896
3897		SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("FEEDBACK"),
3898			^(SecTransformAttributeRef ah, CFTypeRef value)
3899			{
3900				if (value) {
3901					if (feedback) {
3902						SecTransformPushbackAttribute(ref, ah, value);
3903					} else {
3904						feedback = (CFNumberRef)CFRetain(value);
3905					}
3906				}
3907
3908				return (CFTypeRef)NULL;
3909			});
3910
3911		return result;
3912
3913	};
3914
3915	return Block_copy(instanceBlock);
3916}
3917
3918-(void)testCycleCheck {
3919
3920	SecTransformRef cat = SecNullTransformCreate();
3921	SecTransformRef group = SecTransformCreateGroupTransform();
3922	CFErrorRef err = NULL;
3923
3924	CFStringRef name = CFSTR("com.apple.examples.unit-test.loop-test");
3925
3926	SecTransformRegister(name, &CycleCheckTest, NULL);
3927
3928	SecTransformRef twenty = count_transform(20);
3929
3930	// this is getting an internal error, but it's being ignored.
3931	SecTransformRef xxor = SecTransformCreate(name, &err);
3932
3933	SecTransformConnectTransforms(xxor, CFSTR("OUTPUT"), cat, CFSTR("INPUT"), group, &err);
3934	STAssertNil((id)err, @"xor->cat");
3935	SecTransformConnectTransforms(xxor, CFSTR("OUTPUT"), xxor, CFSTR("FEEDBACK"), group, &err);
3936	STAssertNil((id)err, @"xor->xor");
3937	SecTransformConnectTransforms(twenty, CFSTR("OUTPUT"), xxor, CFSTR("INPUT"), group, &err);
3938	STAssertNil((id)err, @"twenty->xor");
3939
3940	//SecTransformSetAttribute(xxor, CFSTR("DEBUG"), kCFBooleanTrue, &err);
3941
3942	CFTypeRef r = SecTransformExecute(group, &err);
3943	STAssertNil((id)err, @"execute err=%@", err);
3944	STAssertNotNil((id)r, @"no results from execute");
3945
3946	if (r) {
3947		CFNumberRef z = (CFNumberRef)[NSNumber numberWithInt:0];
3948		CFIndex n = CFArrayGetCountOfValue((CFArrayRef)r, CFRangeMake(0, CFArrayGetCount((CFArrayRef)r)), z);
3949		// There should be six zeros in the xor->feedback chain from 0 to 19.
3950		STAssertEquals(n, (CFIndex) 6, @"There should be six zeros in %@", r);
3951	}
3952
3953	CFRelease(r);
3954	CFRelease(group);
3955	CFRelease(twenty);
3956	CFRelease(xxor);
3957	CFRelease(cat);
3958}
3959
3960-(void)testValidate {
3961	SecTransformRef group = SecTransformCreateGroupTransform();
3962	CFErrorRef err = NULL;
3963
3964	CFStringRef data_or_null_name = CFSTR("com.apple.examples.unit-test.data-or-null");
3965	SecTransformCreateBlock data_or_null = ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params) {
3966		params->overrideAttribute(kSecTransformActionAttributeValidation, CFSTR("INPUT"), SecTransformCreateValidatorForCFtype(CFDataGetTypeID(), YES));
3967	};
3968
3969
3970	SecTransformRef makes_numbers = count_transform(20);
3971	SecTransformRef wants_data = custom_transform(data_or_null_name, data_or_null);
3972
3973	SecTransformConnectTransforms(makes_numbers, CFSTR("OUTPUT"), wants_data, CFSTR("INPUT"), group, NULL);
3974	STAssertNil((id)err, @"unexpected connect error: %@", err);
3975	CFTypeRef r = SecTransformExecute(group, &err);
3976	STAssertNil((id)r, @"Got non-null result (%@) when expecting null!", r);
3977	STAssertNotNil((id)err, @"Expected an error!", err);
3978	STAssertErrorHas((id)err, @"/INPUT", @"Error indicated attribute that was set incorrectly");
3979	STAssertErrorHas((id)err, @" type CFNumber", @"Error indicated provided type");
3980	STAssertErrorHas((id)err, @" a CFData", @"Error indicated required type");
3981
3982	if (err) {
3983		CFRelease(err);
3984	}
3985	err = NULL;
3986	CFRelease(wants_data);
3987
3988	wants_data = custom_transform(data_or_null_name, data_or_null);
3989
3990	char raw_data[] = "`Twas brillig, and the slithy toves / Did gyre and gimble in the wabe: / All mimsy were the borogoves, / And the mome raths outgrabe.";
3991	CFDataRef the_data = CFDataCreate(NULL, (UInt8*)raw_data, strlen(raw_data));
3992	SecTransformSetAttribute(wants_data, kSecTransformInputAttributeName, the_data, &err);
3993	CFRelease(the_data);
3994
3995	STAssertNil((id)err, @"unexpected set error: %@", err);
3996	r = SecTransformExecute(wants_data, &err);
3997	STAssertNotNil((id)r, @"Expected a result, got error: %@", err);
3998	if (r) {
3999		STAssertEqualObjects((id)the_data, (id)r, @"Invalid result");
4000	}
4001
4002	CFStringRef numbers_only_name = CFSTR("com.apple.examples.unit-test.numbers-only");
4003	SecTransformCreateBlock numbers_only = ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params) {
4004		params->overrideAttribute(kSecTransformActionAttributeValidation, CFSTR("INPUT"), SecTransformCreateValidatorForCFtype(CFNumberGetTypeID(), NO));
4005	};
4006
4007	CFRelease(group);
4008	CFRelease(makes_numbers);
4009	CFRelease(wants_data);
4010
4011	group = SecTransformCreateGroupTransform();
4012	makes_numbers = count_transform(20);
4013	SecTransformRef wants_numbers = custom_transform(numbers_only_name, numbers_only);
4014
4015	SecTransformConnectTransforms(makes_numbers, CFSTR("OUTPUT"), wants_numbers, CFSTR("INPUT"), group, NULL);
4016	STAssertNil((id)err, @"unexpected connect error: %@", err);
4017	r = SecTransformExecute(group, &err);
4018	CFfprintf(stderr, "r=%@; err=%@\n", r, err);
4019	STAssertNil((id)r, @"Got non-null result (%@) when expecting null!", r);
4020	STAssertNotNil((id)err, @"Expected an error!", err);
4021	STAssertErrorHas((id)err, @"/INPUT", @"Error indicated attribute that was set incorrectly");
4022	STAssertErrorHas((id)err, @"received NULL value", @"Error indicated provided value is NULL");
4023	STAssertErrorHas((id)err, @" a CFNumber", @"Error indicated required type");
4024
4025	CFRelease(err);
4026	CFRelease(group);
4027	CFRelease(makes_numbers);
4028	CFRelease(wants_numbers);
4029}
4030
4031-(void)testCodeBase32 {
4032	struct base32_test_vector {
4033		const char *plain_text;
4034		const char *base32_rfc4648;
4035		const char *base32_fde;
4036	};
4037
4038	// RFC 4648 test vectors
4039	static base32_test_vector base32_test_vectors[] = {
4040		{"", "", ""},
4041		{"f", "MY======", "MY======"},
4042		{"fo", "MZXQ====", "MZXQ===="},
4043		{"foo", "MZXW6===", "MZXW6==="},
4044		{"foob", "MZXW6YQ=", "MZXW6YQ="},
4045		{"fooba", "MZXW6YTB", "MZXW6YTB"},
4046		{"foobar", "MZXW6YTBOI======", "MZXW6YTBO8======"}};
4047
4048	void (^test)(NSString *test_name, SecTransformRef transform, const char *input, const char *expected_output, NSString *error_format) =
4049	^(NSString *test_name, SecTransformRef transform, const char *input, const char *expected_output, NSString *error_format)
4050	{
4051		if (!transform) {
4052			STFail(@"No transform for %@", test_name);
4053			return;
4054		}
4055
4056		CFErrorRef err = NULL;
4057		NSData *input_data = [NSData dataWithBytes:input length:strlen(input)];
4058		NSData *expected_output_data = [NSData dataWithBytes:expected_output length:strlen(expected_output)];
4059		SecTransformSetAttribute(transform, kSecTransformInputAttributeName, input_data, &err);
4060		STAssertNil((NSError *)err, @"unexpected error %@ from SecTransformSetAttribute for %@", err, test_name);
4061		NSData *output_data = (NSData *)SecTransformExecute(transform, &err);
4062		[output_data autorelease];
4063		STAssertNil((NSError *)err, @"Error from %@ execute (in=%s, err=%s)", test_name, input, err);
4064		STAssertNotNil(output_data, @"Unexpected nil output from %@ execute (in=%s)", test_name, input);
4065		if (output_data) {
4066			NSString *output_string = [NSString alloc];
4067			output_string = [output_string initWithBytes:[output_data bytes] length:[output_data length] encoding:NSMacOSRomanStringEncoding];
4068			[output_string autorelease];
4069			NSString *msg = [NSString stringWithFormat:error_format, input, expected_output, output_string];
4070			STAssertEqualObjects(expected_output_data, output_data, @"%@ %@", test_name, msg);
4071		}
4072		CFRelease(transform);
4073	};
4074
4075	for(int idx = 0; idx < sizeof(base32_test_vectors)/sizeof(*base32_test_vectors); idx++)
4076	{
4077		SecTransformRef base32encoder = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
4078		test(@"base32 encode", base32encoder, base32_test_vectors[idx].plain_text, base32_test_vectors[idx].base32_rfc4648, @"B32(\"%1$s\") should be \"%2$s\", got \"%3$@\"");
4079
4080		SecTransformRef base32decoder = SecDecodeTransformCreate(kSecBase32Encoding, NULL);
4081		test(@"base32 decode", base32decoder, base32_test_vectors[idx].base32_rfc4648, base32_test_vectors[idx].plain_text, @"B32dec(\"%1$s\") should be \"%2$s\", got \"%3$@\"");
4082
4083		SecTransformRef base32FDEencoder = SecEncodeTransformCreate(CFSTR("base32FDE"), NULL);
4084		test(@"base32FDE encode", base32FDEencoder, base32_test_vectors[idx].plain_text, base32_test_vectors[idx].base32_fde, @"B32(\"%1$s\") should be \"%2$s\", got \"%3$@\"");
4085
4086		SecTransformRef base32FDEdecoder = SecDecodeTransformCreate(CFSTR("base32FDE"), NULL);
4087		test(@"base32FDE decode", base32FDEdecoder, base32_test_vectors[idx].base32_fde, base32_test_vectors[idx].plain_text, @"B32dec(\"%1$s\") should be \"%2$s\", got \"%3$@\"");
4088	}
4089
4090	SecTransformRef bet = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
4091	STAssertNotNil((id)bet, @"got bulk base 32 encoder");
4092	SecTransformRef bdt = SecDecodeTransformCreate(kSecBase32Encoding, NULL);
4093	STAssertNotNil((id)bdt, @"got bulk base 32 decoder");
4094	STAssertTrue(RoundTrip((CFStringRef)@"/usr/share/dict/words", bet, bdt, YES), @"Roundtrip base32 /usr/share/dict/words");
4095
4096	CFRelease(bet);
4097	CFRelease(bdt);
4098
4099	// FDE uses a modified base32 alphabet, we want to test it here.
4100	SecTransformRef FDE_encode_transform = SecEncodeTransformCreate(@"base32FDE", NULL);
4101	STAssertNotNil((id)FDE_encode_transform, @"got FDE encoder");
4102	SecTransformRef FDE_decode_transform = SecDecodeTransformCreate(@"base32FDE", NULL);
4103	STAssertNotNil((id)FDE_decode_transform, @"got bulk base 32 decoder");
4104	STAssertTrue(RoundTrip((CFStringRef)@"/usr/share/dict/words", FDE_encode_transform, FDE_decode_transform, YES), @"Roundtrip base32FDE /usr/share/dict/words");
4105
4106	CFRelease(FDE_encode_transform);
4107	CFRelease(FDE_decode_transform);
4108}
4109
4110-(void)testCodeBase64 {
4111	CFErrorRef error = NULL;
4112
4113#if 0
4114	SecTransformRef tr = SecDecodeTransformCreate(@"Not a real encoding", &error);
4115	// XXX: known failure in Transform::SetAttribute 7707822 -- I would fix on this branch, but I think that code has diverged
4116	STAssertTrue(tr == NULL, @"Checks for invalid encodings");
4117	NSLog(@"Error: %@", error);
4118#endif
4119
4120	SecTransformRef dt = SecDecodeTransformCreate(kSecBase64Encoding, NULL);
4121	STAssertNotNil((id)dt, @"Got decoder");
4122
4123	const char raw_data0[] = "Tm90IHV1ZW5jb2RlZAo=";
4124	const char raw_data1[] = "Not uuencoded\n";
4125	CFDataRef data0 = CFDataCreate(NULL, (const UInt8*)raw_data0, strlen(raw_data0));
4126	CFDataRef data1 = CFDataCreate(NULL, (const UInt8*)raw_data1, strlen(raw_data1));
4127    SecTransformSetAttribute(dt, kSecTransformInputAttributeName, data0, NULL);
4128
4129    CFDataRef decoded_data = (CFDataRef)SecTransformExecute(dt, &error);
4130    STAssertNotNil((NSData *)decoded_data, @"Got a decode result");
4131    STAssertEqualObjects((NSData *)decoded_data, (NSData *)data1, @"Proper decode results");
4132
4133	SecTransformRef et = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
4134	STAssertNotNil((id)et, @"Got encoder");
4135
4136	SecTransformSetAttribute(et, kSecTransformInputAttributeName, data1, NULL);
4137
4138	CFDataRef encoded_data = (CFDataRef)SecTransformExecute(et, NULL);
4139	STAssertNotNil((NSData *)encoded_data, @"Got an encode result");
4140
4141	STAssertEqualObjects((NSData *)encoded_data, (NSData *)data0, @"Proper encode results");
4142
4143    // Negative testing, assume the following struct
4144    //   struct __CFData {
4145    //        CFRuntimeBase _base;
4146    //        CFIndex _length;    /* number of bytes */
4147    //        CFIndex _capacity;    /* maximum number of bytes */
4148    //        CFAllocatorRef _bytesDeallocator;    /* used only for immutable; if NULL, no deallocation */
4149    //        uint8_t *_bytes;    /* compaction: direct access to _bytes is only valid when data is not inline */
4150    //        };
4151    SecTransformRef et_neg = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
4152    STAssertNotNil((id)et_neg, @"Got encoder for negative testing");
4153
4154    CFIndex *data1_length=(CFIndex*)((unsigned char *)data1 + sizeof(CFRuntimeBase));
4155    CFIndex data1_backup=*data1_length;
4156    if (sizeof(CFIndex)==8)
4157    {
4158        *data1_length=0x5ffffffffffffff7;
4159    }
4160    else if (sizeof(CFIndex)==4)
4161    {
4162        *data1_length=0x5ffffff7;
4163    }
4164    else
4165    {
4166        STAssertTrue(false, @"Test error, representation of CFIndex not supported. Sizeof(CFIndex)=%d. Only support 4 or 8",sizeof(CFIndex));
4167    }
4168    STAssertTrue(CFDataGetLength(data1)==*data1_length, @"Length not properly set - test bug - reads %lu expect %lu",CFDataGetLength(data1),*data1_length);
4169    SecTransformSetAttribute(et_neg, kSecTransformInputAttributeName, data1, NULL);
4170    CFDataRef encoded_data2 = (CFDataRef)SecTransformExecute(et_neg, &error);
4171    STAssertNil((id)encoded_data2, @"No encoded data for negative testing");
4172    STAssertNotNil((id)error, @"Got error for negative testing");
4173    *data1_length=data1_backup;
4174    if (error!=NULL)
4175    {
4176        STAssertTrue((CFErrorGetCode(error)==kSecTransformErrorInvalidLength),
4177                 @"Error for invalid length, got %lu expect %lu",CFErrorGetCode(error),kSecTransformErrorInvalidLength);
4178    }
4179	// XXX also for general testing we want a "RandomChunkSizer" that copies INPUT to OUTPUT, but makes random size chunks (incl 0) as it goes.
4180
4181	SecTransformRef dt2 = SecDecodeTransformCreate(kSecBase64Encoding, NULL);
4182	SecTransformRef et2 = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
4183	int ll = 75;
4184	SecTransformSetAttribute(et2, kSecEncodeLineLengthAttribute, CFNumberCreate(NULL, kCFNumberIntType, &ll), NULL);
4185
4186	STAssertTrue(RoundTrip((CFStringRef)@"/usr/share/dict/words", et2, dt2, YES), @"Roundtrip base64 /usr/share/dict/words");
4187
4188	CFRelease(et2);
4189	CFRelease(dt2);
4190    CFRelease(et);
4191    CFRelease(et_neg);
4192    CFRelease(dt);
4193}
4194
4195static SecTransformInstanceBlock ErrorResultsTest(CFStringRef name,
4196							SecTransformRef newTransform,
4197							SecTransformImplementationRef ref)
4198{
4199	SecTransformInstanceBlock instanceBlock =
4200	^{
4201		CFErrorRef result = NULL;
4202		SecTransformSetDataAction(ref, kSecTransformActionProcessData,
4203		^(CFTypeRef value)
4204		{
4205			if (value != NULL)
4206			{
4207				return (CFTypeRef)CFErrorCreate(NULL, CFSTR("expected error"), 42, NULL);
4208			}
4209			else
4210			{
4211				return SecTransformNoData();
4212			}
4213		});
4214
4215		return result;
4216	};
4217
4218	return Block_copy(instanceBlock);
4219}
4220
4221
4222-(void)testErrorResults {
4223	CFStringRef name = CFSTR("com.apple.security.unit-test.error-results");
4224	SecTransformRegister(name, &ErrorResultsTest, NULL);
4225
4226	SecTransformRef tr = SecTransformCreate(name, NULL);
4227	CFDataRef data = CFDataCreate(NULL, NULL, 0);
4228	SecTransformSetAttribute(tr, kSecTransformInputAttributeName, data, NULL);
4229
4230	CFErrorRef err = NULL;
4231	CFTypeRef no_result = SecTransformExecute(tr, &err);
4232
4233	STAssertErrorHas((id)err, @"expected error", @"Signaled error has original string");
4234	STAssertErrorHas((id)err, @"42", @"Signaled error has original error code");
4235	STAssertNil((id)no_result, @"No result from erroring transform");
4236	CFRelease(data);
4237	CFRelease(tr);
4238	CFRelease(err);
4239}
4240
4241-(void)testErrorExecutesInRightQueue {
4242	// testExecuteBlock checks to see if blocks are generally executed on the proper queue, this specifically checks
4243	// for an error while starting (which was originally improperly coded).
4244
4245	SecTransformRef unassigned_input = SecNullTransformCreate();
4246	dispatch_queue_t q = dispatch_queue_create("com.apple.unit-test.ErrorExecutesInRightQueue", NULL);
4247	dispatch_group_t got_final = dispatch_group_create();
4248	dispatch_group_enter(got_final);
4249	__block bool saw_data = false;
4250	__block bool saw_error = false;
4251
4252	SecTransformExecuteAsync(unassigned_input, q, ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
4253		STAssertEquals(q, const_cast<const dispatch_queue_t>(dispatch_get_current_queue()), @"Should be running on %p, but is running on %p", q, dispatch_get_current_queue());
4254		saw_data = saw_data || (message != NULL);
4255		saw_error = saw_error || (error != NULL);
4256		if (isFinal) {
4257			dispatch_group_leave(got_final);
4258		}
4259	});
4260
4261	STAssertFalse(dispatch_group_wait(got_final, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"Execute completed");
4262	STAssertFalse(saw_data, @"Should have seen no data (but did)");
4263	STAssertTrue(saw_error, @"Should have seen error (but didn't)");
4264
4265	CFRelease(unassigned_input);
4266	dispatch_group_notify(got_final, q, ^{
4267		dispatch_release(got_final);
4268		dispatch_release(q);
4269	});
4270
4271}
4272
4273-(void)testSignVerify {
4274	unsigned char *raw_message = (unsigned char *)"Controlling complexity is the essence of computer programming. - Brian Kernigan";
4275	dispatch_group_t dg = dispatch_group_create();
4276	CFErrorRef err = NULL;
4277	CFDataRef message = CFDataCreate(NULL, raw_message, strlen((const char *)raw_message));
4278	__block SecKeyRef rsa_pub_key = NULL;
4279	__block SecKeyRef rsa_priv_key = NULL;
4280	__block SecKeyRef ecdsa_pub_key = NULL;
4281	__block SecKeyRef ecdsa_priv_key = NULL;
4282	__block SecKeyRef dsa_pub_key = NULL;
4283	__block SecKeyRef dsa_priv_key = NULL;
4284
4285	char *tmp_dir;
4286	asprintf(&tmp_dir, "%s/sign-verify-test-keychain-", getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp");
4287
4288	unsigned char *raw_bad_message = (unsigned char *)"Standards are great, there are so many to choose from - Andrew S. Tanenbaum (maybe)";
4289	CFDataRef bad_message = CFDataCreate(NULL, raw_bad_message, strlen((const char *)raw_bad_message));
4290
4291	// when safe replace with a concurrent queue
4292	dispatch_queue_t key_q = dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4293
4294	dispatch_group_async(dg, key_q,
4295	^{
4296		NSAutoreleasePool *pool = [NSAutoreleasePool new];
4297
4298		// (note the key must be bigger then a SHA2-256 signature plus the DER.1 packing of the OID, so 1024 was chosen for that, not speed or safety)
4299		NSDictionary *key_opts = [NSDictionary dictionaryWithObjectsAndKeys:
4300								  (NSString *)kSecAttrKeyTypeRSA, (NSString *)kSecAttrKeyType,
4301								  [NSNumber numberWithInt:1024], (NSString *)kSecAttrKeySizeInBits,
4302								  @"RSA transform unit test key", (NSString *)kSecAttrLabel,
4303								  nil];
4304		OSStatus gp_status = SecKeyGeneratePair((CFDictionaryRef)key_opts, &rsa_pub_key, &rsa_priv_key);
4305		STAssertTrue(gp_status == 0, @"RSA (gp_status=0x%x)", gp_status);
4306		[pool drain];
4307	});
4308
4309	dispatch_group_async(dg, key_q,
4310	^{
4311		OSStatus gp_status;
4312#if 0
4313		// I don't know how "safe" a 512 bit ECDSA key is, but again this is just for testing, not for signing any real data
4314		NSDictionary *key_opts = [NSDictionary dictionaryWithObjectsAndKeys:
4315								  (NSString *)kSecAttrKeyTypeECDSA, (NSString *)kSecAttrKeyType,
4316								  [NSNumber numberWithInt:512], (NSString *)kSecAttrKeySizeInBits,
4317								  @"ECDSA transform unit test key", (NSString *)kSecAttrLabel,
4318								  nil];
4319		gp_status = SecKeyGeneratePair((CFDictionaryRef)key_opts, &ecdsa_pub_key, &ecdsa_priv_key);
4320#else
4321		{
4322			SecKeychainRef tmp_keychain = NULL;
4323			gp_status = SecKeyCreatePair(tmp_keychain, CSSM_ALGID_ECDSA, 256, NULL,
4324				CSSM_KEYUSE_VERIFY,
4325				CSSM_KEYATTR_EXTRACTABLE|CSSM_KEYATTR_PERMANENT,
4326				CSSM_KEYUSE_SIGN,
4327				CSSM_KEYATTR_EXTRACTABLE|CSSM_KEYATTR_PERMANENT,
4328				NULL, &ecdsa_pub_key, &ecdsa_priv_key);
4329		}
4330#endif
4331		if (gp_status)
4332		{
4333			STAssertTrue(gp_status == 0, @"ECDSA (gp_status=0x%x)", gp_status);
4334		}
4335	});
4336
4337	dispatch_group_async(dg, key_q,
4338	^{
4339		OSStatus gp_status;
4340#if 0
4341		// I don't know how "safe" a 1024 bit DSA key is, but again this is just for testing, not for signing any real data
4342		NSDictionary *key_opts = [NSDictionary dictionaryWithObjectsAndKeys:(NSString *)kSecAttrKeyTypeDSA,
4343					(NSString *)kSecAttrKeyType, [NSNumber numberWithInt:512], (NSString *)kSecAttrKeySizeInBits, nil];
4344		gp_status = SecKeyGeneratePair((CFDictionaryRef)key_opts, &ecdsa_pub_key, &ecdsa_priv_key);
4345#else
4346		{
4347			const char *passwd = "this is not secret";
4348			SecKeychainRef tmp_keychain = NULL;
4349			char *kcfname;
4350			asprintf(&kcfname, "%s-DSA-XXXXXXXXXX", tmp_dir);
4351			// NOTE: "mktemp" isn't as safe as you might think...but this is test code and doesn't have to be, but
4352			// if you copy it elsewhere you may well need to rewrite it.  (use mkstemp)
4353			mktemp(kcfname);
4354			gp_status = SecKeychainCreate(kcfname, (UInt32) strlen(passwd), passwd, NO, NULL, &tmp_keychain);
4355			STAssertTrue(gp_status == 0, @"SecKeychainCreate (gp_status=0x%x)", gp_status);
4356			gp_status = SecKeyCreatePair(tmp_keychain, CSSM_ALGID_DSA, 512, NULL,
4357				CSSM_KEYUSE_VERIFY|CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_WRAP,
4358				CSSM_KEYATTR_EXTRACTABLE|CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_RETURN_REF,
4359				CSSM_KEYUSE_SIGN|CSSM_KEYUSE_DECRYPT|CSSM_KEYUSE_UNWRAP,
4360				CSSM_KEYATTR_EXTRACTABLE|CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_RETURN_REF,
4361				NULL, &dsa_pub_key, &dsa_priv_key);
4362			free(kcfname);
4363		}
4364#endif
4365		STAssertTrue(gp_status == 0, @"DSA (gp_status=0x%x)", gp_status);
4366	});
4367
4368	struct sv_test {
4369		NSString *name;
4370		SecKeyRef pub_key, priv_key;
4371		CFDataRef msg_sign, msg_verify;
4372		CFTypeRef dalgo_sign, dalgo_verify;
4373		int dlen_sign, dlen_verify;
4374		BOOL pass;
4375	};
4376
4377	dispatch_group_wait(dg, DISPATCH_TIME_FOREVER);
4378
4379	struct sv_test sv_tests[] =
4380	{
4381		{@"Basic RSA", rsa_pub_key, rsa_priv_key, message, message, NULL, NULL, 0, 0, YES},
4382		{@"Basic RSA (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, NULL, NULL, 0, 0, NO},
4383		{@"RSA, mismatched digest algos", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA1, 0, 0, NO},
4384
4385		{@"RSA SHA1 MD5", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, YES},
4386		{@"RSA SHA1 MD5 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, NO},
4387
4388		{@"RSA MD5", rsa_pub_key, rsa_priv_key, message, message, kSecDigestMD5, kSecDigestMD5, 0, 0, YES},
4389		{@"RSA MD5 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestMD5, kSecDigestMD5, 0, 0, NO},
4390
4391		{@"RSA MD2", rsa_pub_key, rsa_priv_key, message, message, kSecDigestMD2, kSecDigestMD2, 0, 0, YES},
4392		{@"RSA MD2 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestMD2, kSecDigestMD2, 0, 0, NO},
4393
4394		{@"RSA SHA2 512", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 512, 512, YES},
4395		{@"RSA SHA2 512 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 512, 512, NO},
4396		{@"RSA SHA2 512 vs. 384", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 512, 384, NO},
4397
4398		{@"RSA SHA2 384", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 384, 384, YES},
4399		{@"RSA SHA2 384 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 384, 384, NO},
4400
4401		{@"RSA SHA2 256", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 256, 256, YES},
4402		{@"RSA SHA2 256 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 256, 256, NO},
4403
4404		{@"RSA SHA2 224", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 224, 224, YES},
4405		{@"RSA SHA2 224 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 224, 224, NO},
4406
4407		{@"RSA SHA2 0", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 0, 0, YES},
4408		{@"RSA SHA2 0 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 0, 0, NO},
4409
4410		{@"Basic ECDSA", ecdsa_pub_key, ecdsa_priv_key, message, message, NULL, NULL, 0, 0, YES},
4411		{@"Basic ECDSA (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, NULL, NULL, 0, 0, NO},
4412		{@"ECDSA (mismatched digest algos)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA1, 0, 0, NO},
4413
4414		{@"ECDSA SHA1", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, YES},
4415		{@"ECDSA SHA1 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, NO},
4416
4417		{@"ECDSA SHA2 224", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 224, 224, YES},
4418		{@"ECDSA SHA2 224 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 224, 224, NO},
4419
4420		{@"ECDSA SHA2 256", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 256, 256, YES},
4421		{@"ECDSA SHA2 256 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 256, 256, NO},
4422
4423		{@"ECDSA SHA2 384", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 384, 384, YES},
4424		{@"ECDSA SHA2 384 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 384, 384, NO},
4425
4426		{@"ECDSA SHA2 512", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 512, 512, YES},
4427		{@"ECDSA SHA2 512 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 512, 512, NO},
4428
4429		{@"ECDSA SHA2 0", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 0, 0, YES},
4430		{@"ECDSA SHA2 0 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 0, 0, NO},
4431
4432		{@"Basic DSA", dsa_pub_key, dsa_priv_key, message, message, NULL, NULL, 0, 0, YES},
4433		{@"Basic DSA (tampered data)", dsa_pub_key, dsa_priv_key, message, bad_message, NULL, NULL, 0, 0, NO},
4434		// only SHA1 is supported, so no mismatched digest algo test is available
4435
4436		{@"DSA SHA1", dsa_pub_key, dsa_priv_key, message, message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, YES},
4437		{@"DSA SHA1 (tampered data)", dsa_pub_key, dsa_priv_key, message, bad_message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, NO},
4438	};
4439
4440	free(tmp_dir);
4441
4442	int i;
4443	for(i = 0; i < sizeof(sv_tests)/sizeof(sv_test); i++)
4444	{
4445		CFStringRef input_cases[] = {kSecInputIsPlainText, kSecInputIsDigest};
4446		//CFStringRef input_cases[] = {kSecInputIsPlainText, kSecInputIsDigest, kSecInputIsRaw};
4447		const int ilim = sizeof(input_cases)/sizeof(input_cases[0]);
4448		int ii = 0, ij = 0;
4449		for(; ii < ilim; ++ii)
4450		{
4451			for(ij = 0; ij < ilim; ++ij)
4452			{
4453				err = NULL;
4454				struct sv_test *tst = sv_tests + i;
4455				NSString *tname = [NSString stringWithFormat:@"%@ %@ %@", tst->name, input_cases[ii], input_cases[ij]];
4456
4457				CFStringRef sign_input_is = input_cases[ii];
4458				CFStringRef verify_input_is = input_cases[ij];
4459
4460				if (sign_input_is != kSecInputIsPlainText && tst->dalgo_sign == NULL) {
4461					continue;
4462				}
4463				if (verify_input_is != kSecInputIsPlainText && tst->dalgo_verify == NULL) {
4464					continue;
4465				}
4466
4467				if ((sign_input_is == kSecInputIsRaw || verify_input_is == kSecInputIsRaw) && [tst->name rangeOfString:@"RSA"].location == NSNotFound) {
4468					// we can only synthesize these tests for RSA
4469					NSLog(@"No %@ test", tname);
4470					continue;
4471				}
4472
4473				STAssertNotNil((id)tst->pub_key, @"Have pub_key for %@", tname);
4474				STAssertNotNil((id)tst->priv_key, @"Have priv_key for %@", tname);
4475
4476				if (tst->pub_key == nil || tst->priv_key == nil) {
4477					continue;
4478				}
4479
4480				SecTransformRef sign = SecSignTransformCreate(tst->priv_key, &err);
4481				STAssertNil((NSError *)err, @"creating sign for %@", tname);
4482				STAssertNotNil((id)sign, @"Creating sign for %@", tname);
4483
4484				if (sign == NULL) {
4485					continue;
4486				}
4487
4488				SecTransformRef verify = SecVerifyTransformCreate(tst->pub_key, NULL, &err);
4489				STAssertNotNil((id)verify, @"Creating verify for %@", tname);
4490				STAssertNil((NSError *)err, @"Creating verify for %@", tname);
4491
4492				if (verify == NULL) {
4493					continue;
4494				}
4495
4496				SecTransformRef sign_digest = NULL;
4497				SecTransformRef verify_digest = NULL;
4498				SecTransformRef sign2 = NULL;
4499
4500				if (tst->dalgo_sign)
4501				{
4502					SecTransformSetAttribute(sign, kSecDigestTypeAttribute, tst->dalgo_sign, &err);
4503					STAssertNil((NSError *)err, @"Setting sign's digest type for %@", tname);
4504					SecTransformSetAttribute(sign, kSecDigestLengthAttribute, [NSNumber numberWithInt:tst->dlen_sign], &err);
4505					STAssertNil((NSError *)err, @"Setting sign's digest length for %@", tname);
4506
4507					if (sign_input_is == kSecInputIsDigest)
4508					{
4509						sign_digest = SecDigestTransformCreate(tst->dalgo_sign, tst->dlen_sign, &err);
4510						STAssertNotNil((id)sign_digest, @"Create sign's %@-%d digest transform (for %@)", tst->dalgo_sign, tst->dlen_sign, tname);
4511						STAssertNil((NSError *)err, @"Making sign's digester (for %@) - err=%@", tname, err);
4512
4513						SecTransformSetAttribute(sign, kSecInputIsAttributeName, sign_input_is, &err);
4514						STAssertNil((NSError *)err, @"Setting sign's InputIs (for %@) - err=%@", tname, err);
4515					}
4516				}
4517
4518				if (tst->dalgo_verify) {
4519					SecTransformSetAttribute(verify, kSecDigestTypeAttribute, tst->dalgo_verify, &err);
4520					STAssertNil((NSError *)err, @"Setting verify's digest type for %@", tname);
4521					SecTransformSetAttribute(verify, kSecDigestLengthAttribute, [NSNumber numberWithInt:tst->dlen_verify], &err);
4522					STAssertNil((NSError *)err, @"Setting verify's digest length for %@", tname);
4523
4524					if (verify_input_is == kSecInputIsDigest) {
4525						verify_digest = SecDigestTransformCreate(tst->dalgo_verify, tst->dlen_verify, &err);
4526						STAssertNotNil((id)verify_digest, @"Create verify's %@-%d digest transform (for %@)", tst->dalgo_verify, tst->dlen_verify, tname);
4527						STAssertNil((NSError *)err, @"Making verify's digester (for %@) - err=%@", tname, err);
4528
4529						SecTransformSetAttribute(verify, kSecInputIsAttributeName, verify_input_is, &err);
4530						STAssertNil((NSError *)err, @"Setting verify's InputIs (for %@) - err=%@", tname, err);
4531					}
4532				}
4533
4534				SecGroupTransformRef group = SecTransformCreateGroupTransform();
4535				SecTransformSetAttribute(sign_digest ? sign_digest : sign, kSecTransformInputAttributeName, tst->msg_sign, (CFErrorRef *)&err);
4536				if (sign_digest) {
4537					STAssertNil((NSError *)err, @"Setting sign's digest's input for %@", tname);
4538					SecTransformConnectTransforms(sign_digest, kSecTransformOutputAttributeName,
4539									sign, kSecTransformInputAttributeName, group, NULL);
4540				} else {
4541					STAssertNil((NSError *)err, @"Setting sign's input for %@", tname);
4542				}
4543
4544
4545				SecTransformSetAttribute(verify_digest ? verify_digest : verify, kSecTransformInputAttributeName, tst->msg_verify, (CFErrorRef *)&err);
4546				if (verify_digest) {
4547					STAssertNil((NSError *)err, @"Setting verify's digest's input for %@", tname);
4548					SecTransformConnectTransforms(verify_digest, kSecTransformOutputAttributeName,
4549							verify, kSecTransformInputAttributeName, group, NULL);
4550				} else {
4551					STAssertNil((NSError *)err, @"Setting verify's input for %@", tname);
4552				}
4553
4554				SecTransformConnectTransforms(sign2 ? sign2 : sign, kSecTransformOutputAttributeName, verify, kSecSignatureAttributeName, group, NULL);
4555
4556				dispatch_group_enter(dg);
4557				dispatch_queue_t temp_q = dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
4558				SecTransformExecuteAsync(sign, temp_q,
4559					^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
4560					{
4561						if (message)
4562						{
4563							if (tst->pass)
4564							{
4565								STAssertTrue(message == kCFBooleanTrue, @"Failed to verify proper signature %@; message = %@", tname, message);
4566							} else
4567							{
4568								STAssertTrue(message == kCFBooleanFalse, @"Failed to detect tampering %@; message = %@", tname, message);
4569							}
4570						}
4571
4572						STAssertNil((NSError *)err, @"Executed ok for %@ (err=%@)", tname, error);
4573
4574						if (isFinal)
4575						{
4576							dispatch_group_leave(dg);
4577						}
4578					});
4579
4580				CFRelease(sign);
4581				CFRelease(verify);
4582				CFRelease(group);
4583			}
4584		}
4585	}
4586
4587	struct raw_test {
4588		SecKeyRef pub, priv;
4589		NSString *name;
4590	} raw_tests[] = {
4591		{rsa_pub_key, rsa_priv_key, @"RSA raw test"},
4592		{dsa_pub_key, dsa_priv_key, @"DSA raw test"},
4593		{ecdsa_pub_key, ecdsa_priv_key, @"ECDSA raw test"},
4594	};
4595
4596	for(i = 0; i < sizeof(raw_tests)/sizeof(raw_tests[0]); ++i) {
4597		raw_test *t = raw_tests + i;
4598		SecTransformRef tee = SecNullTransformCreate();
4599		const char *raw_bytes = "some bytes";
4600		CFDataRef bytes = CFDataCreate(NULL, (UInt8*)raw_bytes, strlen(raw_bytes));
4601		CFErrorRef err = NULL;
4602
4603		SecTransformRef sign = SecSignTransformCreate(t->priv, &err);
4604		STAssertNil((id)err, @"%@ test sign create err=%@", t->name, err);
4605
4606		SecTransformRef verify = SecVerifyTransformCreate(t->pub, NULL, &err);
4607		STAssertNil((id)err, @"%@ test verify create err=%@", t->name, err);
4608
4609		SecGroupTransformRef group = SecTransformCreateGroupTransform();
4610		SecTransformConnectTransforms(sign, kSecTransformOutputAttributeName, verify, kSecSignatureAttributeName, group, &err);
4611		SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, sign, kSecTransformInputAttributeName, group, &err);
4612		SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, verify, kSecTransformInputAttributeName, group, &err);
4613		SecTransformSetAttribute(tee, kSecTransformInputAttributeName, bytes, &err);
4614		STAssertNil((id)err, @"%@ setup error=%@", t->name, err);
4615		CFRetain(group);
4616		dispatch_group_async(dg, dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
4617			CFErrorRef xerr = NULL;
4618			CFTypeRef result = SecTransformExecute(group, &xerr);
4619			CFRelease(group);
4620
4621			if (result) {
4622				STAssertTrue(result == kCFBooleanTrue, @"%@ sign result=%@", t->name, result);
4623			} else {
4624				STFail(@"%@ no result", t->name);
4625			}
4626			STAssertNil((id)err, @"%@ execute error=%@", t->name, xerr);
4627		});
4628		CFRelease(group);
4629	}
4630
4631	// Test some things we want to fail for:
4632
4633	SecTransformRef tee = SecNullTransformCreate();
4634	SecTransformSetAttribute(tee, kSecTransformInputAttributeName, message, NULL);
4635	SecTransformRef vrfy = SecVerifyTransformCreate(ecdsa_pub_key, NULL, NULL);
4636
4637	SecGroupTransformRef group = SecTransformCreateGroupTransform();
4638	SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, vrfy, kSecSignatureAttributeName, group, NULL);
4639	SecTransformSetAttribute(vrfy, kSecDigestTypeAttribute, CFSTR("No such type"), NULL);
4640	SecTransformSetAttribute(vrfy, kSecTransformInputAttributeName, message, NULL);
4641	err = NULL;
4642	CFTypeRef no_result = SecTransformExecute(group, (CFErrorRef*)&err);
4643	CFRelease(group);
4644
4645	STAssertNil((id)no_result, @"No result from nonexistent digest");
4646	STAssertErrorHas((id)err, @"[Ii]nvalid digest algorithm", @"Error message describes nature of error (%@)", err);
4647	STAssertErrorHas((id)err, @"ECDSA signature", @"Error is not overly general (%@)", err);
4648	STAssertErrorHas((id)err, @"ECDSA signature", @"Error is not overly general (%@)", err);
4649	STAssertErrorHas((id)err, @"SHA1.*SHA2", @"Error describes valid algorithms (%@)", err);
4650	CFRelease(vrfy);
4651
4652	// It would be awesome if we supported all the digests, and this test went away.
4653	vrfy = SecVerifyTransformCreate(dsa_pub_key, message, NULL);
4654	tee = SecNullTransformCreate();
4655
4656	group = SecTransformCreateGroupTransform();
4657	SecTransformConnectTransforms(vrfy, kSecSignatureAttributeName, tee, kSecTransformOutputAttributeName, group, NULL);
4658	SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, vrfy, kSecTransformInputAttributeName, group, NULL);
4659	SecTransformSetAttribute(vrfy, kSecDigestTypeAttribute, kSecDigestSHA2, NULL);
4660	SecTransformSetAttribute(tee, kSecTransformInputAttributeName, message, NULL);
4661	err = NULL;
4662	no_result = SecTransformExecute(group, (CFErrorRef*)&err);
4663	CFRelease(group);
4664
4665	STAssertNil((id)no_result, @"No result from invalid digest");
4666	STAssertErrorHas((id)err, @"[Ii]nvalid digest algorithm", @"Error message gives problem statement (%@)", err);
4667	STAssertErrorHas((id)err, @"[^A-Z]DSA signature", @"Error is not overly general (%@)", err);
4668	STAssertErrorHas((id)err, @"SHA1", @"Correct algorithm is named (%@)", err);
4669
4670	dispatch_group_wait(dg, DISPATCH_TIME_FOREVER);
4671}
4672
4673static BOOL keyWithBytes(CFDataRef keyData, SecKeyRef* key, CFTypeRef keyClass) {
4674	CFErrorRef errorRef=NULL;
4675	CFMutableDictionaryRef parameters;
4676	parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 10, NULL, NULL);
4677
4678	/*
4679	 kSecAttrKeyClass values:
4680	 kSecAttrKeyClassPublic
4681	 kSecAttrKeyClassPrivate
4682	 kSecAttrKeyClassSymmetric
4683	 */
4684	CFDictionaryAddValue(parameters, kSecAttrKeyClass, keyClass);
4685	CFDictionaryAddValue(parameters, kSecAttrIsPermanent, kCFBooleanFalse); /* also means we have raw bits */
4686	CFDictionaryAddValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeRSA); /* also means we have raw bits */
4687	*key = SecKeyCreateFromData(parameters, keyData, &errorRef);
4688	CFRelease(parameters);
4689	return (key != NULL);
4690}
4691
4692-(void)testVerifyWithKeyFromBytes  {
4693    /*
4694	static const uint8_t original_pubKeyData[] =
4695	{
4696		0x30, 0x48, 0x02, 0x41, 0x00, 0xd1, 0x4d, 0x1c, 0xe6, 0xbd, 0xd6, 0x8c, 0x4b, 0x77, 0x1e, 0x9f,
4697		0xbc, 0xe1, 0xf6, 0x96, 0xf2, 0x55, 0xa2, 0xdc, 0x28, 0x36, 0x39, 0xf4, 0xec, 0x5b, 0x85, 0x9b,
4698		0x3c, 0x7f, 0x98, 0xe0, 0xed, 0x49, 0xf5, 0x44, 0xb1, 0x87, 0xa8, 0xf6, 0x7f, 0x55, 0xc0, 0x39,
4699		0xf0, 0xe7, 0xcc, 0x9c, 0x84, 0xde, 0x7d, 0x9a, 0x87, 0x38, 0xf2, 0x4b, 0x11, 0x6f, 0x63, 0x90,
4700		0xfc, 0x72, 0x2c, 0x86, 0xa3, 0x02, 0x03, 0x01, 0x00, 0x01
4701	}; */
4702
4703	// openssl genrsa -out /tmp/rsa512.pem
4704	// openssl rsa -inform PEM -in /tmp/rsa512.pem -outform DER -out /tmp/rsa512.der
4705	// hexdump -C /tmp/rsa512.der | cut -c10-58 | tr -s ' ' ' ' | sed -e 's/ /, 0x/g' -e 's/$/,/' | cut -c3-|pbcopy
4706	static const uint8_t pubKeyData[] = {
4707		0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
4708		0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xbf, 0xd5, 0xce, 0x43, 0x59, 0xd5, 0xf8,
4709		0x41, 0xb2, 0xe1, 0x16, 0x02, 0x2a, 0x16, 0xcb, 0xef, 0x49, 0xea, 0x98, 0x71, 0xf8, 0xfb, 0x94,
4710		0x23, 0x12, 0xf7, 0xbc, 0x80, 0xd0, 0x8b, 0xfd, 0x29, 0xb8, 0xfc, 0x2c, 0x3d, 0x13, 0x6f, 0x37,
4711		0xef, 0xa7, 0x1e, 0xf9, 0x4c, 0x3d, 0x38, 0x3a, 0x2f, 0x6b, 0xa8, 0x16, 0x00, 0x27, 0x5a, 0xbe,
4712		0x3d, 0x61, 0xdd, 0x18, 0x45, 0x22, 0xdb, 0x1a, 0xff, 0x02, 0x03, 0x01, 0x00, 0x01,
4713	};
4714	static const uint8_t signatureData[] =
4715	{
4716		0xbc, 0x76, 0x2a, 0x50, 0x4e, 0x17, 0x0b, 0xa9, 0x31, 0x3b, 0xc5, 0xb0, 0x4d, 0x2a, 0x01, 0x9a,
4717		0xbb, 0x5e, 0x7b, 0x6e, 0x90, 0x2f, 0xaf, 0x3f, 0x40, 0xdb, 0xb0, 0xfc, 0x49, 0xcf, 0xbb, 0xb6,
4718		0x08, 0xf0, 0xbb, 0x04, 0x5f, 0x89, 0x0b, 0x10, 0x47, 0x06, 0x93, 0xb3, 0xb7, 0x0b, 0x4e, 0x17,
4719		0xe9, 0xb1, 0x55, 0x94, 0x63, 0x30, 0x0b, 0xa3, 0xb1, 0x28, 0xba, 0xe8, 0xef, 0xb4, 0xbd, 0xc5
4720	};
4721
4722	const char *raw_data = "Data to verify";
4723	CFDataRef data = CFDataCreate(NULL, (UInt8*) raw_data, strlen(raw_data));
4724
4725	SecKeyRef key;
4726	CFDataRef cfkeybytes;
4727	CFErrorRef error;
4728	cfkeybytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pubKeyData, sizeof(pubKeyData),kCFAllocatorNull);
4729
4730	if(keyWithBytes(cfkeybytes, &key, kSecAttrKeyClassPublic)){
4731		CFDataRef signature = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, signatureData, sizeof(signatureData),kCFAllocatorNull);
4732		SecTransformRef vt = SecVerifyTransformCreate(key,signature,&error);
4733		SecTransformSetAttribute(vt, kSecTransformDebugAttributeName, @"YES", NULL);
4734		SecTransformSetAttribute(vt, kSecTransformInputAttributeName, data, &error);
4735		CFBooleanRef signature_ok = (CFBooleanRef) SecTransformExecute(vt, &error);
4736
4737		CFRelease(vt);
4738		CFRelease(key);
4739		CFRelease(signature);
4740		CFRelease(data);
4741		CFRelease(cfkeybytes);
4742
4743		NSLog(@"STE result %@, err=%@", signature_ok, error);
4744		STAssertNil((id)error, @"Error from SecTransformExecute: %@", error);
4745	} else {
4746		STFail(@"Can't get SecKeyCreateFromData to work");
4747	}
4748}
4749
4750-(void)testAESAndCastKeysFromBytes  {
4751	CFErrorRef err = NULL;
4752	struct tcase {
4753		const char *name;
4754		CFTypeRef key_type;
4755		NSData *key_data;
4756	};
4757	const char *aes_kbytes = "0123456789012345";
4758	const char *cast_kbytes = "01234567";
4759
4760	struct tcase cases[] = {
4761		{"AES", kSecAttrKeyTypeAES, [NSData dataWithBytes:aes_kbytes length:strlen(aes_kbytes)]},
4762		{"CAST", kSecAttrKeyTypeCAST, [NSData dataWithBytes:cast_kbytes length:strlen(cast_kbytes)]},
4763	};
4764
4765	int i;
4766	for(i = 0; i < sizeof(cases)/sizeof(cases[0]); ++i) {
4767		NSDictionary *parm = [NSDictionary dictionaryWithObjectsAndKeys:
4768							  (id)kSecAttrKeyClassSymmetric, kSecAttrKeyClass,
4769							  (id)cases[i].key_type, kSecAttrKeyType,
4770							  (id)kCFBooleanFalse, kSecAttrIsPermanent,
4771							  NULL];
4772
4773		SecKeyRef k = SecKeyCreateFromData((CFDictionaryRef)parm, (CFDataRef)cases[i].key_data, (CFErrorRef *)&err);
4774		STAssertNotNil((id)k, @"%s SecKeyCreateFromData didn't", cases[i].name);
4775		STAssertNil((id)err, @"%s SecKeyCreateFromData err=%@", err);
4776
4777		SecTransformRef et = SecEncryptTransformCreate(k, &err);
4778		STAssertNotNil((id)et, @"No %s EncryptTransform created", cases[i].name);
4779		STAssertNil((id)err, @"Error from %s SecEncryptTransformCreate err=%@", cases[i].name, err);
4780
4781		SecTransformRef dt = SecDecryptTransformCreate(k, &err);
4782		STAssertNotNil((id)dt, @"No %s DecryptTransform created", cases[i].name);
4783		STAssertNil((id)err, @"Error from %s SecDecryptTransformCreate err=%@", cases[i].name, err);
4784
4785		if (k) {
4786			BOOL rt_ok = RoundTrip(CFSTR("/usr/share/dict/propernames"), et, dt, YES);
4787			CFRelease(et);
4788			CFRelease(dt);
4789			STAssertTrue(rt_ok, @"%s's round trip", cases[i].name);
4790		}
4791	}
4792}
4793
4794-(void)testDispatchAsumptions {
4795    // Failures here don't directly indicate we have a bug.  It would indicate that
4796    // either dispatch has one, or that we rely on something dispatch never promised
4797    // and has changed.
4798
4799    dispatch_semaphore_t pre_sem = dispatch_semaphore_create(0);
4800    dispatch_semaphore_t post_sem = dispatch_semaphore_create(0);
4801    __block bool pre_wait_works = false;
4802
4803    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
4804        STAssertTrue(0 == dispatch_semaphore_wait(pre_sem, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"semaphore signal prior to wait pre-wakes");
4805        pre_wait_works = true;
4806        dispatch_semaphore_signal(post_sem);
4807    });
4808    dispatch_semaphore_signal(pre_sem);
4809    STAssertTrue(0 == dispatch_semaphore_wait(post_sem, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"signal after wait wakes");
4810    STAssertTrue(pre_wait_works, @"pre-wait worked");
4811
4812
4813}
4814
4815// Build a group containing 3 subgroups, G1 which has 2 encoders, G2 and G3 which have one
4816// decoder each.   Exports and attributes are hooked up so execution results in a CFData
4817// with the same contents as input_data.   "self" is used by the STAssert macros.
4818// The various transforms are assigned names: G1, G2, G3, E64, EZLIB, DZLIB, D64.
4819static SecTransformRef build_nested_groups(id self, CFDataRef input_data) {
4820    SecGroupTransformRef outer = SecTransformCreateGroupTransform();
4821    SecGroupTransformRef g1 = SecTransformCreateGroupTransform();
4822    SecGroupTransformRef g2 = SecTransformCreateGroupTransform();
4823    SecGroupTransformRef g3 = SecTransformCreateGroupTransform();
4824
4825    CFErrorRef err = NULL;
4826
4827    SecTransformSetAttribute(outer, kSecTransformTransformName, CFSTR("OUTER"), &err);
4828    STAssertNil((id)err, @"Can't set outer's name: %@", err);
4829    SecTransformSetAttribute(g1, kSecTransformTransformName, CFSTR("G1"), &err);
4830    STAssertNil((id)err, @"Can't set g1's name: %@", err);
4831    SecTransformSetAttribute(g2, kSecTransformTransformName, CFSTR("G2"), &err);
4832    STAssertNil((id)err, @"Can't set g2's name: %@", err);
4833    SecTransformSetAttribute(g3, kSecTransformTransformName, CFSTR("G3"), &err);
4834    STAssertNil((id)err, @"Can't set g3's name: %@", err);
4835
4836    SecTransformRef e64 = SecEncodeTransformCreate(kSecBase64Encoding, &err);
4837    STAssertNil((id)err, @"Expected err to be nil, got: %@", err);
4838    STAssertNotNil((id)e64, @"Could not make Encode64 transform");
4839    SecTransformSetAttribute(e64, kSecTransformTransformName, CFSTR("E64"), NULL);
4840    SecTransformRef ezlib = SecEncodeTransformCreate(kSecZLibEncoding, &err);
4841    STAssertNil((id)err, @"Expected err to be nil, got: %@", err);
4842    STAssertNotNil((id)ezlib, @"Could not make Encode ZLib transform");
4843    SecTransformSetAttribute(ezlib, kSecTransformTransformName, CFSTR("EZLIB"), NULL);
4844
4845    SecTransformConnectTransforms(e64, kSecTransformOutputAttributeName, ezlib, kSecTransformInputAttributeName, g1, &err);
4846    STAssertNil((id)err, @"Can't connect e64 to ezlib: %@", err);
4847    SecTransformConnectTransforms(g1, kSecTransformInputAttributeName, e64, kSecTransformInputAttributeName, g1, &err);
4848    STAssertNil((id)err, @"Can't connect g1's input to e64's input: %@", err);
4849    SecTransformConnectTransforms(ezlib, kSecTransformOutputAttributeName, g1, kSecTransformOutputAttributeName, g1, &err);
4850    STAssertNil((id)err, @"Can't connect ezlib's output to g1's output: %@", err);
4851
4852    SecTransformRef dzlib = SecDecodeTransformCreate(kSecZLibEncoding, &err);
4853    STAssertNil((id)err, @"Expected err to be nil, got: %@", err);
4854    STAssertNotNil((id)dzlib, @"Could not make Decode ZLib transform");
4855    SecTransformSetAttribute(dzlib, kSecTransformTransformName, CFSTR("dzlib"), NULL);
4856    SecTransformRef d64 = SecDecodeTransformCreate(kSecBase64Encoding, &err);
4857    STAssertNil((id)err, @"Expected err to be nil, got: %@", err);
4858    STAssertNotNil((id)d64, @"Could not make Decode64 transform");
4859    SecTransformSetAttribute(dzlib, kSecTransformTransformName, CFSTR("D64"), NULL);
4860
4861    // putting just one transform in g2 and g3
4862    SecTransformConnectTransforms(g2, kSecTransformInputAttributeName, dzlib, kSecTransformInputAttributeName, g2, &err);
4863    STAssertNil((id)err, @"Can't connect g2's input to dzlib's input: %@", err);
4864    SecTransformConnectTransforms(dzlib, kSecTransformOutputAttributeName, g2, kSecTransformOutputAttributeName, g2, &err);
4865    STAssertNil((id)err, @"Can't connect dzlib's output to g2's output: %@", err);
4866
4867    SecTransformConnectTransforms(g3, kSecTransformInputAttributeName, d64, kSecTransformInputAttributeName, g3, &err);
4868    STAssertNil((id)err, @"Can't connect g2's input to d64's input: %@", err);
4869    SecTransformConnectTransforms(d64, kSecTransformOutputAttributeName, g3, kSecTransformOutputAttributeName, g3, &err);
4870    STAssertNil((id)err, @"Can't connect d64's output to g2's output: %@", err);
4871
4872    SecTransformConnectTransforms(g1, kSecTransformOutputAttributeName, g2, kSecTransformInputAttributeName, outer, &err);
4873    STAssertNil((id)err, @"Can't connect g1 to g2 (dzlib): %@", err);
4874    SecTransformConnectTransforms(g2, kSecTransformOutputAttributeName, g3, kSecTransformInputAttributeName, outer, &err);
4875    STAssertNil((id)err, @"Can't connect g2 (dzlib) to g3 (d64): %@", err);
4876
4877    SecTransformSetAttribute(g1, kSecTransformInputAttributeName, input_data, &err);
4878    STAssertNil((id)err, @"Can't set g1's input: %@", err);
4879
4880
4881    CFRelease(e64);
4882    CFRelease(ezlib);
4883    CFRelease(dzlib);
4884    CFRelease(d64);
4885    CFRelease(g1);
4886    CFRelease(g2);
4887    CFRelease(g3);
4888    return outer;
4889}
4890
4891-(void)testGroupsInGroups {
4892    UInt8 original_bytes[] = "'Twas brillig and the...was that smiley toads?   Something with chives?  Aw heck!";
4893    CFDataRef original = CFDataCreate(NULL, original_bytes, sizeof(original_bytes));
4894
4895    // Test executing the top group, a sub group, and a non-group member.
4896    for (NSString *name in [NSArray arrayWithObjects:@"OUTER", @"G1", @"D64", nil]) {
4897        CFErrorRef err = NULL;
4898        SecGroupTransformRef outer = build_nested_groups(self, original);
4899        SecTransformRef start_at = SecTransformFindByName(outer, (CFStringRef)name);
4900        STAssertNotNil((id)start_at, @"Expected to find %@", name);
4901
4902        CFDataRef output = (CFDataRef)SecTransformExecute(start_at, &err);
4903        STAssertNil((id)err, @"Can't execute directly created nested transform starting at %@: %@", start_at, err);
4904        STAssertEqualObjects((id)output, (id)original, @"Output and original should match (started at %@)", start_at);
4905        CFRelease(outer);
4906        if (err) {
4907            CFRelease(err);
4908        }
4909    }
4910
4911    {
4912        SecGroupTransformRef bad_outer = build_nested_groups(self, original);
4913        SecTransformRef d64 = SecTransformFindByName(bad_outer, CFSTR("D64"));
4914        STAssertNotNil((id)d64, @"Expected to find d64");
4915        CFErrorRef err = NULL;
4916        // d64 is in a group in bad_outer, we set things up to fail
4917        // and later expect execute to fail because of it.
4918        SecTransformSetAttribute(d64, kSecDecodeTypeAttribute, CFSTR("NOT valid"), &err);
4919        if (err) {
4920            // It can fail right away
4921            ErrorHas((NSError*)err, @"Unsupported decode type");
4922        } else {
4923            // Or later (see below)
4924            STAssertNil((id)err, @"Expected to set decode type: %@", err);
4925        }
4926
4927        SecTransformRef e64 = SecTransformFindByName(bad_outer, CFSTR("E64"));
4928        STAssertNotNil((id)e64, @"Expected to find e64");
4929        CFStringRef any = CFSTR("ANY");
4930        // e64 and d64 aren't in the same groups, but they are in outer.
4931        // There should be no way to (directly) connect them, so try all
4932        // 4 groups and make sure none work.
4933        for (NSString *group_name in [NSArray arrayWithObjects:@"OUTER", @"G1", @"G2", @"G3", nil]) {
4934            SecTransformRef connect_in = SecTransformFindByName(bad_outer, (CFStringRef)group_name);
4935            STAssertNotNil((id)connect_in, @"Expected to find %@", group_name);
4936            err = NULL;
4937            SecTransformConnectTransforms(d64, any, e64, any, bad_outer, &err);
4938            STAssertNotNil((id)err, @"Expected error on cross group connect (in %@)", group_name);
4939            if (err) {
4940                STAssertEquals(CFErrorGetCode(err), (CFIndex)kSecTransformErrorInvalidConnection, @"error code (in %@)", group_name);
4941                STAssertEqualObjects((id)CFErrorGetDomain(err), (id)kSecTransformErrorDomain, @"error domain (in %@)", group_name);
4942                CFRelease(err);
4943                err = NULL;
4944            }
4945
4946            // While we are here, make sure we can't set a non-exported group attribute
4947            SecTransformSetAttribute((SecTransformRef)connect_in, CFSTR("nobody-exports-me"), CFSTR("VALUE"), &err);
4948            STAssertNotNil((id)err, @"Expected an error setting a non-exported attribute on %@", connect_in);
4949            // Make sure this is the error we expect, not something unrelated to our transgression
4950            ErrorHas((NSError*)err, @"non-exported attribute");
4951            // Error should have the name of the offending attribute
4952            ErrorHas((NSError*)err, @"nobody-exports-me");
4953            if (err) {
4954                CFRelease(err);
4955                err = NULL;
4956            }
4957        }
4958
4959        CFTypeRef no_result = SecTransformExecute(bad_outer, &err);
4960        STAssertNotNil((id)err, @"Expected error");
4961        ErrorHas((NSError*)err, @"Unsupported decode type");
4962        STAssertNil((id)no_result, @"Expected no result, got: %@", no_result);
4963        CFRelease(bad_outer);
4964
4965        // Make sure we can't connect to or from non-exported group attributes
4966        bad_outer = build_nested_groups(self, original);
4967        STAssertNotNil((id)bad_outer, @"Expected to build nested transform");
4968        SecTransformRef g1 = SecTransformFindByName(bad_outer, CFSTR("G1"));
4969        STAssertNotNil((id)g1, @"Expected to find g1");
4970        SecTransformRef appendix = SecNullTransformCreate();
4971        SecTransformConnectTransforms(appendix, kSecTransformOutputAttributeName, g1, CFSTR("NONE"), bad_outer, &err);
4972        STAssertNotNil((id)err, @"Expected to fail connecting appendix to g1, but didn't");
4973        ErrorHas((NSError*)err, @"non-exported attribute");
4974        if (err) {
4975            CFRelease(err);
4976            err = NULL;
4977        }
4978        SecTransformConnectTransforms(g1, CFSTR("DOES_NOT_EXIST"), appendix, kSecTransformInputAttributeName, bad_outer, &err);
4979        STAssertNotNil((id)err, @"Expected to fail connecting g1 to appendix, but didn't");
4980        ErrorHas((NSError*)err, @"non-exported attribute");
4981        if (err) {
4982            CFRelease(err);
4983            err = NULL;
4984        }
4985
4986        CFRelease(bad_outer);
4987        CFRelease(appendix);
4988    }
4989}
4990
4991// 10080968 covers this case.   It isn't a regression (it was impossible to create nested groups
4992// until recently), but it needs to be addressed before we ship.
4993-(void)disabledUntilPR_10080968_testExternalizeGroupsInGroups {
4994    CFErrorRef err = NULL;
4995    UInt8 original_bytes[] = "Sic Semper Tyrannosaurus!";
4996    CFDataRef original = CFDataCreate(NULL, original_bytes, sizeof(original_bytes));
4997
4998    SecGroupTransformRef outer = build_nested_groups(self, original);
4999    NSLog(@"outer=%@", SecTransformDotForDebugging(outer));
5000    SecTransformRef d64 = SecTransformFindByName(outer, CFSTR("D64"));
5001    STAssertNotNil((id)d64, @"Expected to find d64");
5002
5003    CFDictionaryRef freezeDriedNestedGroups = SecTransformCopyExternalRepresentation(d64);
5004    STAssertNotNil((id)freezeDriedNestedGroups, @"Expected to externalize group");
5005
5006    SecTransformRef outer2 = SecTransformCreateFromExternalRepresentation(freezeDriedNestedGroups, &err);
5007    STAssertNil((id)err, @"Can't create nested group err: %@", err);
5008    STAssertNotNil((id)outer2, @"Expected transform fron xrep: %@", freezeDriedNestedGroups);
5009    NSLog(@"outer2=%@", SecTransformDotForDebugging(outer2));
5010
5011    CFTypeRef output2 = SecTransformExecute(outer2, &err);
5012    STAssertNil((id)err, @"Can't execute outer2: %@", err);
5013    STAssertEqualObjects((id)output2, (id)original, @"Output2 and original should match");
5014}
5015
5016static NSString *CopyLeakLine()
5017{
5018    static char os_build[16];
5019    static dispatch_once_t get_os_build_once;
5020    static BOOL broken_leaks_command = NO;
5021
5022    dispatch_once(&get_os_build_once, ^{
5023        int mib[] = { CTL_KERN, KERN_OSVERSION };
5024        size_t bufsz = sizeof(os_build);
5025        sysctl(mib, 2, os_build, &bufsz, NULL, 0);
5026
5027        if (4 == sizeof(char*) && 0 == strcmp(os_build, "12A75")) {
5028            // 12A75's leaks command was badly broken for 32 bit.
5029            // Running it suspends otest, and it is too hard to
5030            // recover.
5031            broken_leaks_command = YES;
5032        }
5033    });
5034
5035    if (broken_leaks_command) {
5036        return [NSString stringWithFormat:@"Leaks command is broken in %s", os_build];
5037    }
5038
5039    NSRegularExpression *matchLeaksLine = [NSRegularExpression regularExpressionWithPattern:@"^Process \\d+: \\d+ leaks for \\d+ total leaked bytes.$" options:NSRegularExpressionAnchorsMatchLines error:NULL];
5040
5041	char *leak_command = NULL;
5042	NSString *fname = [NSString stringWithFormat:@"/tmp/L%d-%d", getpid(), (int)arc4random()];
5043	asprintf(&leak_command, "(/usr/bin/leaks %d >%s || (echo OOPS; kill -CONT %d))", getpid(), [fname UTF8String], getpid());
5044	system(leak_command);
5045	free(leak_command);
5046	NSString *output = [NSString stringWithContentsOfFile:fname encoding:NSUTF8StringEncoding error:NULL];
5047	NSTextCheckingResult *result = [matchLeaksLine firstMatchInString:output options:0 range:NSMakeRange(0, [output length])];
5048	if (result.range.location == NSNotFound) {
5049		return NULL;
5050	}
5051	NSRange matchRange = result.range;
5052	return [output substringWithRange:matchRange];
5053}
5054
5055-(void)testAAASimpleLeakTest {
5056	NSString *starting_leaks = CopyLeakLine();
5057	STAssertNotNil(starting_leaks, @"Found initial leaks");
5058	for(int i = 0; i < 10; i++) {
5059		CFRelease(SecTransformCreateGroupTransform());
5060	}
5061
5062	NSString *current_leaks = NULL;
5063
5064	// Some of the destruction is async, so if they don't pan out the same, a little sleep and retry
5065	// can legitimately fix it.
5066	for(int i = 0; i < 10; i++) {
5067		current_leaks = CopyLeakLine();
5068		if ([current_leaks isEqualToString:starting_leaks]) {
5069			break;
5070		} else {
5071			sleep(1);
5072		}
5073	}
5074
5075	STAssertNotNil(current_leaks, @"Found current leaks");
5076	STAssertEqualObjects(current_leaks, starting_leaks, @"Expected no new leaks");
5077}
5078
5079-(void)testAAASimpleishLeakTest {
5080    NSLog(@"pid=%d", getpid());
5081	NSString *starting_leaks = CopyLeakLine();
5082	STAssertNotNil(starting_leaks, @"Found initial leaks");
5083    CFErrorRef err = NULL;
5084
5085    // Derived from Matt Wright's 10242560 test.c
5086    int fd = open("/dev/random", O_RDONLY);
5087    SecTransformRef b64encode = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
5088    const int buffer_size = 1024;
5089    void *buffer = malloc(buffer_size);
5090    // For this test, ignore short reads
5091    read(fd, buffer, buffer_size);
5092    CFDataRef data = CFDataCreateWithBytesNoCopy(NULL, (UInt8*)buffer, buffer_size, kCFAllocatorMalloc);
5093    SecTransformSetAttribute(b64encode, kSecTransformInputAttributeName, data, &err);
5094    STAssertNil((id)err, @"Expected no SecTransformSetAttribute error, got: %@", err);
5095    CFRelease(data);
5096    CFTypeRef output = SecTransformExecute(b64encode, &err);
5097    STAssertNotNil((id)output, @"Expected result");
5098    STAssertNil((id)err, @"Expected no execute error, got: %@", err);
5099    CFRelease(output);
5100    CFRelease(b64encode);
5101
5102	NSString *current_leaks = NULL;
5103
5104	// Some of the destruction is async, so if they don't pan out the same, a little sleep and retry
5105	// can legitimately fix it.
5106	for(int i = 0; i < 10; i++) {
5107		current_leaks = CopyLeakLine();
5108		if ([current_leaks isEqualToString:starting_leaks]) {
5109			break;
5110		} else {
5111			sleep(1);
5112		}
5113	}
5114
5115	STAssertNotNil(current_leaks, @"Found current leaks");
5116	STAssertEqualObjects(current_leaks, starting_leaks, @"Expected no new leaks");
5117}
5118
5119@end
5120