]> git.saurik.com Git - apple/security.git/blobdiff - Security/libsecurity_transform/custom.mm
Security-57031.1.35.tar.gz
[apple/security.git] / Security / libsecurity_transform / custom.mm
diff --git a/Security/libsecurity_transform/custom.mm b/Security/libsecurity_transform/custom.mm
new file mode 100644 (file)
index 0000000..496048c
--- /dev/null
@@ -0,0 +1,5119 @@
+/*
+ * Copyright (c) 2010 Apple Inc. All rights reserved.
+ *
+ * @APPLE_LICENSE_HEADER_START@
+ * 
+ * This file contains Original Code and/or Modifications of Original Code
+ * as defined in and that are subject to the Apple Public Source License
+ * Version 2.0 (the 'License'). You may not use this file except in
+ * compliance with the License. Please obtain a copy of the License at
+ * http://www.opensource.apple.com/apsl/ and read it before using this
+ * file.
+ * 
+ * The Original Code and all software distributed under the License are
+ * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
+ * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
+ * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
+ * Please see the License for the specific language governing rights and
+ * limitations under the License.
+ * 
+ * @APPLE_LICENSE_HEADER_END@
+ */
+
+
+
+#import "SecTransform.h"
+#import "SecCustomTransform.h"
+#import "SecDigestTransform.h"
+#import "SecEncryptTransform.h"
+#import "SecEncodeTransform.h"
+#import "SecDecodeTransform.h"
+#import "SecSignVerifyTransform.h"
+#import "SecNullTransform.h"
+#import "SecExternalSourceTransform.h"
+#import <Security/SecItem.h>
+#import "misc.h"
+#import "Utilities.h"
+#import "SecNullTransform.h"
+#include "regex.h"
+#include <dispatch/dispatch.h>
+#import "SecMaskGenerationFunctionTransform.h"
+#import "SecTransformInternal.h"
+#import "custom.h"
+#include "SecTransformReadTransform.h"
+#import "SecTransformValidator.h"
+#include <sys/types.h>
+#include <sys/sysctl.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <CommonCrypto/CommonCryptor.h>
+#include <sys/stat.h>
+#import "NSData+HexString.h"
+#include <CoreFoundation/CFBase.h>
+#include <CoreFoundation/CFData.h>
+#include <CoreFoundation/CFRuntime.h>
+
+// compatibility layer
+struct SecTransformCreateBlockParameters {
+       CFTypeRef (^send)(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type, CFTypeRef value);
+       CFTypeRef (^get)(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type);
+       CFTypeRef (^pushback)(SecTransformStringOrAttributeRef attribute, CFTypeRef value);
+       CFErrorRef (^overrideTransform)(CFStringRef action, SecTransformActionBlock newAction);
+       CFErrorRef (^overrideAttribute)(CFStringRef action, SecTransformStringOrAttributeRef attribute, SecTransformAttributeActionBlock newAction);
+};
+
+typedef void (^SecTransformCreateBlock)(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params);
+
+SecTransformCreateBlock global_create_block;
+
+static SecTransformInstanceBlock block_for_custom_transform(CFStringRef name, SecTransformRef tr, SecTransformImplementationRef ir)
+{      
+       SecTransformInstanceBlock b = ^{
+               // XXX: leak, need to override Finalize and clean up…   (and need to handle caller overriding finalize…)
+               SecTransformCreateBlockParameters *params = static_cast<SecTransformCreateBlockParameters *>(malloc(sizeof(SecTransformCreateBlockParameters)));
+               
+               params->overrideAttribute = ^(CFStringRef action, SecTransformStringOrAttributeRef attribute, SecTransformAttributeActionBlock newAction) {
+                       // We don't need to special case ProcessData to call SecTransformSetDataAction as there are no longer any uses of it
+                       return SecTransformSetAttributeAction(ir, action, attribute, newAction);
+               };
+               
+               params->overrideTransform = ^(CFStringRef action, SecTransformActionBlock newAction) {
+                       return SecTransformSetTransformAction(ir, action, newAction);
+               };
+               
+               params->get = ^(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type) {
+                       return SecTranformCustomGetAttribute(ir, attribute, type);
+               };
+               
+               params->send = ^(SecTransformStringOrAttributeRef attribute, SecTransformMetaAttributeType type, CFTypeRef value) {
+                       return SecTransformCustomSetAttribute(ir, attribute, type, value);
+               };
+               
+               params->pushback = ^(SecTransformStringOrAttributeRef attribute, CFTypeRef value) {
+                       return SecTransformPushbackAttribute(ir, attribute, value);
+               };
+               
+               params->overrideAttribute = Block_copy(params->overrideAttribute);
+               params->overrideTransform = Block_copy(params->overrideTransform);
+               params->get = Block_copy(params->get);
+               params->send = Block_copy(params->send);
+               params->pushback = Block_copy(params->pushback);
+
+               global_create_block(name, tr, params);
+               
+               return (CFErrorRef)NULL;
+       };
+       
+       return Block_copy(b);
+}
+
+// Sort of a bridge from the old Custom SPI to the new API, but is also
+// useful when you REALLY need to access stack locals as __block variables,
+// but don't need multithreading, or generic internalizing.
+static SecTransformRef custom_transform(CFStringRef base_name, SecTransformCreateBlock cb)
+{
+       static int ct_cnt = 0;
+       static dispatch_queue_t cnt_q = dispatch_queue_create("com.apple.security.custom_trasnform-cnt", 0);
+       __block CFStringRef name = NULL;
+       __block SecTransformRef ret = NULL;
+       
+       dispatch_sync(cnt_q, ^{
+               CFErrorRef err = NULL;
+               
+               name = CFStringCreateWithFormat(NULL, NULL, CFSTR("%@.%d"), base_name, ct_cnt++);
+               global_create_block = cb;
+               if (SecTransformRegister(name, block_for_custom_transform, &err)) {
+                       ret = SecTransformCreate(name, &err);
+                       if (err) {
+                               CFfprintf(stderr, "Error %@ creating %@\n", err, base_name);
+                               CFRelease(err);
+                       }
+               } else {
+                       CFfprintf(stderr, "Error %@ registering %@\n", err, base_name);
+                       CFRelease(err);
+               }
+               global_create_block = NULL;
+               CFRelease(name);
+       });
+       
+       return ret;
+}
+
+
+#define STAssertErrorHas(err, rx, msg...) STAssertTrue(ErrorHas(err, rx), ##msg);
+
+static BOOL ErrorHas(NSError *error, NSString *rx) {
+       if (!error) {
+               return NO;
+       }
+       if (![error isKindOfClass:[NSError class]]) {
+               return NO;
+       }
+       
+       NSString *es = [error description];
+       if (!es) {
+               return NO;
+       }
+       return [es rangeOfString:rx options:NSRegularExpressionSearch].location != NSNotFound;
+}
+
+
+static SecTransformInstanceBlock DelayTransformBlock(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+                               
+                                                               
+               SecTransformSetDataAction(ref, kSecTransformActionProcessData,
+                       ^(CFTypeRef value) 
+                       {
+                                               
+                               if (NULL != value && CFNumberGetTypeID() == CFGetTypeID(value))
+                               {
+                                       long long n;
+                                       CFNumberGetValue((CFNumberRef)value, kCFNumberLongLongType, &n);
+                                       usleep((useconds_t)(n / NSEC_PER_USEC));
+                               }
+                               
+                               return value;
+                       });
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);
+}
+
+static SecTransformRef delay_transform(long long nsec) {
+       CFStringRef name = CFSTR("com.apple.security.unit-test.delay");
+       
+       
+       
+       static dispatch_once_t once;
+       __block Boolean ok = TRUE;
+                       
+       dispatch_block_t aBlock = ^
+       {
+               ok = SecTransformRegister(name, &DelayTransformBlock, NULL);
+       };
+       
+       dispatch_once(&once, aBlock);
+
+       if (!ok) 
+       {
+               return NULL;
+       }
+       
+       SecTransformRef ct = SecTransformCreate(name, NULL);
+       CFNumberRef nr = CFNumberCreate(NULL, kCFNumberLongLongType, &nsec);
+       SecTransformSetAttribute(ct, CFSTR("DELAY"), nr, NULL);
+       CFRelease(nr);
+       
+       return ct;
+}
+
+@implementation custom
+
+class BufferStream
+{
+protected:
+       const char* mBuffer;
+       size_t mLength;
+       size_t mStringLength;
+       size_t mPos;
+
+       char *mCurrentString;
+
+public:
+       BufferStream(const char* buffer, size_t length) : mBuffer(buffer), mLength(length), mStringLength(0), mPos(0), mCurrentString(NULL) {}
+       ~BufferStream();
+       
+       const char* GetNextString();
+       void SplitString(const char*& stringA, const char*& stringB);
+};
+
+
+
+BufferStream::~BufferStream()
+{
+       if (NULL != mCurrentString)
+       {
+               free(mCurrentString);
+       }
+}
+
+
+
+const char* BufferStream::GetNextString()
+{
+       size_t p = mPos;
+       if (p >= mLength)
+       {
+               return NULL; // eof
+       }
+       
+       // run to either the end of the buffer or a return
+       while (p < mLength && mBuffer[p] != '\n')
+       {
+               p += 1;
+       }
+       
+       if (p != mLength)
+       {
+               // handle the end of the buffer specially, since it doesn't point
+               // to valid space
+               p -= 1;
+       }
+       
+       // p now points to the last character in the string
+       // allocate memory for our buffer
+       mStringLength = p - mPos + 1;
+       mCurrentString = (char*) realloc(mCurrentString, mStringLength + 1);
+       memmove(mCurrentString, mBuffer + mPos, mStringLength);
+       mCurrentString[mStringLength] = 0;
+       mPos = p + 2;
+       
+       return mCurrentString;
+}
+
+
+
+void BufferStream::SplitString(const char*& a, const char*& b)
+{
+       // scan the buffer, looking for a ':'
+       size_t p = 0;
+       while (mCurrentString[p] != 0 && mCurrentString[p] != ':')
+       {
+               p += 1;
+       }
+       
+       // the first string is always our buffer pointer
+       a = mCurrentString;
+
+       if (mCurrentString[p] == ':')
+       {
+               mCurrentString[p] = 0;
+               
+               // look for the beginning of the next string
+               p += 1;
+               while (p < mLength && isspace(mCurrentString[p]))
+               {
+                       p += 1;
+               }
+               
+               b = mCurrentString + p;
+       }
+       else
+       {
+               b = NULL;
+       }
+}
+
+
+
+-(void)disabledtestzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
+{
+       // open leaks and make a connection to it.
+       char* name;
+
+       const int kChunkSize = 16384;
+       char buffer[kChunkSize];
+       pid_t thePid = getpid();
+       asprintf(&name, "/tmp/leaks%d.txt", thePid);
+       sprintf(buffer, "/usr/bin/leaks %d >%s", thePid, name);
+       system(buffer);
+       
+       struct stat st;
+       stat(name, &st);
+       
+       char* rBuffer = (char*) malloc((size_t)st.st_size);
+       FILE* f = fopen(name, "r");
+       fread(rBuffer, 1, (size_t)st.st_size, f);
+       fclose(f);
+       
+       // set up our output parser
+       BufferStream bStream(rBuffer, (size_t)st.st_size);
+       const char* s = bStream.GetNextString();
+       
+       bool didError = true;
+
+       if (NULL != s)
+       {
+               // we have our string, split it and see what it means
+               const char* key;
+               const char* value;
+               
+               bStream.SplitString(key, value);
+               if (strcmp(key, "leaks Report Version") != 0 || strcmp(value, "2.0") != 0)
+               {
+                       didError = true;
+               }
+               else
+               {
+                       didError = false;
+               }
+       }
+
+       if (!didError)
+       {
+               const char* key;
+               const char* value;
+
+               // figure out what our target line will look like
+               char* target;
+               asprintf(&target, "Process %d", thePid);
+               
+               const char* nextString = bStream.GetNextString();
+               while (nextString)
+               {
+                       bStream.SplitString(key, value);
+                       if (strcmp(key, target) == 0) // we found our target!!!
+                       {
+                               // do it again
+                               bStream.GetNextString();
+                               bStream.SplitString(key, value);
+                               
+                               if (value[0] != '0') // we have a non-zero result... :(
+                               {
+                                       didError = true;
+                               }
+                       }
+                       
+                       nextString = bStream.GetNextString();
+               }
+               
+               free(target);
+       }
+       
+       STAssertFalse(didError, @"You have leaks!");
+       
+       if (didError)
+       {
+               // dump to our output file
+               // make a file name for the leaks output
+               FILE* f = fopen(name, "w");
+               fwrite(rBuffer, 1, (size_t)st.st_size, f);
+               fclose(f);
+       }
+       else
+       {
+               unlink(name);
+       }
+       
+       free(name);
+}
+
+
+
+static const char* gHMACText = "The judicial Power shall extend to all Cases, in "
+                                                          "Law and Equity, arising under this Constitution, "
+                                                          "the Laws of the United States, and Treaties made, "
+                                                          "or which shall be made, under their Authority;--to "
+                                                          "all Cases affecting Ambassadors, other public "
+                                                          "Ministers and Consuls;--to all Cases of admiralty "
+                                                          "and maritime Jurisdiction;--to Controversies to "
+                                                          "which the United States shall be a Party;--to "
+                                                          "Controversies between two or more States;-- "
+                                                          "between a State and Citizens of another State, "
+                                                          "--between Citizens of different States,-- "
+                                                          "between Citizens of the same State claiming Lands "
+                                                          "under Grants of different States, and between a "
+                                                          "State, or the Citizens thereof, and foreign "
+                                                          "States, Citizens or Subjects";
+
+const NSString* gAbortTransformName = (NSString*) kSecTransformAbortAttributeName;
+
+static const char* gHMACKey = "No person shall be held to answer for a capital, or "
+                                                         "otherwise infamous crime, unless on a presentment "
+                                                         "or indictment of a Grand Jury, except in cases "
+                                                         "arising in the land or naval forces, or in the "
+                                                         "Militia, when in actual service in time of War or "
+                                                         "public danger; nor shall any person be subject for "
+                                                         "the same offence to be twice put in jeopardy of life "
+                                                         "or limb; nor shall be compelled in any criminal case "
+                                                         "to be a witness against himself, nor be deprived of "
+                                                         "life, liberty, or property, without due process of "
+                                                         "law; nor shall private property be taken for public "
+                                                         "use, without just compensation.";
+
+static const u_int8_t gSHA1HMAC[] = {0x2f, 0x68, 0x4b, 0x6b, 0x4f,
+                                                                        0xf7, 0x41, 0xc3, 0x76, 0x3d,
+                                                                        0x0b, 0xc3, 0x25, 0x02, 0x99,
+                                                                        0x03, 0xfa, 0xa5, 0xe9, 0xde};
+
+static const u_int8_t gSHA256HMAC[] = {0xc2, 0x5c, 0x9a, 0x65, 0x08, 0x9e, 0x61, 0xb5,
+                                                                          0x03, 0xfe, 0xcb, 0x57, 0xb7, 0x55, 0x4f, 0x69,
+                                                                          0xdb, 0xef, 0xdb, 0xe7, 0x0d, 0xe2, 0x78, 0x2e,
+                                                                          0xf9, 0x48, 0xbd, 0xf6, 0x4f, 0x4b, 0x94, 0x0c};
+-(void)testPaddings
+{
+    CFStringRef paddings[] = {kSecPaddingNoneKey, kSecPaddingPKCS7Key, kSecPaddingPKCS5Key, kSecPaddingPKCS1Key};
+
+    for(int i = 0; i < sizeof(paddings) / sizeof(*paddings); i++) {
+        CFErrorRef error = NULL;
+        SecKeyRef cryptokey = NULL;
+        SecTransformRef encrypt = NULL, decrypt = NULL;
+        CFDataRef cfdatacryptokey = NULL, sourceData = NULL, encryptedData = NULL, decryptedData = NULL;
+        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.
+        const char *sourceCString = "All these worlds are yours except Europa.";  // I'm not so sure about that Earth one either
+
+        CFMutableDictionaryRef parameters = CFDictionaryCreateMutable(
+                                                                      kCFAllocatorDefault, 0, &kCFTypeDictionaryKeyCallBacks,
+                                                                      &kCFTypeDictionaryValueCallBacks);
+
+        CFDictionarySetValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeAES);
+
+        cfdatacryptokey = CFDataCreate(kCFAllocatorDefault, rawcryptokey,
+                                       sizeof(rawcryptokey));
+        cryptokey = SecKeyCreateFromData(parameters,
+                                         cfdatacryptokey, &error);
+        STAssertNil((id)error, @"Unexpected SecKeyCreateFromData error: %@", error);
+
+        size_t len = strlen(sourceCString) +1;
+        if (paddings[i] == kSecPaddingNoneKey) {
+            STAssertTrue(len >= kCCBlockSizeAES128, @"Had at least one block");
+            // Get to an AES block multiple, discarding bytes wildly.
+            len -= len % kCCBlockSizeAES128;
+        }
+        sourceData = (CFDataRef)[NSData dataWithBytes:sourceCString length:len];
+
+        encrypt = SecEncryptTransformCreate(cryptokey, &error);
+        STAssertNil((id)error, @"Unexpected error creating encrypt transform: %@", error);
+        decrypt = SecDecryptTransformCreate(cryptokey, &error);
+        STAssertNil((id)error, @"Unexpected error creating decrypt transform: %@", error);
+
+        /* Set the padding on the transforms */
+        SecTransformSetAttribute(encrypt, kSecPaddingKey, paddings[i], &error);
+        STAssertNil((id)error, @"Couldn't set encrypt padding to %@: %@", paddings[i], error);
+        SecTransformSetAttribute(decrypt, kSecPaddingKey, paddings[i], &error);
+        STAssertNil((id)error, @"Couldn't set decrypt padding to %@: %@", paddings[i], error);
+
+        SecTransformSetAttribute(encrypt, kSecTransformInputAttributeName, sourceData, &error);
+        STAssertNil((id)error, @"Couldn't set encrypt transform input: %@", error);
+
+        encryptedData = (CFDataRef)SecTransformExecute(encrypt, &error);
+        STAssertNil((id)error, @"Couldn't execute encrypt: %@ (padding %@)", paddings[i], error);
+        STAssertNotNil((id)encryptedData, @"Didn't get encrypted data");
+
+        SecTransformSetAttribute(decrypt, kSecTransformInputAttributeName, encryptedData, &error);
+        STAssertNil((id)error, @"Couldn't set decrypt transform input: %@", error);
+
+        decryptedData = (CFDataRef)SecTransformExecute(decrypt, &error);
+        STAssertNil((id)error, @"Couldn't execute decrypt: %@", error);
+        STAssertNotNil((id)decryptedData, @"Didn't get decrypt data");
+
+        STAssertEqualObjects((id)decryptedData, (id)sourceData, @"Decrypt output didn't match encrypt input for padding %@", paddings[i]);
+    }
+}
+
+static SecTransformInstanceBlock nopInstance(CFStringRef name, SecTransformRef newTransform, SecTransformImplementationRef ref)
+{
+    SecTransformInstanceBlock instanceBlock = ^{
+        return (CFErrorRef)NULL;
+    };
+    
+    return Block_copy(instanceBlock);
+}
+
+
+-(void)test_manyregister
+{
+    dispatch_apply(4000, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^(size_t i) {
+        NSString *name = [NSString stringWithFormat:@"many%luregister", i];
+        CFErrorRef err = NULL;
+        BOOL ok = SecTransformRegister((CFStringRef)name, nopInstance, &err);
+        STAssertTrue(ok, @"register not ok");
+        STAssertNil((id)err, @"register error: %@", err);
+    });
+}
+
+-(void)test_emptyOAEP
+{
+    SecKeychainRef tmp_keychain = NULL;
+    char *kcfname;
+    asprintf(&kcfname, "%s-OAEP-XXXXXXXXXX", "/tmp/");
+    const char *passwd = "sekret";
+    // NOTE: "mktemp" isn't as safe as you might think...but this is test code and doesn't have to be, but
+    // if you copy it elsewhere you may well need to rewrite it.  (use mkstemp)
+    mktemp(kcfname);                   
+    OSStatus status = SecKeychainCreate(kcfname, (UInt32)strlen(passwd), passwd, NO, NULL, &tmp_keychain);
+    STAssertTrue(status == 0, @"Expected to make keychain, but got error 0x%x", status);
+    
+    const char *pem_key_bytes[] = {
+        // From the spec
+        "-----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",
+        NULL,
+    };
+    struct key_pair {
+        SecKeyRef pubKey, privKey;
+    };
+    key_pair keys[1];
+    
+    int i;
+    for(i = 0; i < sizeof(keys)/sizeof(key_pair); i++) {
+        NSAssert(pem_key_bytes[i] != NULL, @"Expected a key");
+        NSLog(@"Importing: %s", pem_key_bytes[i]);
+        CFDataRef pem_data = CFDataCreate(NULL, (UInt8*)(pem_key_bytes[i]), strlen(pem_key_bytes[i]));
+        SecKeyImportExportParameters import_params;
+        bzero(&import_params, sizeof(import_params));
+        
+        import_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+        import_params.keyUsage = CSSM_KEYUSE_ANY;
+        import_params.keyAttributes = CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE;
+        import_params.accessRef = NULL;
+        import_params.passphrase = CFSTR("");
+        import_params.alertPrompt = CFSTR("");
+        
+        CFArrayRef keypair = NULL;
+        SecExternalFormat key_format = kSecFormatOpenSSL;
+        SecExternalItemType itemType = kSecItemTypeUnknown;
+        status = SecKeychainItemImport(pem_data, CFSTR(".pem"), &key_format, &itemType, 0, &import_params, tmp_keychain, &keypair);
+        STAssertTrue(status == 0, @"Expected pubkey import to be ok, got err=0x%x", status);
+        NSAssert(keypair != NULL, @"Expected to get some keys back");
+        STAssertNotNil((id)keypair, @"Expected to get some keys back");
+        STAssertTrue(CFArrayGetCount(keypair) == 2, @"Expected 2 keys, got %@", keypair);
+        keys[i].pubKey = (SecKeyRef)CFArrayGetValueAtIndex(keypair, 0);
+        keys[i].privKey = (SecKeyRef)CFArrayGetValueAtIndex(keypair, 1);
+    }
+    STAssertNil((id)pem_key_bytes[i], @"Expected to convert all pem keys, but found at least: %s", pem_key_bytes[i]);
+    CFDataRef encoding_parameters = CFDataCreate(NULL, NULL, 0);
+    
+       
+       CFErrorRef err = NULL;
+       
+       SecTransformRef encryptor = SecEncryptTransformCreate(keys[0].pubKey, &err);
+       
+       CFReadStreamRef empty_stream = CFReadStreamCreateWithBytesNoCopy(NULL, (UInt8*)"", 0, kCFAllocatorNull);
+       SecTransformSetAttribute(encryptor, kSecTransformInputAttributeName, empty_stream, &err);
+       SecTransformSetAttribute(encryptor, kSecPaddingKey, kSecPaddingOAEPKey, &err);
+       SecTransformSetAttribute(encryptor, kSecOAEPEncodingParametersAttributeName, encoding_parameters, &err);
+       
+       CFTypeRef encryptedData = SecTransformExecute(encryptor, &err);
+       STAssertNotNil((id)encryptedData, @"Expected to get encrypted data");
+       STAssertNil((NSError*)err, @"Expected no error, got err=%@", err);
+       // Can't support "seed" with commoncrypto, just check round trip.
+       //STAssertEqualObjects((id)encryptedData, (id)tests[i].encryptedMessage, @"encrypted data should have matched test vector (%@) data", tests[i].label);
+       CFRelease(encryptor);
+       
+       SecTransformRef decryptor = SecDecryptTransformCreate(keys[0].privKey, NULL);
+       // XXX: totally round trip, not even partial KAT (KAT can't really be done on OAEP
+       // without supporitng settign the seed externally)
+       SecTransformSetAttribute(decryptor, kSecTransformInputAttributeName, encryptedData, NULL);
+       SecTransformSetAttribute(decryptor, kSecPaddingKey, kSecPaddingOAEPKey, NULL);
+       SecTransformSetAttribute(decryptor, kSecOAEPEncodingParametersAttributeName, encoding_parameters, NULL);
+       CFTypeRef decryptedData = SecTransformExecute(decryptor, &err);
+       STAssertNil((id)err, @"Expected no error, got: %@", err);
+       STAssertNotNil((id)decryptedData, @"Expected to get decrypted data");
+       // What do we expect an empty enc/dec to look like?   Mostly "not a crash"
+       CFDataRef empty_data = CFDataCreate(NULL, (UInt8*)"", 0);
+       STAssertEqualObjects((id)decryptedData, (id)empty_data, @"Expected decrypted data to match original message");
+       CFRelease(decryptor);
+    sleep(5);
+       
+    return;
+}
+
+-(void)testzzzzZZZZ
+{
+       // Give xcode a little time to parse all the output before the unit tests exit
+       sleep(2);
+}
+
+-(void)test_multiOAEP
+{
+    SecKeychainRef tmp_keychain = NULL;
+    char *kcfname;
+    asprintf(&kcfname, "%s-OAEP-XXXXXXXXXX", "/tmp/");
+    const char *passwd = "sekret";
+    // NOTE: "mktemp" isn't as safe as you might think...but this is test code and doesn't have to be, but
+    // if you copy it elsewhere you may well need to rewrite it.  (use mkstemp)
+    mktemp(kcfname);                   
+    OSStatus status = SecKeychainCreate(kcfname, (UInt32)strlen(passwd), passwd, NO, NULL, &tmp_keychain);
+    STAssertTrue(status == 0, @"Expected to make keychain, but got error 0x%x", status);
+    
+    const char *pem_key_bytes[] = {
+        // From the spec
+        "-----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",
+        // The next 10 are from oaep-vect.txt (via a lot of OpenSSL higgerdy-jiggerdey)
+        "-----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",
+        "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",
+        "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",
+        "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",
+        "-----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",
+        "-----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",
+        "-----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",
+        "-----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",
+        "-----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",
+        "-----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",
+        NULL,
+    };
+    struct key_pair {
+        SecKeyRef pubKey, privKey;
+    };
+    key_pair keys[11];
+    
+    int i;
+    for(i = 0; i < sizeof(keys)/sizeof(key_pair); i++) {
+        NSAssert(pem_key_bytes[i] != NULL, @"Expected a key");
+        NSLog(@"Importing: %s", pem_key_bytes[i]);
+        CFDataRef pem_data = CFDataCreate(NULL, (UInt8*)(pem_key_bytes[i]), strlen(pem_key_bytes[i]));
+        SecKeyImportExportParameters import_params;
+        bzero(&import_params, sizeof(import_params));
+        
+        import_params.version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
+        import_params.keyUsage = CSSM_KEYUSE_ANY;
+        import_params.keyAttributes = CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_SENSITIVE;
+        import_params.accessRef = NULL;
+        import_params.passphrase = CFSTR("");
+        import_params.alertPrompt = CFSTR("");
+        
+        CFArrayRef keypair = NULL;
+        SecExternalFormat key_format = kSecFormatOpenSSL;
+        SecExternalItemType itemType = kSecItemTypeUnknown;
+        status = SecKeychainItemImport(pem_data, CFSTR(".pem"), &key_format, &itemType, 0, &import_params, tmp_keychain, &keypair);
+        STAssertTrue(status == 0, @"Expected pubkey import to be ok, got err=0x%x", status);
+        NSAssert(keypair != NULL, @"Expected to get some keys back");
+        STAssertNotNil((id)keypair, @"Expected to get some keys back");
+        STAssertTrue(CFArrayGetCount(keypair) == 2, @"Expected 2 keys, got %@", keypair);
+        keys[i].pubKey = (SecKeyRef)CFArrayGetValueAtIndex(keypair, 0);
+        keys[i].privKey = (SecKeyRef)CFArrayGetValueAtIndex(keypair, 1);
+    }
+    STAssertNil((id)pem_key_bytes[i], @"Expected to convert all pem keys, but found at least: %s", pem_key_bytes[i]);
+    CFDataRef encoding_parameters = CFDataCreate(NULL, NULL, 0);
+    
+    struct KAT {
+        NSData *message, *seed, *encryptedMessage;
+        NSString *label;
+        key_pair keys;
+    };
+    KAT tests[] = {
+        // This first one is from the spec
+        {
+            .message = [NSData dataWithHexString:@"d436e99569fd32a7c8a05bbc90d32c49"],
+            .seed = [NSData dataWithHexString:@"aafd12f659cae63489b479e5076ddec2f06cb58f"],
+            .encryptedMessage = [NSData dataWithHexString:@"1253e04dc0a5397bb44a7ab87e9bf2a039a33d1e996fc82a94ccd30074c95df763722017069e5268da5d1c0b4f872cf653c11df82314a67968dfeae28def04bb6d84b1c31d654a1970e5783bd6eb96a024c2ca2f4a90fe9f2ef5c9c140e5bb48da9536ad8700c84fc9130adea74e558d51a74ddf85d8b50de96838d6063e0955"],
+            .keys = keys[0],
+            .label = @"From spec",
+        },
+        // The next 60 are from oaep-vect.txt
+        {
+            .message = [NSData dataWithHexString:@"6628194e12073db03ba94cda9ef9532397d50dba79b987004afefe34"],
+            .seed = [NSData dataWithHexString:@"18b776ea21069d69776a33e96bad48e1dda0a5ef"],
+            .encryptedMessage = [NSData dataWithHexString:@"354fe67b4a126d5d35fe36c777791a3f7ba13def484e2d3908aff722fad468fb21696de95d0be911c2d3174f8afcc201035f7b6d8e69402de5451618c21a535fa9d7bfc5b8dd9fc243f8cf927db31322d6e881eaa91a996170e657a05a266426d98c88003f8477c1227094a0d9fa1e8c4024309ce1ecccb5210035d47ac72e8a"],
+            .keys = keys[1],
+            .label = @"1-1",
+        },
+        {
+            .message = [NSData dataWithHexString:@"750c4047f547e8e41411856523298ac9bae245efaf1397fbe56f9dd5"],
+            .seed = [NSData dataWithHexString:@"0cc742ce4a9b7f32f951bcb251efd925fe4fe35f"],
+            .encryptedMessage = [NSData dataWithHexString:@"640db1acc58e0568fe5407e5f9b701dff8c3c91e716c536fc7fcec6cb5b71c1165988d4a279e1577d730fc7a29932e3f00c81515236d8d8e31017a7a09df4352d904cdeb79aa583adcc31ea698a4c05283daba9089be5491f67c1a4ee48dc74bbbe6643aef846679b4cb395a352d5ed115912df696ffe0702932946d71492b44"],
+            .keys = keys[1],
+            .label = @"1-2",
+        },
+        {
+            .message = [NSData dataWithHexString:@"d94ae0832e6445ce42331cb06d531a82b1db4baad30f746dc916df24d4e3c2451fff59a6423eb0e1d02d4fe646cf699dfd818c6e97b051"],
+            .seed = [NSData dataWithHexString:@"2514df4695755a67b288eaf4905c36eec66fd2fd"],
+            .encryptedMessage = [NSData dataWithHexString:@"423736ed035f6026af276c35c0b3741b365e5f76ca091b4e8c29e2f0befee603595aa8322d602d2e625e95eb81b2f1c9724e822eca76db8618cf09c5343503a4360835b5903bc637e3879fb05e0ef32685d5aec5067cd7cc96fe4b2670b6eac3066b1fcf5686b68589aafb7d629b02d8f8625ca3833624d4800fb081b1cf94eb"],
+            .keys = keys[1],
+            .label = @"1-3",
+        },
+        {
+            .message = [NSData dataWithHexString:@"52e650d98e7f2a048b4f86852153b97e01dd316f346a19f67a85"],
+            .seed = [NSData dataWithHexString:@"c4435a3e1a18a68b6820436290a37cefb85db3fb"],
+            .encryptedMessage = [NSData dataWithHexString:@"45ead4ca551e662c9800f1aca8283b0525e6abae30be4b4aba762fa40fd3d38e22abefc69794f6ebbbc05ddbb11216247d2f412fd0fba87c6e3acd888813646fd0e48e785204f9c3f73d6d8239562722dddd8771fec48b83a31ee6f592c4cfd4bc88174f3b13a112aae3b9f7b80e0fc6f7255ba880dc7d8021e22ad6a85f0755"],
+            .keys = keys[1],
+            .label = @"1-4",
+        },
+        {
+            .message = [NSData dataWithHexString:@"8da89fd9e5f974a29feffb462b49180f6cf9e802"],
+            .seed = [NSData dataWithHexString:@"b318c42df3be0f83fea823f5a7b47ed5e425a3b5"],
+            .encryptedMessage = [NSData dataWithHexString:@"36f6e34d94a8d34daacba33a2139d00ad85a9345a86051e73071620056b920e219005855a213a0f23897cdcd731b45257c777fe908202befdd0b58386b1244ea0cf539a05d5d10329da44e13030fd760dcd644cfef2094d1910d3f433e1c7c6dd18bc1f2df7f643d662fb9dd37ead9059190f4fa66ca39e869c4eb449cbdc439"],
+            .keys = keys[1],
+            .label = @"1-5",
+        },
+        {
+            .message = [NSData dataWithHexString:@"26521050844271"],
+            .seed = [NSData dataWithHexString:@"e4ec0982c2336f3a677f6a356174eb0ce887abc2"],
+            .encryptedMessage = [NSData dataWithHexString:@"42cee2617b1ecea4db3f4829386fbd61dafbf038e180d837c96366df24c097b4ab0fac6bdf590d821c9f10642e681ad05b8d78b378c0f46ce2fad63f74e0ad3df06b075d7eb5f5636f8d403b9059ca761b5c62bb52aa45002ea70baace08ded243b9d8cbd62a68ade265832b56564e43a6fa42ed199a099769742df1539e8255"],
+            .keys = keys[1],
+            .label = @"1-6",
+        },
+        
+        {
+            .message = [NSData dataWithHexString:@"8ff00caa605c702830634d9a6c3d42c652b58cf1d92fec570beee7"],
+            .seed = [NSData dataWithHexString:@"8c407b5ec2899e5099c53e8ce793bf94e71b1782"],
+            .encryptedMessage = [NSData dataWithHexString:@"0181af8922b9fcb4d79d92ebe19815992fc0c1439d8bcd491398a0f4ad3a329a5bd9385560db532683c8b7da04e4b12aed6aacdf471c34c9cda891addcc2df3456653aa6382e9ae59b54455257eb099d562bbe10453f2b6d13c59c02e10f1f8abb5da0d0570932dacf2d0901db729d0fefcc054e70968ea540c81b04bcaefe720e"],
+            .keys = keys[2],
+            .label = @"2-1",
+        },
+        {
+            .message = [NSData dataWithHexString:@"2d"],
+            .seed = [NSData dataWithHexString:@"b600cf3c2e506d7f16778c910d3a8b003eee61d5"],
+            .encryptedMessage = [NSData dataWithHexString:@"018759ff1df63b2792410562314416a8aeaf2ac634b46f940ab82d64dbf165eee33011da749d4bab6e2fcd18129c9e49277d8453112b429a222a8471b070993998e758861c4d3f6d749d91c4290d332c7a4ab3f7ea35ff3a07d497c955ff0ffc95006b62c6d296810d9bfab024196c7934012c2df978ef299aba239940cba10245"],
+            .keys = keys[2],
+            .label = @"2-2",
+        },
+        {
+            .message = [NSData dataWithHexString:@"74fc88c51bc90f77af9d5e9a4a70133d4b4e0b34da3c37c7ef8e"],
+            .seed = [NSData dataWithHexString:@"a73768aeeaa91f9d8c1ed6f9d2b63467f07ccae3"],
+            .encryptedMessage = [NSData dataWithHexString:@"018802bab04c60325e81c4962311f2be7c2adce93041a00719c88f957575f2c79f1b7bc8ced115c706b311c08a2d986ca3b6a9336b147c29c6f229409ddec651bd1fdd5a0b7f610c9937fdb4a3a762364b8b3206b4ea485fd098d08f63d4aa8bb2697d027b750c32d7f74eaf5180d2e9b66b17cb2fa55523bc280da10d14be2053"],
+            .keys = keys[2],
+            .label = @"2-3",
+        },
+        {
+            .message = [NSData dataWithHexString:@"a7eb2a5036931d27d4e891326d99692ffadda9bf7efd3e34e622c4adc085f721dfe885072c78a203b151739be540fa8c153a10f00a"],
+            .seed = [NSData dataWithHexString:@"9a7b3b0e708bd96f8190ecab4fb9b2b3805a8156"],
+            .encryptedMessage = [NSData dataWithHexString:@"00a4578cbc176318a638fba7d01df15746af44d4f6cd96d7e7c495cbf425b09c649d32bf886da48fbaf989a2117187cafb1fb580317690e3ccd446920b7af82b31db5804d87d01514acbfa9156e782f867f6bed9449e0e9a2c09bcecc6aa087636965e34b3ec766f2fe2e43018a2fddeb140616a0e9d82e5331024ee0652fc7641"],
+            .keys = keys[2],
+            .label = @"2-4",
+        },
+        {
+            .message = [NSData dataWithHexString:@"2ef2b066f854c33f3bdcbb5994a435e73d6c6c"],
+            .seed = [NSData dataWithHexString:@"eb3cebbc4adc16bb48e88c8aec0e34af7f427fd3"],
+            .encryptedMessage = [NSData dataWithHexString:@"00ebc5f5fda77cfdad3c83641a9025e77d72d8a6fb33a810f5950f8d74c73e8d931e8634d86ab1246256ae07b6005b71b7f2fb98351218331ce69b8ffbdc9da08bbc9c704f876deb9df9fc2ec065cad87f9090b07acc17aa7f997b27aca48806e897f771d95141fe4526d8a5301b678627efab707fd40fbebd6e792a25613e7aec"],
+            .keys = keys[2],
+            .label = @"2-5",
+        },
+        {
+            .message = [NSData dataWithHexString:@"8a7fb344c8b6cb2cf2ef1f643f9a3218f6e19bba89c0"],
+            .seed = [NSData dataWithHexString:@"4c45cf4d57c98e3d6d2095adc51c489eb50dff84"],
+            .encryptedMessage = [NSData dataWithHexString:@"010839ec20c27b9052e55befb9b77e6fc26e9075d7a54378c646abdf51e445bd5715de81789f56f1803d9170764a9e93cb78798694023ee7393ce04bc5d8f8c5a52c171d43837e3aca62f609eb0aa5ffb0960ef04198dd754f57f7fbe6abf765cf118b4ca443b23b5aab266f952326ac4581100644325f8b721acd5d04ff14ef3a"],
+            .keys = keys[2],
+            .label = @"2-6",
+        },
+        
+        {
+            .message = [NSData dataWithHexString:@"087820b569e8fa8d"],
+            .seed = [NSData dataWithHexString:@"8ced6b196290805790e909074015e6a20b0c4894"],
+            .encryptedMessage = [NSData dataWithHexString:@"026a0485d96aebd96b4382085099b962e6a2bdec3d90c8db625e14372de85e2d5b7baab65c8faf91bb5504fb495afce5c988b3f6a52e20e1d6cbd3566c5cd1f2b8318bb542cc0ea25c4aab9932afa20760eaddec784396a07ea0ef24d4e6f4d37e5052a7a31e146aa480a111bbe926401307e00f410033842b6d82fe5ce4dfae80"],
+            .keys = keys[3],
+            .label = @"3-1",
+        },
+        {
+            .message = [NSData dataWithHexString:@"4653acaf171960b01f52a7be63a3ab21dc368ec43b50d82ec3781e04"],
+            .seed = [NSData dataWithHexString:@"b4291d6567550848cc156967c809baab6ca507f0"],
+            .encryptedMessage = [NSData dataWithHexString:@"024db89c7802989be0783847863084941bf209d761987e38f97cb5f6f1bc88da72a50b73ebaf11c879c4f95df37b850b8f65d7622e25b1b889e80fe80baca2069d6e0e1d829953fc459069de98ea9798b451e557e99abf8fe3d9ccf9096ebbf3e5255d3b4e1c6d2ecadf067a359eea86405acd47d5e165517ccafd47d6dbee4bf5"],
+            .keys = keys[3],
+            .label = @"3-2",
+        },
+        {
+            .message = [NSData dataWithHexString:@"d94cd0e08fa404ed89"],
+            .seed = [NSData dataWithHexString:@"ce8928f6059558254008badd9794fadcd2fd1f65"],
+            .encryptedMessage = [NSData dataWithHexString:@"0239bce681032441528877d6d1c8bb28aa3bc97f1df584563618995797683844ca86664732f4bed7a0aab083aaabfb7238f582e30958c2024e44e57043b97950fd543da977c90cdde5337d618442f99e60d7783ab59ce6dd9d69c47ad1e962bec22d05895cff8d3f64ed5261d92b2678510393484990ba3f7f06818ae6ffce8a3a"],
+            .keys = keys[3],
+            .label = @"3-3",
+        },
+        {
+            .message = [NSData dataWithHexString:@"6cc641b6b61e6f963974dad23a9013284ef1"],
+            .seed = [NSData dataWithHexString:@"6e2979f52d6814a57d83b090054888f119a5b9a3"],
+            .encryptedMessage = [NSData dataWithHexString:@"02994c62afd76f498ba1fd2cf642857fca81f4373cb08f1cbaee6f025c3b512b42c3e8779113476648039dbe0493f9246292fac28950600e7c0f32edf9c81b9dec45c3bde0cc8d8847590169907b7dc5991ceb29bb0714d613d96df0f12ec5d8d3507c8ee7ae78dd83f216fa61de100363aca48a7e914ae9f42ddfbe943b09d9a0"],
+            .keys = keys[3],
+            .label = @"3-4",
+        },
+        {
+            .message = [NSData dataWithHexString:@"df5151832b61f4f25891fb4172f328d2eddf8371ffcfdbe997939295f30eca6918017cfda1153bf7a6af87593223"],
+            .seed = [NSData dataWithHexString:@"2d760bfe38c59de34cdc8b8c78a38e66284a2d27"],
+            .encryptedMessage = [NSData dataWithHexString:@"0162042ff6969592a6167031811a239834ce638abf54fec8b99478122afe2ee67f8c5b18b0339805bfdbc5a4e6720b37c59cfba942464c597ff532a119821545fd2e59b114e61daf71820529f5029cf524954327c34ec5e6f5ba7efcc4de943ab8ad4ed787b1454329f70db798a3a8f4d92f8274e2b2948ade627ce8ee33e43c60"],
+            .keys = keys[3],
+            .label = @"3-5",
+        },
+        {
+            .message = [NSData dataWithHexString:@"3c3bad893c544a6d520ab022319188c8d504b7a788b850903b85972eaa18552e1134a7ad6098826254ff7ab672b3d8eb3158fac6d4cbaef1"],
+            .seed = [NSData dataWithHexString:@"f174779c5fd3cfe007badcb7a36c9b55bfcfbf0e"],
+            .encryptedMessage = [NSData dataWithHexString:@"00112051e75d064943bc4478075e43482fd59cee0679de6893eec3a943daa490b9691c93dfc0464b6623b9f3dbd3e70083264f034b374f74164e1a00763725e574744ba0b9db83434f31df96f6e2a26f6d8eba348bd4686c2238ac07c37aac3785d1c7eea2f819fd91491798ed8e9cef5e43b781b0e0276e37c43ff9492d005730"],
+            .label = @"3-6",
+            .keys = keys[3],
+        },
+        
+        {
+            .message = [NSData dataWithHexString:@"4a86609534ee434a6cbca3f7e962e76d455e3264c19f605f6e5ff6137c65c56d7fb344cd52bc93374f3d166c9f0c6f9c506bad19330972d2"],
+            .seed = [NSData dataWithHexString:@"1cac19ce993def55f98203f6852896c95ccca1f3"],
+            .encryptedMessage = [NSData dataWithHexString:@"04cce19614845e094152a3fe18e54e3330c44e5efbc64ae16886cb1869014cc5781b1f8f9e045384d0112a135ca0d12e9c88a8e4063416deaae3844f60d6e96fe155145f4525b9a34431ca3766180f70e15a5e5d8e8b1a516ff870609f13f896935ced188279a58ed13d07114277d75c6568607e0ab092fd803a223e4a8ee0b1a8"],
+            .keys = keys[4],
+            .label = @"4-1",
+        },
+        {
+            .message = [NSData dataWithHexString:@"b0adc4f3fe11da59ce992773d9059943c03046497ee9d9f9a06df1166db46d98f58d27ec074c02eee6cbe2449c8b9fc5080c5c3f4433092512ec46aa793743c8"],
+            .seed = [NSData dataWithHexString:@"f545d5897585e3db71aa0cb8da76c51d032ae963"],
+            .encryptedMessage = [NSData dataWithHexString:@"0097b698c6165645b303486fbf5a2a4479c0ee85889b541a6f0b858d6b6597b13b854eb4f839af03399a80d79bda6578c841f90d645715b280d37143992dd186c80b949b775cae97370e4ec97443136c6da484e970ffdb1323a20847821d3b18381de13bb49aaea66530c4a4b8271f3eae172cd366e07e6636f1019d2a28aed15e"],
+            .keys = keys[4],
+            .label = @"4-2",
+        },
+        {
+            .message = [NSData dataWithHexString:@"bf6d42e701707b1d0206b0c8b45a1c72641ff12889219a82bdea965b5e79a96b0d0163ed9d578ec9ada20f2fbcf1ea3c4089d83419ba81b0c60f3606da99"],
+            .seed = [NSData dataWithHexString:@"ad997feef730d6ea7be60d0dc52e72eacbfdd275"],
+            .encryptedMessage = [NSData dataWithHexString:@"0301f935e9c47abcb48acbbe09895d9f5971af14839da4ff95417ee453d1fd77319072bb7297e1b55d7561cd9d1bb24c1a9a37c619864308242804879d86ebd001dce5183975e1506989b70e5a83434154d5cbfd6a24787e60eb0c658d2ac193302d1192c6e622d4a12ad4b53923bca246df31c6395e37702c6a78ae081fb9d065"],
+            .keys = keys[4],
+            .label = @"4-3",
+        },
+        {
+            .message = [NSData dataWithHexString:@"fb2ef112f5e766eb94019297934794f7be2f6fc1c58e"],
+            .seed = [NSData dataWithHexString:@"136454df5730f73c807a7e40d8c1a312ac5b9dd3"],
+            .encryptedMessage = [NSData dataWithHexString:@"02d110ad30afb727beb691dd0cf17d0af1a1e7fa0cc040ec1a4ba26a42c59d0a796a2e22c8f357ccc98b6519aceb682e945e62cb734614a529407cd452bee3e44fece8423cc19e55548b8b994b849c7ecde4933e76037e1d0ce44275b08710c68e430130b929730ed77e09b015642c5593f04e4ffb9410798102a8e96ffdfe11e4"],
+            .keys = keys[4],
+            .label = @"4-4",
+        },
+        {
+            .message = [NSData dataWithHexString:@"28ccd447bb9e85166dabb9e5b7d1adadc4b9d39f204e96d5e440ce9ad928bc1c2284"],
+            .seed = [NSData dataWithHexString:@"bca8057f824b2ea257f2861407eef63d33208681"],
+            .encryptedMessage = [NSData dataWithHexString:@"00dbb8a7439d90efd919a377c54fae8fe11ec58c3b858362e23ad1b8a44310799066b99347aa525691d2adc58d9b06e34f288c170390c5f0e11c0aa3645959f18ee79e8f2be8d7ac5c23d061f18dd74b8c5f2a58fcb5eb0c54f99f01a83247568292536583340948d7a8c97c4acd1e98d1e29dc320e97a260532a8aa7a758a1ec2"],
+            .keys = keys[4],
+            .label = @"4-5",
+        },
+        {
+            .message = [NSData dataWithHexString:@"f22242751ec6b1"],
+            .seed = [NSData dataWithHexString:@"2e7e1e17f647b5ddd033e15472f90f6812f3ac4e"],
+            .encryptedMessage = [NSData dataWithHexString:@"00a5ffa4768c8bbecaee2db77e8f2eec99595933545520835e5ba7db9493d3e17cddefe6a5f567624471908db4e2d83a0fbee60608fc84049503b2234a07dc83b27b22847ad8920ff42f674ef79b76280b00233d2b51b8cb2703a9d42bfbc8250c96ec32c051e57f1b4ba528db89c37e4c54e27e6e64ac69635ae887d9541619a9"],
+            .keys = keys[4],
+            .label = @"4-6",
+        },
+        
+        {
+            .message = [NSData dataWithHexString:@"af71a901e3a61d3132f0fc1fdb474f9ea6579257ffc24d164170145b3dbde8"],
+            .seed = [NSData dataWithHexString:@"44c92e283f77b9499c603d963660c87d2f939461"],
+            .encryptedMessage = [NSData dataWithHexString:@"036046a4a47d9ed3ba9a89139c105038eb7492b05a5d68bfd53accff4597f7a68651b47b4a4627d927e485eed7b4566420e8b409879e5d606eae251d22a5df799f7920bfc117b992572a53b1263146bcea03385cc5e853c9a101c8c3e1bda31a519807496c6cb5e5efb408823a352b8fa0661fb664efadd593deb99fff5ed000e5"],
+            .keys = keys[5],
+            .label = @"5-1",
+        },
+        {
+            .message = [NSData dataWithHexString:@"a3b844a08239a8ac41605af17a6cfda4d350136585903a417a79268760519a4b4ac3303ec73f0f87cfb32399"],
+            .seed = [NSData dataWithHexString:@"cb28f5860659fceee49c3eeafce625a70803bd32"],
+            .encryptedMessage = [NSData dataWithHexString:@"03d6eb654edce615bc59f455265ed4e5a18223cbb9be4e4069b473804d5de96f54dcaaa603d049c5d94aa1470dfcd2254066b7c7b61ff1f6f6770e3215c51399fd4e34ec5082bc48f089840ad04354ae66dc0f1bd18e461a33cc1258b443a2837a6df26759aa2302334986f87380c9cc9d53be9f99605d2c9a97da7b0915a4a7ad"],
+            .keys = keys[5],
+            .label = @"5-2",
+        },
+        {
+            .message = [NSData dataWithHexString:@"308b0ecbd2c76cb77fc6f70c5edd233fd2f20929d629f026953bb62a8f4a3a314bde195de85b5f816da2aab074d26cb6acddf323ae3b9c678ac3cf12fbdde7"],
+            .seed = [NSData dataWithHexString:@"2285f40d770482f9a9efa2c72cb3ac55716dc0ca"],
+            .encryptedMessage = [NSData dataWithHexString:@"0770952181649f9f9f07ff626ff3a22c35c462443d905d456a9fd0bff43cac2ca7a9f554e9478b9acc3ac838b02040ffd3e1847de2e4253929f9dd9ee4044325a9b05cabb808b2ee840d34e15d105a3f1f7b27695a1a07a2d73fe08ecaaa3c9c9d4d5a89ff890d54727d7ae40c0ec1a8dd86165d8ee2c6368141016a48b55b6967"],
+            .keys = keys[5],
+            .label = @"5-3",
+        },
+        {
+            .message = [NSData dataWithHexString:@"15c5b9ee1185"],
+            .seed = [NSData dataWithHexString:@"49fa45d3a78dd10dfd577399d1eb00af7eed5513"],
+            .encryptedMessage = [NSData dataWithHexString:@"0812b76768ebcb642d040258e5f4441a018521bd96687e6c5e899fcd6c17588ff59a82cc8ae03a4b45b31299af1788c329f7dcd285f8cf4ced82606b97612671a45bedca133442144d1617d114f802857f0f9d739751c57a3f9ee400912c61e2e6992be031a43dd48fa6ba14eef7c422b5edc4e7afa04fdd38f402d1c8bb719abf"],
+            .keys = keys[5],
+            .label = @"5-4",
+        },
+        {
+            .message = [NSData dataWithHexString:@"21026e6800c7fa728fcaaba0d196ae28d7a2ac4ffd8abce794f0985f60c8a6737277365d3fea11db8923a2029a"],
+            .seed = [NSData dataWithHexString:@"f0287413234cc5034724a094c4586b87aff133fc"],
+            .encryptedMessage = [NSData dataWithHexString:@"07b60e14ec954bfd29e60d0047e789f51d57186c63589903306793ced3f68241c743529aba6a6374f92e19e0163efa33697e196f7661dfaaa47aac6bde5e51deb507c72c589a2ca1693d96b1460381249b2cdb9eac44769f2489c5d3d2f99f0ee3c7ee5bf64a5ac79c42bd433f149be8cb59548361640595513c97af7bc2509723"],
+            .keys = keys[5],
+            .label = @"5-5",
+        },
+        {
+            .message = [NSData dataWithHexString:@"541e37b68b6c8872b84c02"],
+            .seed = [NSData dataWithHexString:@"d9fba45c96f21e6e26d29eb2cdcb6585be9cb341"],
+            .encryptedMessage = [NSData dataWithHexString:@"08c36d4dda33423b2ed6830d85f6411ba1dcf470a1fae0ebefee7c089f256cef74cb96ea69c38f60f39abee44129bcb4c92de7f797623b20074e3d9c2899701ed9071e1efa0bdd84d4c3e5130302d8f0240baba4b84a71cc032f2235a5ff0fae277c3e8f9112bef44c9ae20d175fc9a4058bfc930ba31b02e2e4f444483710f24a"],
+            .keys = keys[5],
+            .label = @"5-6",
+        },
+        {
+            .label = @"6-1",
+            .keys = keys[6],
+            .message = [NSData dataWithHexString:@"4046ca8baa3347ca27f49e0d81f9cc1d71be9ba517d4"],
+            .seed = [NSData dataWithHexString:@"dd0f6cfe415e88e5a469a51fbba6dfd40adb4384"],
+            .encryptedMessage = [NSData dataWithHexString:@"0630eebcd2856c24f798806e41f9e67345eda9ceda386acc9facaea1eeed06ace583709718d9d169fadf414d5c76f92996833ef305b75b1e4b95f662a20faedc3bae0c4827a8bf8a88edbd57ec203a27a841f02e43a615bab1a8cac0701de34debdef62a088089b55ec36ea7522fd3ec8d06b6a073e6df833153bc0aefd93bd1a3"],
+        },
+        {
+            .label = @"6-2",
+            .keys = keys[6],
+            .message = [NSData dataWithHexString:@"5cc72c60231df03b3d40f9b57931bc31109f972527f28b19e7480c7288cb3c92b22512214e4be6c914792ddabdf57faa8aa7"],
+            .seed = [NSData dataWithHexString:@"8d14bd946a1351148f5cae2ed9a0c653e85ebd85"],
+            .encryptedMessage = [NSData dataWithHexString:@"0ebc37376173a4fd2f89cc55c2ca62b26b11d51c3c7ce49e8845f74e7607317c436bc8d23b9667dfeb9d087234b47bc6837175ae5c0559f6b81d7d22416d3e50f4ac533d8f0812f2db9e791fe9c775ac8b6ad0f535ad9ceb23a4a02014c58ab3f8d3161499a260f39348e714ae2a1d3443208fd8b722ccfdfb393e98011f99e63f"],
+        },
+        {
+            .label = @"6-3",
+            .keys = keys[6],
+            .message = [NSData dataWithHexString:@"b20e651303092f4bccb43070c0f86d23049362ed96642fc5632c27db4a52e3d831f2ab068b23b149879c002f6bf3feee97591112562c"],
+            .seed = [NSData dataWithHexString:@"6c075bc45520f165c0bf5ea4c5df191bc9ef0e44"],
+            .encryptedMessage = [NSData dataWithHexString:@"0a98bf1093619394436cf68d8f38e2f158fde8ea54f3435f239b8d06b8321844202476aeed96009492480ce3a8d705498c4c8c68f01501dc81db608f60087350c8c3b0bd2e9ef6a81458b7c801b89f2e4fe99d4900ba6a4b5e5a96d865dc676c7755928794130d6280a8160a190f2df3ea7cf9aa0271d88e9e6905ecf1c5152d65"],
+        },
+        {
+            .label = @"6-4",
+            .keys = keys[6],
+            .message = [NSData dataWithHexString:@"684e3038c5c041f7"],
+            .seed = [NSData dataWithHexString:@"3bbc3bd6637dfe12846901029bf5b0c07103439c"],
+            .encryptedMessage = [NSData dataWithHexString:@"008e7a67cacfb5c4e24bec7dee149117f19598ce8c45808fef88c608ff9cd6e695263b9a3c0ad4b8ba4c95238e96a8422b8535629c8d5382374479ad13fa39974b242f9a759eeaf9c83ad5a8ca18940a0162ba755876df263f4bd50c6525c56090267c1f0e09ce0899a0cf359e88120abd9bf893445b3cae77d3607359ae9a52f8"],
+        },
+        {
+            .label = @"6-5",
+            .keys = keys[6],
+            .message = [NSData dataWithHexString:@"32488cb262d041d6e4dd35f987bf3ca696db1f06ac29a44693"],
+            .seed = [NSData dataWithHexString:@"b46b41893e8bef326f6759383a83071dae7fcabc"],
+            .encryptedMessage = [NSData dataWithHexString:@"00003474416c7b68bdf961c385737944d7f1f40cb395343c693cc0b4fe63b31fedf1eaeeac9ccc0678b31dc32e0977489514c4f09085f6298a9653f01aea4045ff582ee887be26ae575b73eef7f3774921e375a3d19adda0ca31aa1849887c1f42cac9677f7a2f4e923f6e5a868b38c084ef187594dc9f7f048fea2e02955384ab"],
+        },
+        {
+            .label = @"6-6",
+            .keys = keys[6],
+            .message = [NSData dataWithHexString:@"50ba14be8462720279c306ba"],
+            .seed = [NSData dataWithHexString:@"0a2403312a41e3d52f060fbc13a67de5cf7609a7"],
+            .encryptedMessage = [NSData dataWithHexString:@"0a026dda5fc8785f7bd9bf75327b63e85e2c0fdee5dadb65ebdcac9ae1de95c92c672ab433aa7a8e69ce6a6d8897fac4ac4a54de841ae5e5bbce7687879d79634cea7a30684065c714d52409b928256bbf53eabcd5231eb7259504537399bd29164b726d33a46da701360a4168a091ccab72d44a62fed246c0ffea5b1348ab5470"],
+        },
+        {
+            .label = @"7-1",
+            .keys = keys[7],
+            .message = [NSData dataWithHexString:@"47aae909"],
+            .seed = [NSData dataWithHexString:@"43dd09a07ff4cac71caa4632ee5e1c1daee4cd8f"],
+            .encryptedMessage = [NSData dataWithHexString:@"1688e4ce7794bba6cb7014169ecd559cede2a30b56a52b68d9fe18cf1973ef97b2a03153951c755f6294aa49adbdb55845ab6875fb3986c93ecf927962840d282f9e54ce8b690f7c0cb8bbd73440d9571d1b16cd9260f9eab4783cc482e5223dc60973871783ec27b0ae0fd47732cbc286a173fc92b00fb4ba6824647cd93c85c1"],
+        },
+        {
+            .label = @"7-2",
+            .keys = keys[7],
+            .message = [NSData dataWithHexString:@"1d9b2e2223d9bc13bfb9f162ce735db48ba7c68f6822a0a1a7b6ae165834e7"],
+            .seed = [NSData dataWithHexString:@"3a9c3cec7b84f9bd3adecbc673ec99d54b22bc9b"],
+            .encryptedMessage = [NSData dataWithHexString:@"1052ed397b2e01e1d0ee1c50bf24363f95e504f4a03434a08fd822574ed6b9736edbb5f390db10321479a8a139350e2bd4977c3778ef331f3e78ae118b268451f20a2f01d471f5d53c566937171b2dbc2d4bde459a5799f0372d6574239b2323d245d0bb81c286b63c89a361017337e4902f88a467f4c7f244bfd5ab46437ff3b6"],
+        },
+        {
+            .label = @"7-3",
+            .keys = keys[7],
+            .message = [NSData dataWithHexString:@"d976fc"],
+            .seed = [NSData dataWithHexString:@"76a75e5b6157a556cf8884bb2e45c293dd545cf5"],
+            .encryptedMessage = [NSData dataWithHexString:@"2155cd843ff24a4ee8badb7694260028a490813ba8b369a4cbf106ec148e5298707f5965be7d101c1049ea8584c24cd63455ad9c104d686282d3fb803a4c11c1c2e9b91c7178801d1b6640f003f5728df007b8a4ccc92bce05e41a27278d7c85018c52414313a5077789001d4f01910b72aad05d220aa14a58733a7489bc54556b"],
+        },
+        {
+            .label = @"7-4",
+            .keys = keys[7],
+            .message = [NSData dataWithHexString:@"d4738623df223aa43843df8467534c41d013e0c803c624e263666b239bde40a5f29aeb8de79e3daa61dd0370f49bd4b013834b98212aef6b1c5ee373b3cb"],
+            .seed = [NSData dataWithHexString:@"7866314a6ad6f2b250a35941db28f5864b585859"],
+            .encryptedMessage = [NSData dataWithHexString:@"0ab14c373aeb7d4328d0aaad8c094d88b9eb098b95f21054a29082522be7c27a312878b637917e3d819e6c3c568db5d843802b06d51d9e98a2be0bf40c031423b00edfbff8320efb9171bd2044653a4cb9c5122f6c65e83cda2ec3c126027a9c1a56ba874d0fea23f380b82cf240b8cf540004758c4c77d934157a74f3fc12bfac"],
+        },
+        {
+            .label = @"7-5",
+            .keys = keys[7],
+            .message = [NSData dataWithHexString:@"bb47231ca5ea1d3ad46c99345d9a8a61"],
+            .seed = [NSData dataWithHexString:@"b2166ed472d58db10cab2c6b000cccf10a7dc509"],
+            .encryptedMessage = [NSData dataWithHexString:@"028387a318277434798b4d97f460068df5298faba5041ba11761a1cb7316b24184114ec500257e2589ed3b607a1ebbe97a6cc2e02bf1b681f42312a33b7a77d8e7855c4a6de03e3c04643f786b91a264a0d6805e2cea91e68177eb7a64d9255e4f27e713b7ccec00dc200ebd21c2ea2bb890feae4942df941dc3f97890ed347478"],
+        },
+        {
+            .label = @"7-6",
+            .keys = keys[7],
+            .message = [NSData dataWithHexString:@"2184827095d35c3f86f600e8e59754013296"],
+            .seed = [NSData dataWithHexString:@"52673bde2ca166c2aa46131ac1dc808d67d7d3b1"],
+            .encryptedMessage = [NSData dataWithHexString:@"14c678a94ad60525ef39e959b2f3ba5c097a94ff912b67dbace80535c187abd47d075420b1872152bba08f7fc31f313bbf9273c912fc4c0149a9b0cfb79807e346eb332069611bec0ff9bcd168f1f7c33e77313cea454b94e2549eecf002e2acf7f6f2d2845d4fe0aab2e5a92ddf68c480ae11247935d1f62574842216ae674115"],
+        },
+        {
+            .label = @"8-1",
+            .keys = keys[8],
+            .message = [NSData dataWithHexString:@"050b755e5e6880f7b9e9d692a74c37aae449b31bfea6deff83747a897f6c2c825bb1adbf850a3c96994b5de5b33cbc7d4a17913a7967"],
+            .seed = [NSData dataWithHexString:@"7706ffca1ecfb1ebee2a55e5c6e24cd2797a4125"],
+            .encryptedMessage = [NSData dataWithHexString:@"09b3683d8a2eb0fb295b62ed1fb9290b714457b7825319f4647872af889b30409472020ad12912bf19b11d4819f49614824ffd84d09c0a17e7d17309d12919790410aa2995699f6a86dbe3242b5acc23af45691080d6b1ae810fb3e3057087f0970092ce00be9562ff4053b6262ce0caa93e13723d2e3a5ba075d45f0d61b54b61"],
+        },
+        {
+            .label = @"8-2",
+            .keys = keys[8],
+            .message = [NSData dataWithHexString:@"4eb68dcd93ca9b19df111bd43608f557026fe4aa1d5cfac227a3eb5ab9548c18a06dded23f81825986b2fcd71109ecef7eff88873f075c2aa0c469f69c92bc"],
+            .seed = [NSData dataWithHexString:@"a3717da143b4dcffbc742665a8fa950585548343"],
+            .encryptedMessage = [NSData dataWithHexString:@"2ecf15c97c5a15b1476ae986b371b57a24284f4a162a8d0c8182e7905e792256f1812ba5f83f1f7a130e42dcc02232844edc14a31a68ee97ae564a383a3411656424c5f62ddb646093c367be1fcda426cf00a06d8acb7e57776fbbd855ac3df506fc16b1d7c3f2110f3d8068e91e186363831c8409680d8da9ecd8cf1fa20ee39d"],
+        },
+        {
+            .label = @"8-3",
+            .keys = keys[8],
+            .message = [NSData dataWithHexString:@"8604ac56328c1ab5ad917861"],
+            .seed = [NSData dataWithHexString:@"ee06209073cca026bb264e5185bf8c68b7739f86"],
+            .encryptedMessage = [NSData dataWithHexString:@"4bc89130a5b2dabb7c2fcf90eb5d0eaf9e681b7146a38f3173a3d9cfec52ea9e0a41932e648a9d69344c50da763f51a03c95762131e8052254dcd2248cba40fd31667786ce05a2b7b531ac9dac9ed584a59b677c1a8aed8c5d15d68c05569e2be780bf7db638fd2bfd2a85ab276860f3777338fca989ffd743d13ee08e0ca9893f"],
+        },
+        {
+            .label = @"8-4",
+            .keys = keys[8],
+            .message = [NSData dataWithHexString:@"fdda5fbf6ec361a9d9a4ac68af216a0686f438b1e0e5c36b955f74e107f39c0dddcc"],
+            .seed = [NSData dataWithHexString:@"990ad573dc48a973235b6d82543618f2e955105d"],
+            .encryptedMessage = [NSData dataWithHexString:@"2e456847d8fc36ff0147d6993594b9397227d577752c79d0f904fcb039d4d812fea605a7b574dd82ca786f93752348438ee9f5b5454985d5f0e1699e3e7ad175a32e15f03deb042ab9fe1dd9db1bb86f8c089ccb45e7ef0c5ee7ca9b7290ca6b15bed47039788a8a93ff83e0e8d6244c71006362deef69b6f416fb3c684383fbd0"],
+        },
+        {
+            .label = @"8-5",
+            .keys = keys[8],
+            .message = [NSData dataWithHexString:@"4a5f4914bee25de3c69341de07"],
+            .seed = [NSData dataWithHexString:@"ecc63b28f0756f22f52ac8e6ec1251a6ec304718"],
+            .encryptedMessage = [NSData dataWithHexString:@"1fb9356fd5c4b1796db2ebf7d0d393cc810adf6145defc2fce714f79d93800d5e2ac211ea8bbecca4b654b94c3b18b30dd576ce34dc95436ef57a09415645923359a5d7b4171ef22c24670f1b229d3603e91f76671b7df97e7317c97734476d5f3d17d21cf82b5ba9f83df2e588d36984fd1b584468bd23b2e875f32f68953f7b2"],
+        },
+        {
+            .label = @"8-6",
+            .keys = keys[8],
+            .message = [NSData dataWithHexString:@"8e07d66f7b880a72563abcd3f35092bc33409fb7f88f2472be"],
+            .seed = [NSData dataWithHexString:@"3925c71b362d40a0a6de42145579ba1e7dd459fc"],
+            .encryptedMessage = [NSData dataWithHexString:@"3afd9c6600147b21798d818c655a0f4c9212db26d0b0dfdc2a7594ccb3d22f5bf1d7c3e112cd73fc7d509c7a8bafdd3c274d1399009f9609ec4be6477e453f075aa33db382870c1c3409aef392d7386ae3a696b99a94b4da0589447e955d16c98b17602a59bd736279fcd8fb280c4462d590bfa9bf13fed570eafde97330a2c210"],
+        },
+        {
+            .label = @"9-1",
+            .keys = keys[9],
+            .message = [NSData dataWithHexString:@"f735fd55ba92592c3b52b8f9c4f69aaa1cbef8fe88add095595412467f9cf4ec0b896c59eda16210e7549c8abb10cdbc21a12ec9b6b5b8fd2f10399eb6"],
+            .seed = [NSData dataWithHexString:@"8ec965f134a3ec9931e92a1ca0dc8169d5ea705c"],
+            .encryptedMessage = [NSData dataWithHexString:@"267bcd118acab1fc8ba81c85d73003cb8610fa55c1d97da8d48a7c7f06896a4db751aa284255b9d36ad65f37653d829f1b37f97b8001942545b2fc2c55a7376ca7a1be4b1760c8e05a33e5aa2526b8d98e317088e7834c755b2a59b12631a182c05d5d43ab1779264f8456f515ce57dfdf512d5493dab7b7338dc4b7d78db9c091ac3baf537a69fc7f549d979f0eff9a94fda4169bd4d1d19a69c99e33c3b55490d501b39b1edae118ff6793a153261584d3a5f39f6e682e3d17c8cd1261fa72"],
+        },
+        {
+            .label = @"9-2",
+            .keys = keys[9],
+            .message = [NSData dataWithHexString:@"81b906605015a63aabe42ddf11e1978912f5404c7474b26dce3ed482bf961ecc818bf420c54659"],
+            .seed = [NSData dataWithHexString:@"ecb1b8b25fa50cdab08e56042867f4af5826d16c"],
+            .encryptedMessage = [NSData dataWithHexString:@"93ac9f0671ec29acbb444effc1a5741351d60fdb0e393fbf754acf0de49761a14841df7772e9bc82773966a1584c4d72baea00118f83f35cca6e537cbd4d811f5583b29783d8a6d94cd31be70d6f526c10ff09c6fa7ce069795a3fcd0511fd5fcb564bcc80ea9c78f38b80012539d8a4ddf6fe81e9cddb7f50dbbbbcc7e5d86097ccf4ec49189fb8bf318be6d5a0715d516b49af191258cd32dc833ce6eb4673c03a19bbace88cc54895f636cc0c1ec89096d11ce235a265ca1764232a689ae8"],
+        },
+        {
+            .label = @"9-3",
+            .keys = keys[9],
+            .message = [NSData dataWithHexString:@"fd326429df9b890e09b54b18b8f34f1e24"],
+            .seed = [NSData dataWithHexString:@"e89bb032c6ce622cbdb53bc9466014ea77f777c0"],
+            .encryptedMessage = [NSData dataWithHexString:@"81ebdd95054b0c822ef9ad7693f5a87adfb4b4c4ce70df2df84ed49c04da58ba5fc20a19e1a6e8b7a3900b22796dc4e869ee6b42792d15a8eceb56c09c69914e813cea8f6931e4b8ed6f421af298d595c97f4789c7caa612c7ef360984c21b93edc5401068b5af4c78a8771b984d53b8ea8adf2f6a7d4a0ba76c75e1dd9f658f20ded4a46071d46d7791b56803d8fea7f0b0f8e41ae3f09383a6f9585fe7753eaaffd2bf94563108beecc207bbb535f5fcc705f0dde9f708c62f49a9c90371d3"],
+        },
+        {
+            .label = @"9-4",
+            .keys = keys[9],
+            .message = [NSData dataWithHexString:@"f1459b5f0c92f01a0f723a2e5662484d8f8c0a20fc29dad6acd43bb5f3effdf4e1b63e07fdfe6628d0d74ca19bf2d69e4a0abf86d293925a796772f8088e"],
+            .seed = [NSData dataWithHexString:@"606f3b99c0b9ccd771eaa29ea0e4c884f3189ccc"],
+            .encryptedMessage = [NSData dataWithHexString:@"bcc35f94cde66cb1136625d625b94432a35b22f3d2fa11a613ff0fca5bd57f87b902ccdc1cd0aebcb0715ee869d1d1fe395f6793003f5eca465059c88660d446ff5f0818552022557e38c08a67ead991262254f10682975ec56397768537f4977af6d5f6aaceb7fb25dec5937230231fd8978af49119a29f29e424ab8272b47562792d5c94f774b8829d0b0d9f1a8c9eddf37574d5fa248eefa9c5271fc5ec2579c81bdd61b410fa61fe36e424221c113addb275664c801d34ca8c6351e4a858"],
+        },
+        {
+            .label = @"9-5",
+            .keys = keys[9],
+            .message = [NSData dataWithHexString:@"53e6e8c729d6f9c319dd317e74b0db8e4ccca25f3c8305746e137ac63a63ef3739e7b595abb96e8d55e54f7bd41ab433378ffb911d"],
+            .seed = [NSData dataWithHexString:@"fcbc421402e9ecabc6082afa40ba5f26522c840e"],
+            .encryptedMessage = [NSData dataWithHexString:@"232afbc927fa08c2f6a27b87d4a5cb09c07dc26fae73d73a90558839f4fd66d281b87ec734bce237ba166698ed829106a7de6942cd6cdce78fed8d2e4d81428e66490d036264cef92af941d3e35055fe3981e14d29cbb9a4f67473063baec79a1179f5a17c9c1832f2838fd7d5e59bb9659d56dce8a019edef1bb3accc697cc6cc7a778f60a064c7f6f5d529c6210262e003de583e81e3167b89971fb8c0e15d44fffef89b53d8d64dd797d159b56d2b08ea5307ea12c241bd58d4ee278a1f2e"],
+        },
+        {
+            .label = @"9-6",
+            .keys = keys[9],
+            .message = [NSData dataWithHexString:@"b6b28ea2198d0c1008bc64"],
+            .seed = [NSData dataWithHexString:@"23aade0e1e08bb9b9a78d2302a52f9c21b2e1ba2"],
+            .encryptedMessage = [NSData dataWithHexString:@"438cc7dc08a68da249e42505f8573ba60e2c2773d5b290f4cf9dff718e842081c383e67024a0f29594ea987b9d25e4b738f285970d195abb3a8c8054e3d79d6b9c9a8327ba596f1259e27126674766907d8d582ff3a8476154929adb1e6d1235b2ccb4ec8f663ba9cc670a92bebd853c8dbf69c6436d016f61add836e94732450434207f9fd4c43dec2a12a958efa01efe2669899b5e604c255c55fb7166de5589e369597bb09168c06dd5db177e06a1740eb2d5c82faeca6d92fcee9931ba9f"],
+        },
+        {
+            .label = @"10-1",
+            .keys = keys[10],
+            .message = [NSData dataWithHexString:@"8bba6bf82a6c0f86d5f1756e97956870b08953b06b4eb205bc1694ee"],
+            .seed = [NSData dataWithHexString:@"47e1ab7119fee56c95ee5eaad86f40d0aa63bd33"],
+            .encryptedMessage = [NSData dataWithHexString:@"53ea5dc08cd260fb3b858567287fa91552c30b2febfba213f0ae87702d068d19bab07fe574523dfb42139d68c3c5afeee0bfe4cb7969cbf382b804d6e61396144e2d0e60741f8993c3014b58b9b1957a8babcd23af854f4c356fb1662aa72bfcc7e586559dc4280d160c126785a723ebeebeff71f11594440aaef87d10793a8774a239d4a04c87fe1467b9daf85208ec6c7255794a96cc29142f9a8bd418e3c1fd67344b0cd0829df3b2bec60253196293c6b34d3f75d32f213dd45c6273d505adf4cced1057cb758fc26aeefa441255ed4e64c199ee075e7f16646182fdb464739b68ab5daff0e63e9552016824f054bf4d3c8c90a97bb6b6553284eb429fcc"],
+        },
+        {
+            .label = @"10-2",
+            .keys = keys[10],
+            .message = [NSData dataWithHexString:@"e6ad181f053b58a904f2457510373e57"],
+            .seed = [NSData dataWithHexString:@"6d17f5b4c1ffac351d195bf7b09d09f09a4079cf"],
+            .encryptedMessage = [NSData dataWithHexString:@"a2b1a430a9d657e2fa1c2bb5ed43ffb25c05a308fe9093c01031795f5874400110828ae58fb9b581ce9dddd3e549ae04a0985459bde6c626594e7b05dc4278b2a1465c1368408823c85e96dc66c3a30983c639664fc4569a37fe21e5a195b5776eed2df8d8d361af686e750229bbd663f161868a50615e0c337bec0ca35fec0bb19c36eb2e0bbcc0582fa1d93aacdb061063f59f2ce1ee43605e5d89eca183d2acdfe9f81011022ad3b43a3dd417dac94b4e11ea81b192966e966b182082e71964607b4f8002f36299844a11f2ae0faeac2eae70f8f4f98088acdcd0ac556e9fccc511521908fad26f04c64201450305778758b0538bf8b5bb144a828e629795"],
+        },
+        {
+            .label = @"10-3",
+            .keys = keys[10],
+            .message = [NSData dataWithHexString:@"510a2cf60e866fa2340553c94ea39fbc256311e83e94454b4124"],
+            .seed = [NSData dataWithHexString:@"385387514deccc7c740dd8cdf9daee49a1cbfd54"],
+            .encryptedMessage = [NSData dataWithHexString:@"9886c3e6764a8b9a84e84148ebd8c3b1aa8050381a78f668714c16d9cfd2a6edc56979c535d9dee3b44b85c18be8928992371711472216d95dda98d2ee8347c9b14dffdff84aa48d25ac06f7d7e65398ac967b1ce90925f67dce049b7f812db0742997a74d44fe81dbe0e7a3feaf2e5c40af888d550ddbbe3bc20657a29543f8fc2913b9bd1a61b2ab2256ec409bbd7dc0d17717ea25c43f42ed27df8738bf4afc6766ff7aff0859555ee283920f4c8a63c4a7340cbafddc339ecdb4b0515002f96c932b5b79167af699c0ad3fccfdf0f44e85a70262bf2e18fe34b850589975e867ff969d48eabf212271546cdc05a69ecb526e52870c836f307bd798780ede"],
+        },
+        {
+            .label = @"10-4",
+            .keys = keys[10],
+            .message = [NSData dataWithHexString:@"bcdd190da3b7d300df9a06e22caae2a75f10c91ff667b7c16bde8b53064a2649a94045c9"],
+            .seed = [NSData dataWithHexString:@"5caca6a0f764161a9684f85d92b6e0ef37ca8b65"],
+            .encryptedMessage = [NSData dataWithHexString:@"6318e9fb5c0d05e5307e1683436e903293ac4642358aaa223d7163013aba87e2dfda8e60c6860e29a1e92686163ea0b9175f329ca3b131a1edd3a77759a8b97bad6a4f8f4396f28cf6f39ca58112e48160d6e203daa5856f3aca5ffed577af499408e3dfd233e3e604dbe34a9c4c9082de65527cac6331d29dc80e0508a0fa7122e7f329f6cca5cfa34d4d1da417805457e008bec549e478ff9e12a763c477d15bbb78f5b69bd57830fc2c4ed686d79bc72a95d85f88134c6b0afe56a8ccfbc855828bb339bd17909cf1d70de3335ae07039093e606d655365de6550b872cd6de1d440ee031b61945f629ad8a353b0d40939e96a3c450d2a8d5eee9f678093c8"],
+        },
+        {
+            .label = @"10-5",
+            .keys = keys[10],
+            .message = [NSData dataWithHexString:@"a7dd6c7dc24b46f9dd5f1e91ada4c3b3df947e877232a9"],
+            .seed = [NSData dataWithHexString:@"95bca9e3859894b3dd869fa7ecd5bbc6401bf3e4"],
+            .encryptedMessage = [NSData dataWithHexString:@"75290872ccfd4a4505660d651f56da6daa09ca1301d890632f6a992f3d565cee464afded40ed3b5be9356714ea5aa7655f4a1366c2f17c728f6f2c5a5d1f8e28429bc4e6f8f2cff8da8dc0e0a9808e45fd09ea2fa40cb2b6ce6ffff5c0e159d11b68d90a85f7b84e103b09e682666480c657505c0929259468a314786d74eab131573cf234bf57db7d9e66cc6748192e002dc0deea930585f0831fdcd9bc33d51f79ed2ffc16bcf4d59812fcebcaa3f9069b0e445686d644c25ccf63b456ee5fa6ffe96f19cdf751fed9eaf35957754dbf4bfea5216aa1844dc507cb2d080e722eba150308c2b5ff1193620f1766ecf4481bafb943bd292877f2136ca494aba0"],
+        },
+        {
+            .label = @"10-6",
+            .keys = keys[10],
+            .message = [NSData dataWithHexString:@"eaf1a73a1b0c4609537de69cd9228bbcfb9a8ca8c6c3efaf056fe4a7f4634ed00b7c39ec6922d7b8ea2c04ebac"],
+            .seed = [NSData dataWithHexString:@"9f47ddf42e97eea856a9bdbc714eb3ac22f6eb32"],
+            .encryptedMessage = [NSData dataWithHexString:@"2d207a73432a8fb4c03051b3f73b28a61764098dfa34c47a20995f8115aa6816679b557e82dbee584908c6e69782d7deb34dbd65af063d57fca76a5fd069492fd6068d9984d209350565a62e5c77f23038c12cb10c6634709b547c46f6b4a709bd85ca122d74465ef97762c29763e06dbc7a9e738c78bfca0102dc5e79d65b973f28240caab2e161a78b57d262457ed8195d53e3c7ae9da021883c6db7c24afdd2322eac972ad3c354c5fcef1e146c3a0290fb67adf007066e00428d2cec18ce58f9328698defef4b2eb5ec76918fde1c198cbb38b7afc67626a9aefec4322bfd90d2563481c9a221f78c8272c82d1b62ab914e1c69f6af6ef30ca5260db4a46"],
+        },
+        
+    };
+    
+       int max_cycles = 100;
+       if (!getenv("SUBMISSION_TEST")) {
+               max_cycles = 10;
+               CFfprintf(stderr, "Running the far faster but far less reliable fast test.\nSet the SUBMISSION_TEST environment variable for full testing\n");
+       }
+       
+    for (int j = 0; j < max_cycles; j++) {
+        NSLog(@"Cycle %d", j);
+        for (i = 0; i < sizeof(tests)/sizeof(KAT); i++) {
+            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]);
+            CFErrorRef err = NULL;
+            
+            SecTransformRef encryptor = SecEncryptTransformCreate(tests[i].keys.pubKey, &err);
+            
+            SecTransformSetAttribute(encryptor, kSecTransformInputAttributeName, tests[i].message, &err);
+            SecTransformSetAttribute(encryptor, kSecPaddingKey, kSecPaddingOAEPKey, &err);
+            SecTransformSetAttribute(encryptor, kSecOAEPEncodingParametersAttributeName, encoding_parameters, &err);
+            SecTransformSetAttribute(encryptor, CFSTR("FixedSeedForOAEPTesting"), tests[i].seed, &err);
+            
+            CFTypeRef encryptedData = SecTransformExecute(encryptor, &err);
+            STAssertNotNil((id)encryptedData, @"Expected to get encrypted data");
+            STAssertNil((NSError*)err, @"Expected no error, got err=%@", err);
+                       // Can't support "seed" with commoncrypto, just check round trip.
+            //STAssertEqualObjects((id)encryptedData, (id)tests[i].encryptedMessage, @"encrypted data should have matched test vector (%@) data", tests[i].label);
+            CFRelease(encryptor);
+            
+            SecTransformRef decryptor = SecDecryptTransformCreate(tests[i].keys.privKey, NULL);
+            SecTransformSetAttribute(decryptor, kSecTransformInputAttributeName, tests[i].encryptedMessage, NULL);
+                       // XXX: totally round trip, not even partial KAT (KAT can't really be done on OAEP
+                       // without supporitng settign the seed externally)
+            SecTransformSetAttribute(decryptor, kSecTransformInputAttributeName, encryptedData, NULL);
+            SecTransformSetAttribute(decryptor, kSecPaddingKey, kSecPaddingOAEPKey, NULL);
+            SecTransformSetAttribute(decryptor, kSecOAEPEncodingParametersAttributeName, encoding_parameters, NULL);
+            CFTypeRef decryptedData = SecTransformExecute(decryptor, &err);
+            STAssertNil((id)err, @"Expected no error, got: %@", err);
+            STAssertNotNil((id)decryptedData, @"Expected to get decrypted data");
+            STAssertEqualObjects((id)decryptedData, tests[i].message, @"Expected decrypted data to match original message (%@)", tests[i].label);
+            CFRelease(decryptor);
+        }
+    }
+    
+    return;
+}
+
+-(void)testNoSignKeyMakesError
+{
+    NSData *data = [NSData dataWithBytes:"" length:1];
+    
+    struct test_case {
+        NSString *name;
+        CFErrorRef createError;
+        SecTransformRef transform;
+    } test_cases[] = {
+        {
+            .name = @"Sign",
+            .createError = NULL,
+            .transform = SecSignTransformCreate(NULL, &(test_cases[0].createError))
+        },
+        {
+            .name = @"Verify",
+            .createError = NULL,
+            .transform = SecVerifyTransformCreate(NULL, (CFDataRef)data, &(test_cases[1].createError))
+        }
+    };
+    
+    for(int i = 0; i < sizeof(test_cases) / sizeof(test_case); i++) {
+        struct test_case *test = test_cases + i;
+        STAssertNil((id)test->createError, @"Testing %@, unexpected error: %@", test->name, test->createError);
+        STAssertNotNil((id)test->transform, @"Didn't manage to create transform for %@", test->name);
+        if (!test->transform) {
+            continue;
+        }
+        
+        __block CFErrorRef err = NULL;
+        SecTransformSetAttribute(test->transform, kSecTransformInputAttributeName, data, &err);
+        STAssertNil((id)err, @"Error setting input for %@: %@", test->name, err);
+        
+        dispatch_group_t execute_done = dispatch_group_create();
+        dispatch_group_enter(execute_done);
+        
+        SecTransformExecuteAsync(test->transform, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
+            if (error) {
+                err = error;
+            }
+            if (isFinal) {
+                dispatch_group_leave(execute_done);
+            }
+        });
+        
+        STAssertFalse(dispatch_group_wait(execute_done, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC * 5)), @"Timeout waiting for %@ transform", test->name);
+        STAssertErrorHas((id)err, @"missing required attributes?:.*KEY", @"Unexpected error during %@ test, expected one about missing keys: %@", test->name, err);
+        dispatch_group_notify(execute_done, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, NULL), ^(void) {
+            dispatch_release(execute_done);
+        });
+    }
+}
+
+-(void)testHMAC
+{
+       // make the data for the key and the data to be HMAC'd
+       CFDataRef hmacData = CFDataCreate(NULL, (u_int8_t*) gHMACText, strlen(gHMACText));
+       CFDataRef hmacKey = CFDataCreate(NULL, (u_int8_t*) gHMACKey, strlen(gHMACKey));
+       SecTransformRef hmacRef;
+       CFErrorRef error = NULL;
+       CFDataRef result;
+       CFDataRef rightAnswer;
+       CFComparisonResult ok;
+
+       // create the object
+       hmacRef = SecDigestTransformCreate(kSecDigestHMACSHA1, 20, &error);
+       STAssertNil((id) error, @"Unexpected error returned.");
+       
+       // set the key
+       SecTransformSetAttribute(hmacRef, kSecDigestHMACKeyAttribute, hmacKey, &error);
+       STAssertNil((id) error, @"Unexpected error returned.");
+       
+       // digest the data
+       SecTransformSetAttribute(hmacRef, kSecTransformInputAttributeName, hmacData, &error);
+       STAssertNil((id) error, @"Unexpected error returned.");
+       
+       result = (CFDataRef) SecTransformExecute(hmacRef, &error);
+       if (error)
+       {
+               CFShow(error);
+               STAssertNil((id) error, @"Unexpected error returned.");
+               CFRelease(error);
+       }
+       
+       STAssertNotNil((id) result, @"No data returned for SHA1");
+       
+       // check to make sure we got the right answer
+       rightAnswer = CFDataCreate(NULL, gSHA1HMAC, sizeof(gSHA1HMAC));
+       ok = CFEqual(rightAnswer, result);
+       CFRelease(rightAnswer);
+       CFRelease(hmacRef);
+       CFRelease(result);
+       
+       if (error)
+       {
+               CFRelease(error);
+       }
+       
+       STAssertTrue(ok, @"Digest returned incorrect HMACSHA1 result.");
+
+       //+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+*+
+
+       // create the object
+       hmacRef = SecDigestTransformCreate(kSecDigestHMACSHA2, 256, &error);
+       STAssertNil((id) error, @"Unexpected error returned.");
+       
+       // set the key
+       SecTransformSetAttribute(hmacRef, kSecDigestHMACKeyAttribute, hmacKey, &error);
+       STAssertNil((id) error, @"Unexpected error returned.");
+       
+       // digest the data
+       SecTransformSetAttribute(hmacRef, kSecTransformInputAttributeName, hmacData, &error);
+       STAssertNil((id) error, @"Unexpected error returned.");
+       
+       result = (CFDataRef) SecTransformExecute(hmacRef, &error);
+       if (error != nil)
+       {
+               CFShow(error);
+               STAssertNil((id) error, @"Unexpected error returned.");
+               CFRelease(error);
+       }
+       
+       STAssertNotNil((id) result, @"No data returned for SHA256");
+       
+       rightAnswer = CFDataCreate(NULL, gSHA256HMAC, sizeof(gSHA256HMAC));
+       ok = CFEqual(result, rightAnswer);
+
+       CFRelease(rightAnswer);
+       CFRelease(hmacRef);
+
+       CFRelease(hmacData);
+       CFRelease(hmacKey);
+       CFRelease(result);
+       
+       STAssertTrue(ok, @"Digest returned incorrect HMACSHA256 result.");
+}
+
+
+       
+-(void)echoParams:(NSString*)p1 p2:(NSString*)p2
+{
+}
+
+-(void)testReadStreamTransform
+{
+       // point to our test data
+       CFURLRef url = CFURLCreateWithFileSystemPath(NULL, CFSTR("/usr/share/dict/words"), kCFURLPOSIXPathStyle, false);
+    FSRef force_resolve;
+    STAssertTrue(CFURLGetFSRef(url, &force_resolve), @"Expected to create FSRef from %@", url);
+    CFURLRef resolved_url = CFURLCreateFromFSRef(NULL, &force_resolve);
+       CFNumberRef size_on_disk = NULL;
+    CFURLCopyResourcePropertyForKey(resolved_url, kCFURLFileSizeKey, &size_on_disk, NULL);
+    STAssertNotNil((id)size_on_disk, @"Expected to fetch size");
+    
+       CFReadStreamRef readStreamRef = CFReadStreamCreateWithFile(NULL, url);
+       SecTransformRef transform = SecTransformCreateReadTransformWithReadStream(readStreamRef);
+       STAssertNotNil((id) transform, @"Returned transform should not be nil.");
+       
+       dispatch_semaphore_t waitSemaphore = dispatch_semaphore_create(0);
+       dispatch_queue_t queue = dispatch_queue_create("ReadStream queue", NULL);
+    __block ssize_t bytes_presented = 0;
+
+       SecTransformExecuteAsync(transform, queue,
+       ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
+       {
+               if (message)
+        {
+            bytes_presented += CFDataGetLength((CFDataRef)message);
+        }
+        if (isFinal)
+               {
+                       STAssertNil((id)error, @"Unexpected error!");
+                       dispatch_semaphore_signal(waitSemaphore);
+               }
+       });
+       
+       dispatch_semaphore_wait(waitSemaphore, DISPATCH_TIME_FOREVER);
+       NSNumber *size_via_stream = [NSNumber numberWithLongLong:bytes_presented];
+    STAssertEqualObjects(size_via_stream, (NSNumber*)size_on_disk, @"Expected sizes to match");
+    CFRelease(size_on_disk);
+    
+       dispatch_release(queue);
+       dispatch_release(waitSemaphore);
+       CFRelease(transform);
+       CFRelease(readStreamRef);
+       CFRelease(url);
+    CFRelease(resolved_url);
+}
+
+-(void)testMGF
+{
+    UInt8 raw_seed[] = {0xaa, 0xfd, 0x12, 0xf6, 0x59, 0xca, 0xe6, 0x34, 0x89, 0xb4, 0x79, 0xe5, 0x07, 0x6d, 0xde, 0xc2, 0xf0, 0x6c, 0xb5, 0x8f};
+    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};
+    CFDataRef seed = CFDataCreate(NULL, raw_seed, sizeof(raw_seed));
+    CFDataRef mgf107 = CFDataCreate(NULL, raw_mgf107, sizeof(raw_mgf107));
+    CFErrorRef err = NULL;
+    
+    SecTransformRef mgfTransform = SecCreateMaskGenerationFunctionTransform(NULL, 107, &err);
+    STAssertNotNil((id)mgfTransform, @"Expected to create a MGF transform e=%@", err);
+    err = NULL;
+    SecTransformSetAttribute(mgfTransform, kSecTransformInputAttributeName, seed, &err);
+    STAssertNil((id)err, @"Expected no error setting MGF's input, got %@", err);
+    err = NULL;
+    CFDataRef mgfOutput = (CFDataRef)SecTransformExecute(mgfTransform, &err);
+    STAssertNotNil((id)mgfOutput, @"Expected output from mgf, got error %@", err);
+    STAssertEqualObjects((id)mgfOutput, (id)mgf107, @"Expected matching output");
+    
+    CFRelease(mgfTransform);
+    // XXX: leak test??
+    
+    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};
+    
+    UInt8 raw_mgf20[] = {0x41, 0x87, 0x0b, 0x5a, 0xb0, 0x29, 0xe6, 0x57, 0xd9, 0x57, 0x50, 0xb5, 0x4c, 0x28, 0x3c, 0x08, 0x72, 0x5d, 0xbe, 0xa9};
+    CFDataRef maskedDB = CFDataCreate(NULL, raw_maskedDB, sizeof(raw_maskedDB));
+    CFDataRef mgf20 = CFDataCreate(NULL, raw_mgf20, sizeof(raw_mgf20));
+    err = NULL;
+    
+    mgfTransform = SecCreateMaskGenerationFunctionTransform(kSecDigestSHA1, 20, &err);
+    STAssertNotNil((id)mgfTransform, @"Expected to create a MGF transform e=%@", err);
+    err = NULL;
+    SecTransformSetAttribute(mgfTransform, kSecTransformInputAttributeName, maskedDB, &err);
+    STAssertNil((id)err, @"Expected no error setting MGF's input, got %@", err);
+    err = NULL;
+    mgfOutput = (CFDataRef)SecTransformExecute(mgfTransform, &err);
+    STAssertNotNil((id)mgfOutput, @"Expected output from mgf, got error %@", err);
+    STAssertEqualObjects((id)mgfOutput, (id)mgf20, @"Expected matching output");
+}
+
+-(void)testAbortParams
+{
+       // make a simple transform
+       SecTransformRef a = SecNullTransformCreate();
+       
+       // try to abort the transform
+       CFErrorRef errorRef = NULL;
+       STAssertFalse(SecTransformSetAttribute(a, CFSTR("ABORT"), NULL, &errorRef), @"SecTransformSetAttribute should have returned FALSE");
+       STAssertNotNil((id) errorRef, @"SecTransformSetAttribute should have had an error.");
+       if (errorRef != NULL)
+       {
+               CFRelease(errorRef);
+       }
+       
+       CFRelease(a);
+       
+       // 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
+       // 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
+       // triggered off of a NULL value.
+       SecGroupTransformRef test_null_abort_via_connection = SecTransformCreateGroupTransform();
+       SecTransformRef pass_through = SecNullTransformCreate();
+       SecTransformRef null_abort = SecNullTransformCreate();
+       
+       CFURLRef dev_null_url = CFURLCreateWithFileSystemPath(NULL, CFSTR("/dev/null"), kCFURLPOSIXPathStyle, NO);
+       CFReadStreamRef dev_null_stream = CFReadStreamCreateWithFile(NULL, dev_null_url);
+       CFReadStreamOpen(dev_null_stream);
+       CFRelease(dev_null_url);
+       
+       SecTransformSetAttribute(pass_through, kSecTransformInputAttributeName, dev_null_stream, NULL);
+       SecTransformConnectTransforms(pass_through, kSecTransformOutputAttributeName, null_abort, kSecTransformAbortAttributeName, test_null_abort_via_connection, NULL);
+       
+       SecTransformRef delay_null = delay_transform(NSEC_PER_SEC / 10);
+       SecTransformConnectTransforms(pass_through, kSecTransformOutputAttributeName, delay_null, kSecTransformInputAttributeName, test_null_abort_via_connection, NULL);
+       SecTransformConnectTransforms(delay_null, kSecTransformOutputAttributeName, null_abort, kSecTransformInputAttributeName, test_null_abort_via_connection, NULL);
+       
+       
+       CFErrorRef err = NULL;
+       CFTypeRef not_null = SecTransformExecute(test_null_abort_via_connection, &err);
+       
+       STAssertNotNil((id)not_null, @"aborted via a NULL from a connection?  err=%@", err);
+       
+       if (err)
+       {
+               CFRelease(err);
+       }
+       
+       CFRelease(test_null_abort_via_connection);
+       CFRelease(pass_through);
+       CFRelease(null_abort);
+       CFRelease(delay_null);
+       
+       CFReadStreamClose(dev_null_stream);
+       CFRelease(dev_null_stream);
+}
+
+
+-(void)testDisconnect
+{
+       SecTransformRef a = SecNullTransformCreate();
+       SecTransformRef b = SecNullTransformCreate();
+       SecTransformRef c = SecNullTransformCreate();
+       SecGroupTransformRef g = SecTransformCreateGroupTransform();
+       
+       SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName, g, NULL);
+       SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, c, kSecTransformInputAttributeName, g, NULL);
+       
+       SecTransformDisconnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName);
+       STAssertTrue(SecGroupTransformHasMember(g, a), @"A should still be in the group, but isn't");
+
+       SecTransformDisconnectTransforms(a, kSecTransformOutputAttributeName, c, kSecTransformInputAttributeName);
+       STAssertFalse(SecGroupTransformHasMember(g, a), @"A should no longer be in the group, but is");
+       
+       CFRelease(g);
+       CFRelease(c);
+       CFRelease(b);
+       CFRelease(a);
+}
+
+
+-(void)testAbort
+{
+       CFStringRef abort_test_name = CFSTR("com.apple.security.unit-test.abortTest");
+       
+       SecTransformCreateBlock setupBlock =
+       ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params)
+       {
+               params->send(kSecTransformInputAttributeName, kSecTransformMetaAttributeDeferred, kCFBooleanTrue);
+               
+               params->overrideAttribute(kSecTransformActionAttributeNotification, CFSTR("PB"), ^(SecTransformAttributeRef ah, CFTypeRef value) {
+                       // Makes sure we can shut down (via ABORT) a transform that has a pending pushback
+                       params->pushback(ah, value);
+                       return (CFTypeRef)NULL;
+               });
+       };
+       
+               
+       SecTransformRef a;
+       SecTransformRef dt; 
+       SecGroupTransformRef group = SecTransformCreateGroupTransform();
+
+       // make two of these transforms and link them together
+       a = custom_transform(abort_test_name, setupBlock);
+       STAssertNotNil((id) a, @"SecCustomTransformCreate failed");
+       
+       dt = delay_transform(NSEC_PER_SEC / 10);
+       STAssertNotNil((id) dt, @"SecCustomTransformCreate failed");
+
+       // connect the two transforms
+       CFErrorRef error;
+       
+       // hook the output up so that the abort automatically fires.
+       SecTransformConnectTransforms(dt, kSecTransformOutputAttributeName, a, CFSTR("ABORT"), group, &error);
+       STAssertNil((id) error, @"SecTransformConnectTransforms failed.");
+       
+       // also hook it up to the input because the input attribute is required on a null transform
+       SecTransformConnectTransforms(dt, CFSTR("NOVALUES"), a, kSecTransformInputAttributeName, group, &error);
+       STAssertNil((id) error, @"SecTransformConnectTransforms failed.");
+       
+       // pass a plain piece of data down the transform just for fun...
+       const u_int8_t data[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
+       
+       CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL, data, sizeof(data), kCFAllocatorNull);
+       SecTransformSetAttribute(dt, kSecTransformInputAttributeName, dataRef, NULL);
+       
+       CFStringRef str = CFStringCreateMutable(NULL, 0);
+       SecTransformSetAttribute(a, CFSTR("PB"), str, NULL);
+
+       CFTypeRef er = SecTransformExecute(a, &error);
+       
+       STAssertNil((id)er, @"Didn't expect an result from aborted transform");
+       STAssertNotNil((id)error, @"Expected error from execute");
+       
+       if (error)
+       {
+               CFShow(error);
+               
+               // while we are at it, make sure that the user dictionary has the originating transform
+               CFDictionaryRef userDictionary = CFErrorCopyUserInfo(error);
+               STAssertNotNil((id) CFDictionaryGetValue(userDictionary, kSecTransformAbortOriginatorKey), @"Originating transform not listed.");
+               CFRelease(error);
+       }
+       
+       CFRelease(a);
+       CFRelease(dt);
+       CFRelease(group);
+       CFRelease(dataRef);
+
+/*
+       // XXX: these should both be 1, not 3 or 4.    WTF?   Is this an abort issue, or a generic leak?
+       // STAssertEquals(rc0, CFGetRetainCount(str), @"The value we sent to PB hasn't been released (value retained by pushback)");
+       // STAssertEquals(rc0, CFGetRetainCount(dataRef), @"The value we sent to INPUT hasn't been released");
+*/
+}
+                                  
+-(void)testPreAbort {
+       CFErrorRef error = NULL;
+       SecTransformRef prebort = SecNullTransformCreate();
+    SecTransformSetAttribute(prebort, kSecTransformInputAttributeName, CFSTR("quux"), NULL);
+       SecTransformSetAttribute(prebort, CFSTR("ABORT"), CFSTR("OOPS"), NULL);
+       CFTypeRef er = SecTransformExecute(prebort, &error);
+       STAssertNil((id)er, @"Didn't expect an result from pre-aborted transform");
+       STAssertNotNil((id)error, @"Expected error from execute of pre-aborted transform");
+       CFRelease(error);
+}
+
+#ifdef DEBUG
+-(void)testFireAndForget
+{
+       bool isGC = false;
+       NSGarbageCollector* gc = [NSGarbageCollector defaultCollector];
+       if (gc)
+       {
+               isGC = [gc isEnabled];
+       }
+
+       CFIndex retCount = 0;
+
+       // make transforms
+       SecNullTransformRef a = SecNullTransformCreate();
+       SecNullTransformRef b = SecNullTransformCreate();
+       SecGroupTransformRef group = SecTransformCreateGroupTransform();
+       SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName, group, NULL);
+
+       if (!isGC)
+       {
+               retCount = CFGetRetainCount(group);
+       }
+       
+       // set up a blob of data to fire
+       const u_int8_t data[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 4};
+       CFDataRef dataRef = CFDataCreateWithBytesNoCopy(NULL, data, sizeof(data), kCFAllocatorNull);
+       SecTransformSetAttribute(a, kSecTransformInputAttributeName, dataRef, NULL);
+       CFRelease(dataRef);
+       
+       // make dispatch related stuff
+       dispatch_queue_t queue = dispatch_queue_create("ffqueue", NULL);
+       // semaphore0's job is to be signaled when we know ExecuteAsync is actually executing (so we don't sample the retain 
+       // count too soone), semaphore signals when we are about done with ExecuteAsync (I'm not sure why we need to know),
+       // and semaphore2 is signaled to let the execute block know we are done sampling retain counts.
+       dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
+       dispatch_semaphore_t semaphore2 = dispatch_semaphore_create(0);
+       
+       // launch the chain
+       SecTransformExecuteAsync(group, queue,
+                                               ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
+                                               {
+                                                       CFfprintf(stderr, "message %p, final %d\n", message, isFinal ? 1 : 0);
+                                                       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()));
+                                                       if (isFinal)
+                                                       {
+                                                               fprintf(stderr, "Final message received.\n");
+                                                               dispatch_semaphore_wait(semaphore2, DISPATCH_TIME_FOREVER); // make sure that the other chain has released its material
+                                                               dispatch_semaphore_signal(semaphore);
+                                                       }
+                                               });
+       CFRelease(a);
+       CFRelease(b);
+       CFRelease(group);
+       dispatch_semaphore_signal(semaphore2);
+       dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
+       
+       // no crash?  Life is good.
+}
+#endif
+
+-(void)testExternalSource
+{
+       CFErrorRef err = NULL;
+       SecTransformRef xs = SecExternalSourceTransformCreate(&err);
+       SecTransformRef tee = SecNullTransformCreate();
+       SecTransformRef group = SecTransformCreateGroupTransform();
+       
+       SecTransformConnectTransforms(xs, kSecTransformOutputAttributeName, tee, kSecTransformInputAttributeName, group, &err);
+       
+       dispatch_queue_t q = dispatch_queue_create("com.apple.security.unit-tests.test-external-source", 0);
+       dispatch_group_t dg = dispatch_group_create();
+       dispatch_group_enter(dg);
+       __block bool got_ping = false;
+       
+       SecTransformExecuteAsync(group, q, ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
+               CFfprintf(stderr, "B: message %@, e %p, f %d\n", message ? message : (CFTypeRef)CFSTR("(NULL)"), error, isFinal);
+               
+               if (message) {
+                       if (CFEqual(message, CFSTR("PING"))) {
+                               got_ping = true;
+                       } else {
+                               STFail(@"expected ping, got: %@", message);
+                       }
+               }
+               
+               if (error) {
+                       STFail(@"unexpected error: %@", error);
+               }
+               
+               if (isFinal) {
+                       if (!got_ping) {
+                               STFail(@"never got ping");
+                       }
+                       dispatch_group_leave(dg);
+               }
+       });
+       
+       SecExternalSourceSetValue(tee, CFSTR("PONG"), &err);
+       STAssertNotNil((id)err, @"Expected error setting tee");
+       STAssertErrorHas((id)err, @"ExternalSource", @"Error should note what should be passed in: %@", err);
+       CFRelease(err);
+       err = NULL;
+       SecExternalSourceSetValue(xs, CFSTR("PING"), &err);
+       STAssertNil((id)err, @"unexpected error setting xs: %@", err);
+       SecExternalSourceSetValue(xs, NULL, &err);
+       STAssertNil((id)err, @"unexpected error setting xs: %@", err);
+       dispatch_group_wait(dg, DISPATCH_TIME_FOREVER);
+       
+       dispatch_release(dg);
+       dispatch_release(q);
+       CFRelease(xs);
+       CFRelease(tee);
+       CFRelease(group);
+}
+
+-(void)testFindLastAndMonitor
+{
+       SecNullTransformRef a = delay_transform(NSEC_PER_SEC / 10);
+       SecNullTransformRef b = SecNullTransformCreate();
+       
+       SecGroupTransformRef groupRef = SecTransformCreateGroupTransform();
+       CFErrorRef error = NULL;
+       SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName, groupRef, &error);
+       STAssertNil((id)error, @"An error was returned when none was expected.");
+       SecTransformSetAttribute(a, kSecTransformInputAttributeName, kCFNull, &error);
+       STAssertNil((id)error, @"An error was returned when none was expected.");
+
+       
+       // get the last transform in the chain (unexecuted).  It had better be b...
+       SecTransformRef tr = SecGroupTransformFindLastTransform(groupRef);
+       STAssertNotNil((id)tr, @"FindLastTransform returned NULL");
+       STAssertTrue(tr == b, @"FindLastTransform returned incorrect result");
+       STAssertFalse(tr == a, @"FindLastTransform returned the head of the chain");
+       
+       // execute the transform. This should attach an output monitor
+       dispatch_queue_t queue = dispatch_queue_create("test delivery queue", NULL);
+       dispatch_semaphore_t last_block_run = dispatch_semaphore_create(0L);
+       dispatch_semaphore_t last_assert_run = dispatch_semaphore_create(0L);
+       SecTransformExecuteAsync(groupRef, queue,
+               ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
+               { 
+                       if (isFinal)
+                       {
+                               dispatch_semaphore_signal(last_block_run);
+                               dispatch_semaphore_wait(last_assert_run, DISPATCH_TIME_FOREVER);
+                               dispatch_release(last_assert_run);
+                       }
+               
+               });
+       
+       dispatch_semaphore_wait(last_block_run, DISPATCH_TIME_FOREVER);
+       
+       // see if the returned transform is the same now
+       tr = SecGroupTransformFindLastTransform(groupRef);
+       STAssertNotNil((id) tr, @"FindLastTransform returned NULL");
+       STAssertTrue(tr == b, @"FindLastTransform returned incorrect result");
+       STAssertFalse(tr == a, @"FindLastTransform returned the head of the chain");
+       
+       // get the monitor, it had better not be a or b
+       tr = SecGroupTransformFindMonitor(groupRef);
+       STAssertNotNil((id) tr, @"FindMonitor returned NULL");
+       STAssertFalse(tr == a, @"FindLastTransform returned the head of the chain");
+       STAssertFalse(tr == b, @"FindLastTransform returned the head of the chain");
+       
+       dispatch_semaphore_signal(last_assert_run);
+       dispatch_release(queue);
+       dispatch_release(last_block_run);
+       CFRelease(a);
+       CFRelease(b);
+       CFRelease(groupRef);
+}
+
+
+-(void)testConnectUnsetAttributes /* <rdar://problem/7769955> Can't connect transform attributes with no setting */
+{
+       SecNullTransformRef a = SecNullTransformCreate();
+       SecNullTransformRef b = SecNullTransformCreate();
+       
+       SecGroupTransformRef group = SecTransformCreateGroupTransform();
+       CFErrorRef error = NULL;
+       SecTransformConnectTransforms(a, CFSTR("RANDOM_NAME"), b, CFSTR("RANDOM_DESTINATION"), group, &error);
+       CFRelease(group);
+       CFRelease(b);
+       CFRelease(a);
+       STAssertNil((id) error, @"An error was returned when none was expected.");
+}
+
+-(void)testNoDataFlowPriorToInit /* <rdar://problem/8163542> Monitor must be attached before the data flow becomes active */
+{
+    CFStringRef name = CFSTR("com.apple.security.unit-test.flow-check");
+       SecTransformCreateBlock cb = ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params) {
+               __block bool inited = false;
+        __block bool saw_x_start = false;
+        __block bool saw_null = false;
+               __block int post_send_left = 8;
+               SecTransformAttributeRef out_ah = params->get(kSecTransformOutputAttributeName, kSecTransformMetaAttributeRef);
+               params->send(out_ah, kSecTransformMetaAttributeValue, CFSTR("create"));
+               
+               params->overrideTransform(kSecTransformActionStartingExecution, ^{
+                       params->send(out_ah, kSecTransformMetaAttributeValue, CFSTR("x-start"));
+                       inited = true;
+                       return (CFTypeRef)NULL;
+               });
+               
+               params->overrideTransform(kSecTransformActionCanExecute, ^{
+                       params->send(out_ah, kSecTransformMetaAttributeValue, CFSTR("can-x"));
+                       return (CFTypeRef)NULL;
+               });
+
+               params->overrideAttribute(kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^(SecTransformAttributeRef ah, CFTypeRef value) {
+                       if (inited) {
+                               if (value) {
+                    if (!saw_x_start) {
+                        saw_x_start = CFStringHasPrefix((CFStringRef)value, CFSTR("x-start"));
+                    }
+                    if (saw_null) {
+                        params->send(kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateGenericErrorRef(name, 88, "saw %@ after NULL", value));
+                    }
+                                       if (post_send_left--) {
+                                               params->send(out_ah, kSecTransformMetaAttributeValue, CFSTR("post-init"));
+                                       }
+                               } else {
+                    saw_null = true;
+                    // The FIRST flow transform should not see x-start (it is the OUTPUT of the flow transform
+                    // before you in the chain), but all the other transforms should see it.
+                    if (params->get(CFSTR("FIRST"), kSecTransformMetaAttributeValue)) {
+                        if (saw_x_start) {
+                            params->send(kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateGenericErrorRef(name, 42, "saw bogus x-start on FIRST flow transform"));
+                        } else {
+                            params->send(out_ah, kSecTransformMetaAttributeValue, value);
+                        }
+                    } else {
+                        if (saw_x_start) {
+                            params->send(out_ah, kSecTransformMetaAttributeValue, value);
+                        } else {
+                            params->send(kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateGenericErrorRef(name, 42, "never saw x-start before EOS"));
+                            return (CFTypeRef)kCFNull;
+                        }
+                    }
+                               }
+                       } else {
+                               // attempting to put the value in the error string sometimes blows up, so I've left it out.
+                               params->send(kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateGenericErrorRef(name, 42, "got: value before init"));
+                return (CFTypeRef)kCFNull;
+                       }
+                       return (CFTypeRef)value;
+               });
+       };
+       
+       // Reliably reproduces with 100000 transforms in our group, but
+       // not at 1000...doesn't even seem to do it at 50000.
+       // Likely a timing issue triggered by heavy swapping.
+       int n_transforms = 100000;
+
+       if (!getenv("SUBMISSION_TEST")) {
+               n_transforms = 10000;
+               CFfprintf(stderr, "Running the far faster but far less reliable fast test.\nSet the SUBMISSION_TEST environment variable for full testing\n");
+       }
+       
+       int i;
+       CFMutableArrayRef ta = CFArrayCreateMutable(NULL, n_transforms, &kCFTypeArrayCallBacks);
+       CFErrorRef err = NULL;
+
+       for(i = 0; i < n_transforms; ++i) {
+               SecTransformRef tr = custom_transform(name, cb);
+               STAssertNil((id)err, @"Failure %@ creating %@ transform", err, name);
+               CFStringRef l = CFStringCreateWithFormat(NULL, NULL, CFSTR("flow-%d"), i);
+               SecTransformSetAttribute(tr, kSecTransformTransformName, l, &err);
+               if (0 == i % 1000) {
+                       CFfprintf(stderr, "Created %@ of %d\n", l, n_transforms);
+               }
+               CFRelease(l);
+               STAssertNil((id)err, @"Can't set name %@", err);
+               CFArrayAppendValue(ta, tr);
+               assert(CFArrayGetCount(ta));
+               assert(CFArrayGetCount(ta) == i+1);
+       }
+
+       SecTransformRef prev_tr = NULL;
+       SecTransformRef group = SecTransformCreateGroupTransform();
+       CFIndex cnt;
+
+       while ((cnt = CFArrayGetCount(ta))) {
+               CFIndex r = arc4random() % cnt;
+               SecTransformRef tr = CFArrayGetValueAtIndex(ta, r);
+               if (prev_tr) {
+                       SecTransformConnectTransforms(tr, kSecTransformOutputAttributeName, prev_tr, kSecTransformInputAttributeName, group, &err);
+                       STAssertNil((id)err, @"Can't connect %@ to %@", tr, prev_tr);
+                       STAssertNotNil((id)group, @"nil group after connect");
+                       CFRelease(prev_tr);
+               }
+               prev_tr = tr;
+               CFArrayRemoveValueAtIndex(ta, r);
+               
+               if (0 == cnt % 1000) {
+                       CFfprintf(stderr, "%d left to hook up\n", cnt);
+               }
+       }
+       
+       CFTypeRef ptl = SecTransformGetAttribute(prev_tr, kSecTransformTransformName);
+       CFfprintf(stderr, "Setting INPUT for %@\n", ptl);
+       SecTransformSetAttribute(prev_tr, kSecTransformInputAttributeName, CFSTR("First!"), &err);
+       STAssertNil((id)err, @"Can't set first's input?  %@", err);
+       SecTransformSetAttribute(prev_tr, CFSTR("FIRST"), kCFBooleanTrue, &err);
+       STAssertNil((id)err, @"Can't set FIRST?  %@", err);
+       CFTypeRef r = SecTransformExecute(group, &err);
+       STAssertNil((id)err, @"execution error: %@", err);
+       STAssertNotNil((id)r, @"result expected from execute");
+       CFRelease(group);
+       CFRelease(prev_tr);
+}
+
+-(void)testNoDataDescription /* <rdar://problem/7791122> CFShow(SecCustomTransformNoData()) crashes */
+{
+       CFStringRef result = CFCopyDescription(SecTransformNoData()); // this is called under the hood in CFShow, and it doesn't dump output
+       STAssertNotNil((id)result, @"SecTransformNoData can be formatted");
+       CFRelease(result);
+}
+
+static SecTransformInstanceBlock KnownProblemPlumbing(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               // At the moment fully disconnecting a transform from a group leaves it in the group, so it can't have any
+               // kSecTransformMetaAttributeRequiresOutboundConnection=TRUE attributes (by default OUTPUT requires an outbound connection)
+               SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
+                       kSecTransformMetaAttributeRequiresOutboundConnection, kCFBooleanFalse);
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               return (CFTypeRef)CFErrorCreate(NULL, kCFErrorDomainPOSIX, EOPNOTSUPP, NULL);
+                       });
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);
+}
+                                                       
+-(void)knownProblemPlumbing // note, this test has been disconnected!
+{
+       SecNullTransformRef a = SecNullTransformCreate();
+       CFStringRef name = CFSTR("com.apple.security.unit-test.error+outputless");
+       CFErrorRef err = NULL;
+       
+       SecTransformRegister(name, &KnownProblemPlumbing, &err); 
+       
+       SecTransformRef b = SecTransformCreate(name, NULL);
+       SecGroupTransformRef group = SecTransformCreateGroupTransform();
+       
+       SecTransformConnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName, group, NULL);
+       SecTransformDisconnectTransforms(a, kSecTransformOutputAttributeName, b, kSecTransformInputAttributeName);
+       
+       
+       CFStringRef data = CFSTR("Test");
+       
+       SecTransformSetAttribute(a, kSecTransformInputAttributeName, data, NULL);
+       CFTypeRef result = SecTransformExecute(a, &err);
+       
+       STAssertEqualObjects((id)data, (id)result, @"Plumbing disconnect failed.");
+       STAssertNil((id)err, @"Unexpected error=%@", err);
+       CFRelease(a);
+       CFRelease(b);
+       CFRelease(group);
+}
+
+-(void)testUnknownEncodeType {
+       CFErrorRef err1 = NULL;
+       CFStringRef invalid_encode_type = CFSTR("no such encoding type ☃✪");
+       SecTransformRef not_created = SecEncodeTransformCreate(invalid_encode_type, &err1);
+       STAssertNil((id)not_created, @"Created encode transform with bad type");
+       STAssertErrorHas((NSError*)err1, @"☃✪", @"Error mentions bad encoding type: %@", err1);
+       fprintf(stderr, "Err1 = %p\n", err1);
+       if (err1) CFRelease(err1);
+       err1 = NULL;
+       
+       CFErrorRef err2 = NULL;
+       SecTransformRef no_type_at_create = SecEncodeTransformCreate(NULL, &err2);
+       fprintf(stderr, "Err2 = %p\n", err2);
+       STAssertNotNil((id)no_type_at_create, @"should be able to create encoder with unset type, but got error %@", err2);
+       
+       if (no_type_at_create) {
+               CFErrorRef err3 = NULL;
+        SecTransformSetAttribute(no_type_at_create, kSecTransformInputAttributeName, NULL, &err3);
+        STAssertNil((id)err3, @"Can't set INPUT: %@", err3);
+               if (err3) {
+            CFRelease(err3);
+            err3 = NULL;
+        }
+               STAssertTrue(SecTransformSetAttribute(no_type_at_create, kSecEncodeTypeAttribute, kSecBase32Encoding, &err3), @"Can't set encode to valid type error: %@", err3);
+               STAssertFalse(SecTransformSetAttribute(no_type_at_create, kSecEncodeTypeAttribute, invalid_encode_type, &err3), @"Set encode to invalid type, no error signaled", err3);
+               fprintf(stderr, "Err3 = %p\n", err3);
+               if (err3) CFRelease(err3);
+
+               CFErrorRef err4 = NULL;
+               CFTypeRef no_result = SecTransformExecute(no_type_at_create, &err4);
+               STAssertNil((id)no_result, @"Got result when none expected %@");
+               STAssertNotNil((id)err4, @"No error when one expected");
+               fprintf(stderr, "Err4 = %p\n", err4);
+               if (err4) CFRelease(err4);
+       } else {
+               STFail(@"Unable to run some tests");
+       }
+       
+       CFRelease(no_type_at_create);
+       CFRelease(invalid_encode_type);
+}
+
+-(void)testNoUnderscores 
+{
+       SecTransformRef zt = SecEncodeTransformCreate(kSecZLibEncoding, NULL);
+       CFErrorRef err = NULL;
+       SecTransformSetAttribute(zt, CFSTR("_FAIL"), kCFBooleanTrue, &err);
+       STAssertNotNil((id)err, @"Expeced an error setting _FAIL");
+       STAssertErrorHas((id)err, @"_FAIL", @"Expected error to contain _FAIL");
+       STAssertErrorHas((id)err, @"Encoder", @"Expecting error to name offending transform", err);
+       CFTypeRef v = SecTransformGetAttribute(zt, CFSTR("_FAIL"));
+       STAssertNil((id)v, @"Expected nil result, got v=%p", v);
+       CFRelease(err);
+       CFRelease(zt);
+}
+
+-(void)testCanFetchDigestSizes
+{
+    NSDictionary *digests = [NSDictionary dictionaryWithObjectsAndKeys:
+                             [NSNumber numberWithInt:128/8], kSecDigestMD2,
+                             [NSNumber numberWithInt:128/8], kSecDigestMD4,
+                             [NSNumber numberWithInt:128/8], kSecDigestMD5,
+                             [NSNumber numberWithInt:160/8], kSecDigestSHA1,
+                             [NSNumber numberWithInt:512/8], kSecDigestSHA2,
+                             nil];
+    NSData *zero = [NSData dataWithBytes:"" length:1];
+    
+    for (NSString *digestType in digests) {
+        CFErrorRef err = NULL;
+        SecTransformRef digest = SecDigestTransformCreate(digestType, 0, &err);
+        STAssertNotNil((id)digest, @"Expected to make digest (err=%@)", err);
+        STAssertNil((id)err, @"Unexpected error: %@", err);
+        NSNumber *actualLength = (NSNumber*)SecTransformGetAttribute(digest, kSecDigestLengthAttribute);
+        STAssertTrue([actualLength intValue] != 0, @"Got zero length back");
+        STAssertNotNil(actualLength, @"Expected to get a length");
+        STAssertEqualObjects(actualLength, [digests objectForKey:digestType], @"Expected lengths to match for %@", digestType);
+        
+        SecTransformSetAttribute(digest, kSecTransformInputAttributeName, zero, &err);
+        STAssertNil((id)err, @"Unexpected error: %@", err);
+        
+        NSData *output = (NSData *)SecTransformExecute(digest, &err);
+        STAssertNil((id)err, @"Unexpected error: %@", err);
+        STAssertNotNil((id)output, @"No output");
+        
+        STAssertEquals([actualLength intValue], (int)[output length], @"Actual output not expected length");
+
+        [output release];
+        CFRelease(digest);
+    }
+}
+
+-(void)testBadTransformTypeNames
+{
+    CFErrorRef error = NULL;
+    Boolean ok = SecTransformRegister(CFSTR("Not valid: has a col..co...double dot thing"), DelayTransformBlock, &error);
+    STAssertFalse(ok, @"Register of name with : fails");
+    STAssertErrorHas((id)error, @":", @"Error mentions invalid character (error=%@)", error);
+    
+    ok = SecTransformRegister(CFSTR("Not/valid has a slash"), DelayTransformBlock, &error);
+    STAssertFalse(ok, @"Register of name with / fails");
+    STAssertErrorHas((id)error, @"/", @"Error mentions invalid character (error=%@)", error);
+
+    ok = SecTransformRegister(CFSTR("https://NOT/VALID"), DelayTransformBlock, &error);
+    STAssertFalse(ok, @"Register of name with : and / fails");
+    STAssertErrorHas((id)error, @"[:/]", @"Error mentions invalid character (error=%@)", error);
+    
+    ok = SecTransformRegister(CFSTR("_not valid at start"), DelayTransformBlock, &error);
+    STAssertFalse(ok, @"Register of _name fails");
+    STAssertErrorHas((id)error, @"_", @"Error mentions invalid character (error=%@)", error);
+
+    ok = SecTransformRegister(CFSTR("it is ok to have a _ after start"), DelayTransformBlock, &error);
+    STAssertTrue(ok, @"Register of _ IN should have worked (error=%@)", error);
+}
+                                  
+-(void)testExecuteBlock {
+       unsigned char *raw_data = (unsigned char *)"Just some bytes, you know";
+       //NSData *empty = [NSData dataWithBytes:NULL length:0];
+       NSData *data = [NSData dataWithBytes:raw_data length:strlen((const char *)raw_data)];
+       //NSUInteger ecnt = [empty retainCount];
+       
+       SecTransformRef zt = SecEncodeTransformCreate(kSecZLibEncoding, NULL);
+       SecTransformSetAttribute(zt, kSecTransformInputAttributeName, data, NULL);
+       dispatch_queue_t q = dispatch_queue_create("com.apple.security.testingQ", NULL);
+       dispatch_queue_t q_sync = dispatch_queue_create("com.apple.security.testingQ_sync", NULL);
+       dispatch_suspend((dispatch_object_t)q_sync);
+       __block BOOL ran_block = NO;
+       
+       SecTransformExecuteAsync(zt, q, ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
+//             if ([empty length]) {
+//                     NSLog(@"Empty data not so empty");
+//             }
+               STAssertTrue(dispatch_get_current_queue() == q, @"block dispatched to proper queue");
+               
+               if (!ran_block) {
+                       usleep(200);
+                       ran_block = YES;
+               }
+               
+               if (message == NULL) {
+                       dispatch_resume((dispatch_object_t)q_sync);
+               }
+       });
+       
+       //STAssertTrue(ecnt < [empty retainCount], @"SecTransformExecute retained block");
+       dispatch_sync(q_sync, ^{ });
+       STAssertTrue(ran_block, @"Block executed");
+       
+       dispatch_release(q_sync);
+       dispatch_release(q);
+       CFRelease(zt);
+       
+       // test for 7735698
+       // STAssertTrue(ecnt == [empty retainCount], @"SecTransformExecute released block");
+}
+
+static SecTransformInstanceBlock ConnectionCheck(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               __block SecTransformMetaAttributeType dir = kSecTransformMetaAttributeValue;
+               
+               SecTransformAttributeRef out_ah = 
+                       SecTranformCustomGetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeRef);
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("DIRECTION"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (CFEqual(value, CFSTR("<"))) 
+                               {
+                                       dir = kSecTransformMetaAttributeHasInboundConnection;
+                               } 
+                               else if (CFEqual(value, CFSTR(">"))) 
+                               {
+                                       dir = kSecTransformMetaAttributeHasOutboundConnections;
+                               } 
+                               else 
+                               {
+                                       return (CFTypeRef)CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "Unsupported direction %@, expected < or >", value);
+                               }
+                               return value;
+                       });
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (dir != kSecTransformMetaAttributeValue) 
+                               {
+                                       if (value) 
+                                       {
+                                               SecTransformCustomSetAttribute(ref, out_ah, kSecTransformMetaAttributeValue, 
+                                                                       SecTranformCustomGetAttribute(ref, value, dir));
+                                       } 
+                                       else 
+                                       {
+                                               SecTransformCustomSetAttribute(ref, out_ah, kSecTransformMetaAttributeValue, NULL);
+                                       }
+                               } 
+                               else 
+                               {
+                                       SecTransformPushbackAttribute(ref, ah, value);
+                               }
+                       
+                               return value;
+                       });
+               
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);       
+}
+
+-(void)testConnectionChecks {
+       
+       CFStringRef name = CFSTR("com.apple.security.unit-test.connection-checks");
+       CFErrorRef error = NULL;
+       SecTransformRegister(name, &*ConnectionCheck, &error); 
+       
+       struct test_case {
+               NSString *attr, *dir;
+               CFBooleanRef expect;
+       } cases[] = {
+               {@"INPUT", @"<", kCFBooleanTrue},
+               {@"OUTPUT", @">", kCFBooleanTrue},
+               {@"INPUT", @">", kCFBooleanFalse},
+               {@"OUTPUT", @"<", kCFBooleanFalse},
+               {@"DIRECTION", @"<", kCFBooleanFalse},
+               {@"DIRECTION", @">", kCFBooleanFalse},
+       };
+       
+       CFIndex i, n = sizeof(cases)/sizeof(test_case);
+       for(i = 0; i < n; ++i) 
+       {
+               test_case *t = cases + i;
+               
+               SecTransformRef cct = SecTransformCreate(name, NULL);
+               SecTransformRef tee0 = SecNullTransformCreate();
+               SecTransformRef tee1 = SecNullTransformCreate();
+               SecTransformRef group = SecTransformCreateGroupTransform();
+               
+               SecTransformSetAttribute(cct, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
+               SecTransformSetAttribute(tee0, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
+               SecTransformSetAttribute(tee1, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
+               
+               SecTransformSetAttribute(tee0, CFSTR("NAME"), CFSTR("tee0"), NULL);
+               SecTransformSetAttribute(tee1, CFSTR("NAME"), CFSTR("tee1"), NULL);
+               
+               SecTransformConnectTransforms(cct, CFSTR("OUTPUT"), tee1, CFSTR("INPUT"), group, NULL);
+               SecTransformConnectTransforms(tee0, CFSTR("OUTPUT"), cct, CFSTR("INPUT"), group, NULL);
+               
+               SecTransformSetAttribute(cct, CFSTR("DIRECTION"), t->dir, NULL);
+               SecTransformSetAttribute(tee0, CFSTR("INPUT"), t->attr, NULL);
+               CFErrorRef err = NULL;
+               CFTypeRef r = SecTransformExecute(group, &err);
+               STAssertNil((id)err, @"Error=%@ for case#%d", err, i);
+               STAssertNotNil((id)r, @"Nil result for case#%d", i);
+               STAssertEqualObjects((id)(t->expect), (id)r, @"Expected result for case#%d %@%@", i, t->dir, t->attr);
+               
+               CFRelease(cct);
+               CFRelease(tee0);
+               CFRelease(tee1);
+               CFRelease(group);
+       }
+        
+}
+
+static SecTransformInstanceBlock PushBackTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               __block CFStringRef input_d = NULL;
+               __block CFStringRef data_d = NULL;
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("DATA"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (!input_d) 
+                               {
+                                       SecTransformPushbackAttribute(ref, ah, value);
+                               } 
+                               else 
+                               {
+                                       if (data_d) 
+                                       {
+                                               CFRelease(data_d);
+                                       }
+                                       data_d = (CFStringRef)CFRetain(value);
+                               }
+                               return value;
+                       });
+       
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (value) 
+                               {
+                                       if (input_d) 
+                                       {
+                                               CFRelease(input_d);
+                                       }
+                                       input_d = (CFStringRef)CFRetain(value);
+                                       if (!data_d) 
+                                       {
+                                               SecTransformPushbackAttribute(ref, ah, value);
+                                               return value;
+                                       }                               
+                               } 
+                               else 
+                               {
+                                       if (data_d) 
+                                       {
+                                               SecTransformPushbackAttribute(ref, ah, NULL);
+                                               value = data_d;
+                                               data_d = NULL;
+                                       }
+                               }
+                               SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, value);
+                               return value;
+                       });
+                       
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);
+}
+
+-(void)testPushback {
+
+       CFStringRef name = CFSTR("com.apple.security.unit-test.basic-pushback");
+       CFStringRef one = CFSTR("1");
+       CFStringRef two = CFSTR("2");
+       CFStringRef expect = CFSTR("12");                                                                                                                                                                                                                                               
+
+       // This unit test makes pushback look very complex, but that is because we are abusing it for test purposes.
+       // normally it is a simple "if I need X before I can go on, and X isn't here yet pushback".   Here we attempt
+       // to carefully sequence 2 attributes to be the inverse of the normal order AND test pushback of NULL as well
+       // as normal values.
+       
+       CFErrorRef error = NULL;
+       SecTransformRegister(name, &PushBackTest, &error); 
+       
+       SecTransformRef pt = SecTransformCreate(name, NULL);
+
+       SecTransformSetAttribute(pt, CFSTR("DATA"), two, NULL);
+       SecTransformSetAttribute(pt, CFSTR("INPUT"), one, NULL);
+       
+       CFTypeRef result = SecTransformExecute(pt, NULL);
+       
+       STAssertEqualObjects((id)result, (id)expect, @"Testing pushback");
+       
+       CFRelease(pt);
+       
+       // NOTE: we want to test doing a double pushback, but that sets the Abort attribute which currently causes an abort not an orderly shutdown
+}
+
+static SecTransformInstanceBlock CustomExternalization(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{      
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               // Create a non-attribute 'instance' variable which will contain
+               // the version number of this class
+               
+               __block float versionNumber = 1.0;
+               
+               // Register the custom externalize override
+               SecTransformActionBlock ExternalizeExtraDataOverride = 
+               ^{
+                       CFStringRef key = CFSTR("VersionNumber");
+                       CFNumberRef value = CFNumberCreate(kCFAllocatorDefault, kCFNumberFloatType, &versionNumber);
+                       
+                       CFDictionaryRef result = CFDictionaryCreate(kCFAllocatorDefault, 
+                               (const void **)&key, (const void **)&value, 
+                               1, &kCFCopyStringDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
+                               
+                       CFRelease(value);
+                       
+                       
+                       return (CFTypeRef)result;
+                       
+               };
+               SecTransformSetTransformAction(ref, kSecTransformActionExternalizeExtraData, ExternalizeExtraDataOverride);
+               
+               // Register the custom internalize override
+               SecTransformDataBlock InternalizeExtraDataOverride = 
+                       ^(CFTypeRef d)
+               {
+                       CFTypeRef internalizeResult = NULL;
+                       //id testObj = (id)d;
+                       
+                       //STAssertNotNil(testObj, @"Internalize did NOT get a dictionary!");
+                               
+                       if (CFDictionaryGetTypeID() == CFGetTypeID(d))
+                       {
+                               CFStringRef key = CFSTR("VersionNumber");
+                               CFDictionaryRef dict = (CFDictionaryRef)d;
+                               CFNumberRef varsNum = (CFNumberRef)CFDictionaryGetValue(dict, key);
+                       //      STAssertNotNil((NSNumber *)varsNum, 
+                       //              @"Unable to retrieve the dictionary when the internalized data override");
+                               if (NULL != varsNum)
+                               {
+                                       Boolean numResult = CFNumberGetValue(varsNum, kCFNumberFloatType, &versionNumber);
+                       //              STAssertTrue(numResult, @"Could not get the version number from the CFNumberRef");
+                                       if (numResult)
+                                       {
+                                               //float knownVersion = 1.0;
+                       //                      STAssertTrue(knownVersion == versionNumber, @"Versions do not Match!");                                         
+                                       }
+                               }
+                       }
+                       return internalizeResult;
+               };
+               SecTransformSetDataAction(ref, kSecTransformActionInternalizeExtraData, 
+                       InternalizeExtraDataOverride);
+               
+               return result;
+       };
+               
+       return Block_copy(instanceBlock);       
+}
+
+/* --------------------------------------------------------------------------
+       method:                 testCustomExternalization
+       description:    Test the ability to write out custom external data
+   -------------------------------------------------------------------------- */
+- (void)testCustomExternalization
+{
+       NSString* ctName = @"com.apple.security.unit-test-customExternalization";
+       NSError* error = nil;
+                       
+       CFStringRef aName = (CFStringRef)ctName;
+       CFErrorRef* anError = (CFErrorRef *)&error;
+       
+       Boolean registerResult = SecTransformRegister(aName, &CustomExternalization, anError);
+       STAssertTrue(registerResult, @"Unable to register the custom externalization transform");
+       
+       SecTransformRef externalTrans = SecTransformCreate((CFStringRef)ctName, 
+               (CFErrorRef *)&error);
+               
+       STAssertNotNil((id)externalTrans, @"Could not create the custom externalization transform");
+       
+       CFDictionaryRef externalData = SecTransformCopyExternalRepresentation(externalTrans);
+       STAssertNotNil((NSDictionary *)externalData, @"Did not get a dictionary from SecTransformCopyExternalRepresentation");
+
+       CFRelease(externalTrans);
+       
+       externalTrans = NULL;
+       externalTrans = SecTransformCreateFromExternalRepresentation(externalData, (CFErrorRef *)&error);
+       STAssertNotNil((id)externalTrans, @"Could not create the custom external representation");
+       CFRelease(externalData);
+       if (NULL != externalTrans)
+       {
+               CFRelease(externalTrans);
+       }
+}
+
+static SecTransformInstanceBlock TestString(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               SecTransformSetDataAction(ref, kSecTransformActionProcessData, 
+                       ^(CFTypeRef value) 
+                       {
+                               CFDataRef d = (CFDataRef)value;
+                               if (d) {
+                                       return (CFTypeRef)CFStringCreateWithBytes(NULL, CFDataGetBytePtr(d), CFDataGetLength(d), kCFStringEncodingMacRoman, FALSE);
+                               } else {
+                                       return (CFTypeRef)d;
+                               }
+                       });
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);
+}
+
+-(void)testStringResults {
+       CFStringRef name = CFSTR("com.apple.security.unit-test.string-converter");
+       CFErrorRef error = NULL;
+       SecTransformRegister(name, &TestString, &error);
+               
+       SecTransformRef sr = SecTransformCreate(name, NULL);
+       
+       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...";
+       CFDataRef data = CFDataCreate(NULL, msg, strlen((const char *)msg));
+       NSString *ns_msg = [NSString stringWithCString:(const char *)msg encoding:NSMacOSRomanStringEncoding];
+       
+       SecTransformRef er = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
+       SecTransformRef dr = SecDecodeTransformCreate(kSecBase32Encoding, NULL);
+       
+       SecTransformSetAttribute(er, kSecTransformInputAttributeName, data, NULL);
+       
+       SecGroupTransformRef group = SecTransformCreateGroupTransform();
+       SecTransformConnectTransforms(er, kSecTransformOutputAttributeName, dr, kSecTransformInputAttributeName, group, NULL);
+       SecTransformConnectTransforms(dr, kSecTransformOutputAttributeName, sr, kSecTransformInputAttributeName, group, NULL);
+
+
+       CFStringRef result = (CFStringRef)SecTransformExecute(sr, NULL);
+       STAssertEqualObjects(ns_msg, (NSString *)result, @"string results");
+
+       CFRelease(result);
+       CFRelease(group);
+       CFRelease(dr);
+       CFRelease(er);
+       CFRelease(sr);
+       if (error)
+       {
+               CFRelease(error);
+       }
+}
+
+static CFNumberRef MakeNumber1(long n)
+{
+       return CFNumberCreate(NULL, kCFNumberLongType, &n);
+}
+
+
+
+static SecTransformInstanceBlock TestRegisterCreate(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       
+       
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               __block long count = 0;
+               
+               __block CFNumberRef countNum = MakeNumber1(count);;
+               SecTransformCustomSetAttribute(ref, CFSTR("Count"), kSecTransformMetaAttributeValue, countNum);
+               CFRelease(countNum);
+               fprintf(stderr, "countNum = %p\n", countNum);
+
+               CFErrorRef result = NULL;
+               SecTransformSetDataAction(ref, kSecTransformActionProcessData,
+                       ^(CFTypeRef value) 
+                       {
+                               CFDataRef d = (CFDataRef)value;
+                               if (d) 
+                               {
+                                       count += CFDataGetLength(d);
+                                       
+                                       CFNumberRef countNum2 = CFNumberCreate(kCFAllocatorDefault, kCFNumberLongType, &count);
+                                       SecTransformCustomSetAttribute(ref, CFSTR("Count"), kSecTransformMetaAttributeValue, countNum2);
+                                       CFRelease(countNum2);
+                                       fprintf(stderr, "countNum = %p\n", countNum);
+                                       
+                               } else 
+                               {
+                                       SecTransformCustomSetAttribute(ref, CFSTR("Count"), kSecTransformMetaAttributeValue, NULL);
+                               }
+                               return value;
+                       });
+                       
+               
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);       
+}
+
+- (void)testRegisterCreate 
+{
+       CFStringRef name = CFSTR("com.apple.security.unit-test.novel-unique-at-least-unusual-name");
+       long count = 0;
+       CFNumberRef countNum = NULL;
+       CFErrorRef error = NULL;
+       Boolean ok = SecTransformRegister(name, &TestRegisterCreate, &error);
+       STAssertTrue(ok, @"Successful register");
+       
+       SecTransformRef tr = SecTransformCreate(name, NULL);
+       STAssertNotNil((NSObject *)tr, @"newly created custom transform");
+       SecTransformSetAttribute(tr, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
+       
+       char *data_bytes = (char *)"It was the best of transforms, it was the worst of transforms.";
+       CFDataRef data = CFDataCreate(NULL, (const UInt8 *)data_bytes, strlen(data_bytes));
+       
+       SecTransformSetAttribute(tr, kSecTransformInputAttributeName, data, NULL);
+       
+       SecTransformRef nt = SecNullTransformCreate();
+       SecTransformRef tg = SecTransformCreateGroupTransform();
+       SecTransformConnectTransforms(tr, CFSTR("OUTPUT"), nt, CFSTR("DISCARD"), tg, &error);
+       STAssertNil((id)error, @"Connected tr's output to nt's discard: %@", error);
+       SecTransformConnectTransforms(tr, CFSTR("Count"), nt, CFSTR("INPUT"), tg, &error);
+       STAssertNil((id)error, @"Connected tr's count to nt's input: %@", error);
+       
+       SecTransformSetAttribute(nt, CFSTR("DEBUG"), kCFBooleanTrue, NULL);
+       
+       usleep(100);
+       countNum = (CFNumberRef)SecTransformGetAttribute(tr, CFSTR("Count"));
+       CFNumberGetValue(countNum, kCFNumberLongType, &count);
+       CFRelease(countNum);    
+       STAssertTrue(count == 0, @"length unchanged before execute");
+       
+       countNum = (CFNumberRef)SecTransformExecute(tg, NULL);
+       STAssertNotNil((id)countNum, @"Got result from execute");
+       STAssertEquals(CFGetTypeID(countNum), CFNumberGetTypeID(), @"expected a number from execute");
+       CFNumberGetValue(countNum, kCFNumberLongType, &count);
+       CFRelease(countNum);
+
+       STAssertTrue(count == CFDataGetLength(data), @"Wrong data length");
+       
+       CFRelease(tg);
+       CFRelease(nt);
+       CFRelease(tr);
+       CFRelease(data);
+}
+
+
+static SecTransformInstanceBlock CountTransformTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               SecTransformSetAttributeAction(ref,kSecTransformActionAttributeNotification, CFSTR("INPUT"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (value) {
+                                       if (CFGetTypeID(value) != CFNumberGetTypeID()) {
+                                               SecTransformCustomSetAttribute(ref, CFSTR("ABORT"), kSecTransformMetaAttributeValue, CFSTR("Bad type"));
+                                               return value;
+                                       }
+                                       CFNumberRef nr = (CFNumberRef)value;
+                                       int max, i;
+                                       CFNumberGetValue(nr, kCFNumberIntType, &max);
+                                       for(i = 0; i < max; ++i) {
+                                               nr = CFNumberCreate(NULL, kCFNumberIntType, &i);
+                                               SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, nr);
+                                               CFRelease(nr);
+                                       }
+                               } else {
+                                       SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, value);
+                               }
+
+                               return value;
+                       });
+
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);
+}
+                                                       
+static SecTransformRef count_transform(int n) {
+       CFStringRef name = CFSTR("com.apple.security.unit-test.count");
+       static dispatch_once_t once;
+
+       dispatch_once(&once, 
+       ^{
+               SecTransformRegister(name, &CountTransformTest, NULL);
+       });
+       
+       SecTransformRef ct = SecTransformCreate(name, NULL);
+       CFNumberRef num = CFNumberCreate(NULL, kCFNumberIntType, &n);
+       SecTransformSetAttribute(ct, CFSTR("INPUT"), num, NULL);
+       CFRelease(num);
+       
+       return ct;
+}
+
+
+static SecTransformInstanceBlock StallTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               __block bool go = false;
+
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("GO"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               go = true;
+                               return value;
+                       });
+
+               SecTransformAttributeRef in_ah = SecTransformCustomGetAttribute(ref, CFSTR("INPUT"), kSecTransformMetaAttributeRef);
+               SecTransformAttributeRef out_ah = SecTransformCustomGetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeRef);
+
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, NULL, 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (!go) {
+                                       SecTransformPushbackAttribute(ref, ah, value);
+                               } else {
+                                       if (ah == in_ah) {
+                                               SecTransformCustomSetAttribute(ref, out_ah, kSecTransformMetaAttributeValue, value);
+                                       }
+                               }
+                               return value;
+                       });
+
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);
+}
+                                                       
+-(void)testStall {
+       CFStringRef name = CFSTR("com.apple.security.unit-test.stall");
+       
+       (void)SecTransformRegister(name, &StallTest, NULL);
+
+       SecTransformRef stall = SecTransformCreate(name, NULL);
+       SecTransformRef seven = count_transform(7);
+       SecTransformRef group = SecTransformCreateGroupTransform();
+       SecTransformRef delay = delay_transform(NSEC_PER_SEC / 10);
+       
+       SecTransformConnectTransforms(seven, CFSTR("OUTPUT"), stall, CFSTR("FOO"), group, NULL);
+       SecTransformConnectTransforms(seven, CFSTR("OUTPUT"), stall, CFSTR("BAR"), group, NULL);
+       SecTransformConnectTransforms(seven, CFSTR("OUTPUT"), stall, CFSTR("BAZ"), group, NULL);
+       SecTransformConnectTransforms(seven, CFSTR("OUTPUT"), stall, CFSTR("INPUT"), group, NULL);
+       SecTransformConnectTransforms(delay, CFSTR("OUTPUT"), stall, CFSTR("GO"), group, NULL);
+       
+       SecTransformSetAttribute(delay, CFSTR("INPUT"), (CFNumberRef)[NSNumber numberWithInt:42], NULL);
+       
+       CFErrorRef err = NULL;
+       CFTypeRef r = SecTransformExecute(group, &err);
+       
+       STAssertNotNil((id)r, @"Results from testStall");
+       STAssertNil((id)err, @"Got %@ error from testStall", err);
+       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];
+       STAssertEqualObjects((id)r, array_seven, @"Correct stall test results");
+       
+       CFRelease(delay);
+       CFRelease(group);
+       CFRelease(seven);
+       CFRelease(stall);
+}
+
+-(void)testInappropriateExecution
+{
+       // We want to have more then enough work for all the CPUs to help force a race, so twice
+       // the number of logical CPUs should do it.   NOTE: the completion blocks got to a low
+       // priority concurrent queue to encourage them to finish out of order and put more
+       // stress on the system we are testing.
+
+       int logical_cpus = 1;
+       size_t int_size = sizeof(logical_cpus);
+       int return_code = sysctlbyname("hw.logicalcpu_max", &logical_cpus, &int_size, NULL, 0);
+       int e = errno; // Save this value so it doesn't get trashed by any subsequent syscalls
+       STAssertEquals(return_code, 0, @"sysctlbyname failed %s (%d), assuming 1 CPU", strerror(e), e);
+       
+       SecTransformRef count_a_bunch = count_transform(logical_cpus * 2);
+       CFErrorRef err = NULL;
+       dispatch_group_t wait_for_async_to_complete = dispatch_group_create();
+       dispatch_group_t outstanding_executions = dispatch_group_create();
+       SecTransformRef count_group = SecTransformCreateGroupTransform();
+       
+       SecTransformConnectTransforms(count_a_bunch, CFSTR("kludge1"), count_a_bunch, CFSTR("kludge2"), count_group, &err);
+       STAssertNil((id)err, @"Error (%@) connecting count transform to itself", err);
+
+       dispatch_group_enter(wait_for_async_to_complete);
+       SecTransformExecuteAsync(count_a_bunch, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^(CFTypeRef message, CFErrorRef error, Boolean isFinal)
+       {
+               dispatch_group_enter(outstanding_executions);
+               
+               CFErrorRef err = NULL;
+               CFTypeRef no_result = SecTransformExecute(count_a_bunch, &err);
+               STAssertNil((id)no_result, @"Attempting to execute an already executing transform should fail, not provide results: %@", no_result);
+               STAssertNotNil((id)err, @"Attempting to execute an already executing transform should produce some sort of error");
+               CFRelease(err);
+               
+               dispatch_group_leave(outstanding_executions);
+               
+               if (isFinal)
+               {
+                       // Give any pending executions time to get to the group_enter
+                       usleep(100);
+                       dispatch_group_wait(outstanding_executions, DISPATCH_TIME_FOREVER);
+                       dispatch_release(outstanding_executions);
+                       dispatch_group_leave(wait_for_async_to_complete);
+               }
+       });
+       
+       // Before that SecTransformExecuteAsync completes, we do some more work at >low priority to help
+       // keep the completion blocks landing out of order.    In particular we run a transform to
+       // completion, and then confirm that we can't run it again.
+       
+       SecTransformRef no_work = SecNullTransformCreate();
+       SecTransformRef no_work_group = SecTransformCreateGroupTransform();
+       SecTransformConnectTransforms(no_work, CFSTR("kludge1"), no_work, CFSTR("kludge2"), no_work_group, &err);
+       STAssertNil((id)err, @"Can't connect no_work to itself (to make no_work_group), err=%@", err);
+       
+       SecTransformSetAttribute(no_work, CFSTR("INPUT"), CFSTR("value"), NULL);
+       CFTypeRef no_result = SecTransformExecute(no_work_group, &err);
+       STAssertNil((id)err, @"First execute of Null Transform should be ok, got e=%@", err);
+       STAssertNotNil((id)no_result, @"First execute of Null Transform should produce a value");
+       
+       no_result = SecTransformExecute(no_work_group, &err);
+       
+       STAssertNotNil((id)err, @"Second execute of Null Transform should fail!");
+       STAssertNil((id)no_result, @"Second execute of Null Transform shouldn't produce a value, got r=%@", no_result);
+       CFRelease(err);
+       
+       // Now we wait for that first batch of tests to finish, we don't want to call STFail after self goes away.
+       
+       dispatch_group_wait(wait_for_async_to_complete, DISPATCH_TIME_FOREVER);
+       dispatch_release(wait_for_async_to_complete);
+       
+       if (no_result) CFRelease(no_result);
+       if (no_work) CFRelease(no_work);
+       if (no_work_group) CFRelease(no_work_group);
+       if (count_group) CFRelease(count_group);
+       if (count_a_bunch) CFRelease(count_a_bunch);
+}
+                                                                 
+static SecTransformInstanceBlock ConnectionReqTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               SecTransformAttributeRef xah = 
+                       (SecTransformAttributeRef)SecTranformCustomGetAttribute(ref, CFSTR("XYZZY"), kSecTransformMetaAttributeRef);
+               
+               SecTransformCustomSetAttribute(ref, xah, kSecTransformMetaAttributeRequiresOutboundConnection, kCFBooleanTrue);
+               SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeRequiresOutboundConnection, kCFBooleanFalse);
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               SecTransformCustomSetAttribute(ref, xah, kSecTransformMetaAttributeValue, value);
+                               return value;
+                       });
+               
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);       
+}
+
+
+-(void)testConnectionReq {
+
+       CFStringRef req_xyzzy_name = CFSTR("com.apple.security.unit-test.req_xyzzy");
+       
+       (void)SecTransformRegister(req_xyzzy_name, &ConnectionReqTest, NULL);
+
+       SecTransformRef tr_req_xyzzy = SecTransformCreate(req_xyzzy_name, NULL);
+       
+       CFTypeRef in_value = (CFTypeRef)@"Fnord";
+       SecTransformSetAttribute(tr_req_xyzzy, CFSTR("INPUT"), in_value, NULL);
+       
+       CFErrorRef err = NULL;
+       CFTypeRef r = SecTransformExecute(tr_req_xyzzy, &err);
+       
+       STAssertNil((id)r, @"Execute of tr_req_xyzzy with no xyzzy r=%@", r);
+       STAssertErrorHas((id)err, @"req_xyzzy", @"Error failed to refer to the transform by name (%@)", err);
+       STAssertErrorHas((id)err, @"XYZZY", @"Error failed to refer to missing attribute by name (%@)", err);
+       STAssertErrorHas((id)err, @"requires.*outbound connection", @"Error failed to diagnose invalid condition (%@)", err);
+       
+       CFRelease(err);
+       CFRelease(tr_req_xyzzy);
+       if (r) CFRelease(r);
+
+       /*
+       
+       Note For Josh:
+       
+       To make this work we need Josh's fix for FindLastTransform!
+       
+       CFRelease(tr_req_xyzzy);
+       tr_req_xyzzy = SecTransformCreate(req_xyzzy_name, NULL);
+       SecTransformSetAttribute(tr_req_xyzzy, CFSTR("INPUT"), in_value, NULL);
+       
+       SecTransformRef group = SecTransformCreateGroupTransform();
+       SecTransformRef tee = SecNullTransformCreate();
+       SecTransformConnectTransforms(tr_req_xyzzy, CFSTR("XYZZY"), tee, kSecTransformInputAttributeName, group, &err);
+       STAssertNil((id)err, @"err=%@ from connect", err);
+       STAssertNotNil((id)group, @"No group after connect");
+       r = SecTransformExecute(group, &err);
+       STAssertNil((id)err, @"Execute err=%@");
+       STAssertEqualObjects((id)in_value, (id)r, @"Execution Result");
+       
+       */
+}
+
+static SecTransformInstanceBlock DeferredTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               SecTransformCustomSetAttribute(ref, CFSTR("LATE"), kSecTransformMetaAttributeDeferred, kCFBooleanTrue);
+               SecTransformCustomSetAttribute(ref, CFSTR("INPUT"), kSecTransformMetaAttributeDeferred, kCFBooleanFalse);
+               
+               __block CFTypeRef in_v = NULL, late_v = NULL;
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (NULL != late_v) {
+                                       SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, 
+                                                                                                  CreateGenericErrorRef(CFSTR("FAIL"), 1, "LATE (%@) should process after INPUT (%@)", late_v, value));
+                               }
+                               in_v = value;
+                               return value;
+                       });
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("LATE"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (NULL == in_v) {
+                                       SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, 
+                                                                                                  CreateGenericErrorRef(CFSTR("FAIL"), 1, "INPUT (%@) should process before LATE (%@)", in_v, value));
+                               }
+                               
+                               late_v = value;
+                               SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, NULL);
+                               return value;
+                       });
+               
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);       
+}
+
+
+-(void)testDeferred {
+       CFStringRef deferred_name = CFSTR("com.apple.security.unit-test.deferred");
+       
+       (void)SecTransformRegister(deferred_name, &DeferredTest, NULL);
+       
+       SecTransformRef dt = SecTransformCreate(deferred_name, NULL);
+       
+       // these set attribute calls are failing, but we're ignoring the failures
+       SecTransformSetAttribute(dt, CFSTR("INPUT"), (CFTypeRef)CFSTR("BLAH"), NULL);
+       SecTransformSetAttribute(dt, CFSTR("LATE"), (CFTypeRef)CFSTR("QUUX"), NULL);
+       CFErrorRef err = NULL;
+       SecTransformExecute(dt, &err);
+       STAssertNil((id)err, @"Error from execute err=%@", err);
+       
+       if (err) CFRelease(err);
+       // CFRelease(dt);
+}
+
+static SecTransformInstanceBlock SaveRestoreTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               SecTransformCustomSetAttribute(ref, CFSTR("Needed"), kSecTransformMetaAttributeRequired, kCFBooleanTrue);
+               SecTransformCustomSetAttribute(ref, CFSTR("NoSaves"), kSecTransformMetaAttributeExternalize, kCFBooleanFalse);
+
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);       
+}
+
+-(void)testSaveRestore 
+{
+       
+       unsigned char raw_data[] = "Val-U-Sav, nw wth lss vwls!";
+       CFDataRef data = CFDataCreate(NULL, (const UInt8*)&raw_data, sizeof(raw_data));
+       CFErrorRef err = NULL;
+       
+       CFStringRef name = CFSTR("com.apple.security.unit-test.SaveRestoreTest");
+       
+       (void)SecTransformRegister(name, &SaveRestoreTest, NULL); 
+       
+       SecTransformRef tr1 = SecTransformCreate(name, NULL);
+       
+       SecTransformSetAttribute(tr1, CFSTR("Optional"), CFSTR("42"), NULL);
+       SecTransformSetAttribute(tr1, CFSTR("Needed"), CFSTR("and provided"), NULL);
+       SecTransformSetAttribute(tr1, CFSTR("NoSaves"), CFSTR("42"), NULL);
+       
+       CFDictionaryRef xr = SecTransformCopyExternalRepresentation(tr1);
+       STAssertNotNil((NSDictionary *)xr, @"external rep");
+       SecTransformRef tr2 = SecTransformCreateFromExternalRepresentation(xr, NULL);
+       SecTransformSetAttribute(tr2, kSecTransformInputAttributeName, data, NULL);
+       CFTypeRef none = SecTransformGetAttribute(tr2, CFSTR("NoSaves"));
+       STAssertNil((id)none, @"Expected %@ to be nil", none);
+       CFTypeRef forty_two = SecTransformGetAttribute(tr2, CFSTR("Optional"));
+       STAssertEqualObjects((id)forty_two, @"42", @"restored incorrect value");
+       
+       CFDataRef d = (CFDataRef)SecTransformExecute((NSObject *)tr2,  &err);
+       
+       STAssertNotNil((NSData * )d, @"execute result (err=%@)", err);
+       
+       if (err) {
+               CFRelease(err);
+       }
+       
+       CFRelease(tr1);
+       CFRelease(tr2);
+       CFRelease(xr);
+       
+       tr1 = SecTransformCreate(name, NULL);
+       SecTransformSetAttribute(tr1, CFSTR("Needed"), CFSTR("and provided"), NULL);
+       SecTransformSetAttribute(tr1, CFSTR("NoSaves"), CFSTR("42"), NULL);
+       xr = SecTransformCopyExternalRepresentation(tr1);
+       tr2 = SecTransformCreateFromExternalRepresentation(xr, NULL);
+       
+       
+       
+       //tr2 = SecTransformCreateFromExternalRepresentation(xr, NULL);
+       SecTransformRef tga = SecTransformCreateGroupTransform();
+       SecTransformSetAttribute(tr1, kSecTransformInputAttributeName, data, NULL);
+       
+       // XXX did not swap
+       SecTransformConnectTransforms(tr1, CFSTR("OUTPUT"), tr2, CFSTR("INPUT"), tga, NULL);
+       CFStringRef has1 = CFSTR("I has one!");
+       CFStringRef has2 = CFSTR("I has two of them!");
+       SecTransformSetAttribute(tr1, CFSTR("Needed"), has1, NULL);
+       SecTransformSetAttribute(tr2, CFSTR("Needed"), has2, NULL);
+       xr = SecTransformCopyExternalRepresentation(tr1);
+       STAssertNotNil((NSDictionary *)xr, @"external rep for 2");
+       NSLog(@"xr=%@", xr);
+       
+       SecTransformRef tgb = SecTransformCreateFromExternalRepresentation(xr, &err);
+       STAssertNil((id)tgb, @"made transform group with duplicate labels");
+       STAssertErrorHas((id)err, (NSString*)name, @"Error failed to identify the transform (%@)", err);
+       STAssertErrorHas((id)err, @"damage|duplicate", @"Error failed to diagnose the invalid condition (%@)", err);
+       
+       CFStringRef new_name2 = CFSTR("SaveRestoreTestThingie#2");
+       CFStringRef fetched_name;
+       int attempts;
+       
+       for(attempts = 0; attempts < 20; ++attempts) 
+       {
+               SecTransformSetAttribute(tr2, CFSTR("NAME"), new_name2, &err);
+               fetched_name = (CFStringRef)SecTransformGetAttribute(tr2, CFSTR("NAME"));
+               
+               STAssertNil((id)err, @"Error from setting tr2's name: %@", err);
+               STAssertEqualObjects((id)fetched_name, (id)new_name2, @"Set tr2's name, attempt %d", attempts);
+               if (CFEqual(fetched_name, new_name2)) 
+               {
+                       break;
+               }
+               if (attempts > 10) 
+               {
+                       usleep(1000);
+               }
+       }
+       
+       xr = SecTransformCopyExternalRepresentation(tr1);
+       STAssertNotNil((NSDictionary *)xr, @"external rep for 2, take 2");
+       NSLog(@"xr=%@", xr);
+       
+       tgb = SecTransformCreateFromExternalRepresentation(xr, &err);
+       STAssertNotNil((id)tgb, @"made transform group (take 2)");
+       STAssertNil((id)err, @"error from make 2 take 2 (err=%@)", err);
+       
+       SecTransformRef tr1b = SecTransformFindByName(tgb, (CFStringRef)SecTransformGetAttribute(tr1, CFSTR("NAME")));
+       STAssertNotNil((id)tr1b, @"Found tr1b");
+       SecTransformRef tr2b = SecTransformFindByName(tgb, (CFStringRef)SecTransformGetAttribute(tr2, CFSTR("NAME")));
+       STAssertNotNil((id)tr2b, @"Found tr2b");
+       
+       CFStringRef has1b = (CFStringRef)SecTransformGetAttribute(tr1b, CFSTR("Needed"));
+       STAssertNotNil((id)tr1b, @"tr1b's name");
+       CFStringRef has2b = (CFStringRef)SecTransformGetAttribute(tr2b, CFSTR("Needed"));
+       STAssertNotNil((id)tr2b, @"tr1b's name");
+       
+       STAssertEqualObjects((id)has1, (id)has1b, @"has1 == has1b");
+       STAssertEqualObjects((id)has2, (id)has2b, @"has2 == has2b");
+       
+}
+                                                                 
+-(void)testRequiredAttributes 
+{
+       CFStringRef name = CFSTR("com.apple.security.unit-test.requiresStuffThings");
+       CFErrorRef error;
+       // In addition to testing required attributes, this also does a  partial "lifecycle" test, making sure we
+       // pass through the stages, don't regress stages, and don't receive the wrong events in the wrong stages.
+       typedef enum { S_INITED = 0, S_STARTED, S_RUN, S_EOS, S_GONE } state_t;
+       
+       __block state_t state = S_INITED;
+       dispatch_group_t leave_on_finalize = dispatch_group_create();
+       
+       SecTransformCreateBlock required_attributes_create_block = ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params) {
+               params->overrideAttribute(kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^(SecTransformAttributeRef attribute, CFTypeRef value) {
+                       // NOTE: this is for testing with a single data value, not a series.
+                       if (value)
+                       {
+                               STAssertTrue(state == S_STARTED, @"Init'ed for data (state=%d)", state);
+                               state = S_RUN;
+                       } else {
+                               STAssertTrue(state == S_RUN, @"In run state at EOS (state=%d)", state);
+                               state = S_EOS;
+                       }
+                       params->send(kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, value);
+                       return value;
+               });
+               
+               params->send(CFSTR("Stuff"), kSecTransformMetaAttributeRequired, kCFBooleanTrue);
+               params->send(CFSTR("Things"), kSecTransformMetaAttributeRequired, kCFBooleanTrue);
+               
+               params->overrideTransform(kSecTransformActionStartingExecution, ^{
+                       STAssertTrue(state == S_INITED, @"Inited (state=%d)");
+                       state = S_STARTED;
+                       return (CFTypeRef)NULL;
+               });
+               
+               params->overrideTransform(kSecTransformActionFinalize, ^{
+                       state = S_GONE;
+                       dispatch_group_leave(leave_on_finalize);
+                       return (CFTypeRef)NULL;
+               });
+       };
+       
+       dispatch_group_enter(leave_on_finalize);
+       SecTransformRef tr = custom_transform(name, required_attributes_create_block);
+       STAssertNotNil((NSObject *)tr, @"newly created custom transform");
+       
+       char *data_bytes = (char *)"It was the best of transforms, it was the worst of transforms.";
+       CFDataRef data = CFDataCreate(NULL, (const UInt8*)data_bytes, strlen(data_bytes));
+       SecTransformSetAttribute(tr, kSecTransformInputAttributeName, data, NULL);
+       usleep(100);
+       STAssertTrue(state == S_INITED, @"not run yet");
+       CFDataRef rdata = (CFDataRef)SecTransformExecute((NSObject *)tr, &error);
+       
+       STAssertTrue(rdata == NULL, @"Expected no result, but got: %@", rdata);
+       STAssertErrorHas((id)error, @"missing required attributes?", @"Error describes condition (%@)", error);
+       STAssertErrorHas((id)error, @" Things[ ,)]", @"Missing attributes named (%@)", error);
+       STAssertErrorHas((id)error, @" Stuff[ ,)]", @"Missing attributes named (%@)", error);
+       STAssertErrorHas((id)error, @"requiresStuffThings", @"Name of erroring Transform in message (%@)", error);
+       
+       if (error) {
+               CFRelease(error);
+       }
+       CFRelease(tr);
+
+       STAssertFalse(dispatch_group_wait(leave_on_finalize, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"group was ready");
+       STAssertTrue(state == S_GONE, @"Transform should be gone, state=%d", state);
+       
+       dispatch_group_enter(leave_on_finalize);
+       state = S_INITED;
+       tr = custom_transform(name, required_attributes_create_block);
+       STAssertNotNil((NSObject *)tr, @"newly created custom transform");
+       
+       error = NULL;
+       SecTransformSetAttribute(tr, kSecTransformInputAttributeName, data, NULL);
+       SecTransformSetAttribute(tr, CFSTR("Things"), CFSTR("grubby things"), NULL);
+       SecTransformSetAttribute(tr, CFSTR("Stuff"), CFSTR("Cool stuff"), NULL);
+       rdata = (CFDataRef)SecTransformExecute(tr,  &error);
+       
+       STAssertNotNil((NSData *)rdata, @"Got data back");
+       STAssertEqualObjects((NSData *)rdata, (NSData *)data, @"Data unchanged");
+       STAssertTrue(state == S_EOS, @"Transform hit EOS");
+       STAssertTrue(error == NULL, @"Error not set");
+       
+       CFRelease(tr);
+       STAssertFalse(dispatch_group_wait(leave_on_finalize, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"group was ready");
+       STAssertTrue(state == S_GONE, @"Transform gone (state=%d)", state);
+       dispatch_group_notify(leave_on_finalize, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
+               dispatch_release(leave_on_finalize);
+       });
+}
+
+static SecTransformInstanceBlock AttributeNotificationTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               
+                       
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, NULL, 
+                       ^(SecTransformStringOrAttributeRef ah, CFTypeRef value) 
+                       {
+                               SecTransformCustomSetAttribute(ref, CFSTR("Generic"), kSecTransformMetaAttributeValue, kCFBooleanTrue);
+                               return value;
+                       });
+
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("Specific"), 
+                       ^(SecTransformStringOrAttributeRef ah, CFTypeRef value) 
+                       {
+                               SecTransformCustomSetAttribute(ref, CFSTR("Specific"), kSecTransformMetaAttributeValue, kCFBooleanTrue);
+                               return value;
+                               
+                               });
+
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("AlsoSpecific"), 
+                       ^(SecTransformStringOrAttributeRef ah, CFTypeRef value) 
+                       {
+                               SecTransformCustomSetAttribute(ref, CFSTR("AlsoSpecific"), kSecTransformMetaAttributeValue, kCFBooleanTrue);
+                               
+                               return value;
+                       });
+
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);
+}
+
+-(void)testAttributeNotifications
+{
+       NSString *name = @"com.apple.security.unit-test.testAttributeNotifications";
+       Boolean generic_called = NO;
+       Boolean specific_called = NO;
+       Boolean also_specific_called = NO;
+       
+       Boolean ok = SecTransformRegister((CFStringRef)name, &AttributeNotificationTest, NULL);
+       
+       STAssertTrue(ok, @"Successful register");
+       
+       SecTransformRef tr = SecTransformCreate((CFStringRef)name, NULL);
+       
+       CFStringRef aNameStr = ((CFStringRef)name);
+       SecTransformSetAttribute(tr, CFSTR("Generic"), aNameStr, NULL);
+       SecTransformSetAttribute(tr, CFSTR("Specific"), aNameStr, NULL);
+       SecTransformSetAttribute(tr, CFSTR("AlsoSpecific"), aNameStr, NULL);
+       
+       generic_called = (kCFBooleanTrue == (CFBooleanRef)SecTransformGetAttribute(tr, CFSTR("Generic")));
+       specific_called = (kCFBooleanTrue == (CFBooleanRef)SecTransformGetAttribute(tr, CFSTR("Specific")));
+       also_specific_called = (kCFBooleanTrue == (CFBooleanRef)SecTransformGetAttribute(tr, CFSTR("AlsoSpecific")));
+       
+       
+       STAssertTrue(generic_called, @"generic called");
+       STAssertTrue(specific_called, @"specific called");
+       STAssertTrue(also_specific_called, @"also specific called");
+
+       CFRelease(tr);
+}
+
+-(void)testEncryptAndDecryptTransforms
+{
+       NSAutoreleasePool *pool = [NSAutoreleasePool new];      
+               
+       // generate a symmetrical key for testing
+       OSStatus err = errSecSuccess;
+       
+       NSString* algNames[] = 
+       {
+               @"AES 128",
+               @"AES 192",
+               @"AES 256",
+               @"DES",
+               @"3DES",
+               @"CAST",
+               @"RC4"
+       };
+       
+       CSSM_ALGORITHMS symmetricalAlgos[] = 
+       {
+               CSSM_ALGID_AES,
+               CSSM_ALGID_AES,
+               CSSM_ALGID_AES,
+               CSSM_ALGID_DES,
+               CSSM_ALGID_3DES_3KEY_EDE,
+               CSSM_ALGID_CAST,
+               CSSM_ALGID_RC4
+       };
+       
+       uint32 keySizes[] = 
+       {
+               128,
+               192,
+               256,
+               64,
+               192,
+               40,
+               8
+       };
+       
+       CSSM_KEYUSE keyUse = CSSM_KEYUSE_ANY;
+       CSSM_KEYATTR_FLAGS keyAttrFlags = CSSM_KEYATTR_RETURN_DEFAULT;
+       SecAccessRef accessRef = NULL;
+       CSSM_CC_HANDLE handle = ((CSSM_CC_HANDLE)0);
+       
+       NSString* dataStr = @"At the round earth's imagined corners blow\
+       Your trumpets, angels, and arise, arise\
+       From death, you numberless infinities\
+       Of souls, and to your scattered bodies go,\
+       All whom the flood did, and fire shall, overthrow,\
+       All whom war, dearth, age, agues, tyrannies,\
+       Despair, law, chance, hath slain, and you whose eyes\
+       Shall behold God, and never taste death's woe.\
+       But let them sleep, Lord, and me mourn a space,\
+       For, if above all these my sins abound,\
+       'Tis late to ask abundance of Thy grace,\
+       When we are there. Here on this lowly ground\
+       Teach me how to repent; for that's as good\
+       As if Thou'dst sealed my pardon, with Thy blood.";
+       
+       NSData* testData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
+       int numItems = (sizeof(symmetricalAlgos) / sizeof(CSSM_ALGORITHMS));
+       int iCnt = 0;
+       
+       for (iCnt = 0; iCnt < numItems; iCnt++)
+       {
+               SecKeyRef testKey = NULL;
+               CSSM_ALGORITHMS algoToUse = symmetricalAlgos[iCnt];
+               uint32 keySizeInBits = keySizes[iCnt];
+               
+               err = SecKeyGenerate(NULL, algoToUse, keySizeInBits, handle, keyUse, keyAttrFlags, accessRef, &testKey);
+               STAssertTrue(err == errSecSuccess, [NSString stringWithFormat:@"Unable to create a symmetrical key %@", algNames[iCnt]]);
+               if (errSecSuccess != err)
+               {
+                       continue;
+               }
+               __block CFErrorRef error = NULL;
+               
+               SecTransformRef encryptSymRef = NULL;
+               encryptSymRef = SecEncryptTransformCreate(testKey, &error);
+               if (NULL != error)
+               {
+                       CFRelease(testKey);
+                       STAssertTrue(NO, [NSString stringWithFormat:@"Unable to create the encrypt transform for key %@", algNames[iCnt]]);
+                       continue;
+               }
+               
+               SecTransformRef decryptSymRef = SecDecryptTransformCreate(testKey, &error);
+               if (NULL != error)
+               {
+                       CFRelease(testKey);
+                       CFRelease(encryptSymRef);
+                       STAssertTrue(NO, [NSString stringWithFormat:@"Unable to create the decrypt transform for key %@", algNames[iCnt]]);
+                       continue;
+               }
+               
+               // connect the output of the encryption to the input of the decryption transform
+               
+               SecGroupTransformRef group = SecTransformCreateGroupTransform();
+               (void)SecTransformConnectTransforms(encryptSymRef, kSecTransformOutputAttributeName,
+                                               decryptSymRef, kSecTransformInputAttributeName, 
+                                               group, &error);
+               if (NULL != error)
+               {
+                       CFRelease(testKey);
+                       CFRelease(encryptSymRef);
+                       CFRelease(decryptSymRef);
+                       STAssertTrue(NO, [NSString stringWithFormat:@"Unable to connect transforms for key %@", algNames[iCnt]]);
+                       continue;
+               }
+               
+               
+               NSInputStream* dataStream = [NSInputStream inputStreamWithData:testData];
+               [dataStream open];
+               
+               SecTransformSetAttribute(encryptSymRef, kSecTransformInputAttributeName, (CFTypeRef)dataStream, &error);
+               if (NULL != error)
+               {
+                       CFRelease(testKey);
+                       CFRelease(encryptSymRef);
+                       CFRelease(decryptSymRef);
+                       STAssertTrue(NO, [NSString stringWithFormat:@"Unable to set the input for key %@", algNames[iCnt]]);
+                       continue;
+               }
+               
+               CFTypeRef transformResult = SecTransformExecute(encryptSymRef, &error);
+               CFRelease(group);
+               CFRelease(encryptSymRef);
+               CFRelease(decryptSymRef);
+               
+               if (NULL != error)
+               {
+                       STAssertTrue(NO, [NSString stringWithFormat:@"returned an error for algo %@", algNames[iCnt]]);
+                       continue;
+                       CFRelease(error);
+               }
+               
+               if (NULL == transformResult || 0 == [(NSData*)transformResult length])
+               {
+                       STAssertTrue(NO, [NSString stringWithFormat:@"transformResult was NULL or empty for %@", algNames[iCnt]]);
+                       continue;
+               }
+               
+               NSData* resultData = nil;
+               if (CFGetTypeID(transformResult) == CFDataGetTypeID())
+               {
+                       resultData = (NSData*)transformResult;                  
+                       [resultData autorelease];
+               }
+               
+               CFRelease(testKey);
+                               
+               STAssertTrue([testData isEqualToData:resultData], @"The output of the decrypt transform does NOT match the original input!");           
+       }
+       
+       
+       SecKeyRef publicKey = NULL;
+       SecKeyRef privateKey = NULL;
+       
+       keyAttrFlags = CSSM_KEYATTR_RETURN_REF;
+       
+       const uint32 publicKeyAttributes = CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_RETURN_REF;
+       const uint32 privateKeyAttributes = CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_RETURN_REF | CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_EXTRACTABLE;
+       
+       CSSM_KEYUSE pubKeyUse = CSSM_KEYUSE_VERIFY | CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_WRAP;
+       CSSM_KEYUSE privKeyUse = CSSM_KEYUSE_SIGN | CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_UNWRAP;
+       
+       
+       err = SecKeyCreatePair(NULL, CSSM_ALGID_RSA, 2048, ((CSSM_CC_HANDLE)0),                                                    
+                                                  pubKeyUse, publicKeyAttributes, 
+                                                  privKeyUse, privateKeyAttributes,
+                                                  NULL, &publicKey, &privateKey);
+       
+       STAssertTrue(errSecSuccess == err, @"Unable to create a key pair");
+       if (errSecSuccess != err)
+       {
+               cssmPerror(NULL, err);
+               return;
+       }
+       
+       CFErrorRef error = NULL;
+       SecTransformRef encryptSymRef = SecEncryptTransformCreate(publicKey , &error);
+       if (NULL != error)
+       {
+               CFRelease(publicKey);
+               CFRelease(privateKey);
+               STAssertTrue(NO, [NSString stringWithFormat:@"Unable to create the encrypt transform for key RSA"]);
+               return;
+       }
+       
+       SecTransformRef decryptSymRef = SecDecryptTransformCreate(privateKey, &error);
+       if (NULL != error)
+       {
+               CFRelease(publicKey);
+               CFRelease(privateKey);
+               CFRelease(encryptSymRef);
+               STAssertTrue(NO, [NSString stringWithFormat:@"Unable to create the decrypt transform for key RSA"]);
+               return;
+       }
+       
+       // connect the output of the encryption to the input of the decryption transform
+       
+       SecGroupTransformRef group = SecTransformCreateGroupTransform();
+       (void)SecTransformConnectTransforms( encryptSymRef, kSecTransformOutputAttributeName,
+                                                       decryptSymRef, kSecTransformInputAttributeName, 
+                                                       group, &error);
+       if (NULL != error)
+       {
+               CFRelease(publicKey);
+               CFRelease(privateKey);
+               CFRelease(encryptSymRef);
+               CFRelease(decryptSymRef);
+               STAssertTrue(NO, [NSString stringWithFormat:@"Unable to connect transforms for key RSA"]);
+               return;
+       }
+       
+       NSInputStream* dataStream = [NSInputStream inputStreamWithData:testData];
+       [dataStream open];
+       
+       SecTransformSetAttribute(encryptSymRef, kSecTransformInputAttributeName, (CFTypeRef)dataStream, &error);
+       if (NULL != error)
+       {
+               CFRelease(publicKey);
+               CFRelease(privateKey);
+               CFRelease(encryptSymRef);
+               CFRelease(decryptSymRef);
+               STAssertTrue(NO, [NSString stringWithFormat:@"Unable to set the input for key RSA"]);
+               return; 
+       }
+       CFTypeRef transformResult = SecTransformExecute(encryptSymRef, &error);
+       if (NULL != error)
+       {
+               STAssertTrue(NO, [NSString stringWithFormat:@"returned an error for RSA"]);
+               CFRelease(error);
+               return;
+       }
+       
+       if (NULL == transformResult || 0 == [(NSData*)transformResult length])
+       {
+               STAssertTrue(NO, [NSString stringWithFormat:@"transformResult was NULL or empty for RSA"]);
+               return;
+       }
+       
+       NSData* resultData = nil;
+       if (CFGetTypeID(transformResult) == CFDataGetTypeID())
+       {
+               resultData = (NSData*)transformResult;          
+               [resultData autorelease];
+       }
+       
+       CFRelease(publicKey);
+       CFRelease(privateKey);
+       CFRelease(encryptSymRef);
+       CFRelease(decryptSymRef);
+               
+       STAssertTrue([testData isEqualToData:resultData], @"(RSA)The output of the decrypt transform does NOT match the original input!");      
+       
+       [pool drain];
+}
+
+// NOTE: this test is largely the same as testEncryptAndDecryptTransforms, but
+// we make a single key and use it from many threads at once.   This uncovered
+// some locking issues, so makes a good regression test.
+-(void)testMultiEncryptWithSameKey {
+       // generate a symmetrical key for testing
+       CSSM_KEYUSE keyUse = CSSM_KEYUSE_ANY;
+       CSSM_KEYATTR_FLAGS keyAttrFlags = CSSM_KEYATTR_RETURN_DEFAULT;
+       SecAccessRef accessRef = NULL;
+       CSSM_CC_HANDLE handle = ((CSSM_CC_HANDLE)0);
+       
+       NSString* dataStr = @"Reduce, reuse, recycle.   No crashes please.";
+       NSData* testData = [dataStr dataUsingEncoding:NSUTF8StringEncoding];
+
+       SecKeyRef testKey = NULL;
+       {
+               OSStatus err;
+               err = SecKeyGenerate(NULL, CSSM_ALGID_AES, 256, handle, keyUse, keyAttrFlags, accessRef, &testKey);
+               STAssertTrue(err == errSecSuccess, @"Unable to create a symmetrical key err=%x", err);
+       }
+       
+       // The number of iterations is somewhat arbitrary.   When we use to have failures they were
+       // within 2*#logicalCPUs iterations, but nothing says we won't have a regression that happens
+       // outside that window.
+       dispatch_apply(128, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t i) {
+               __block CFErrorRef error = NULL;
+               
+               SecTransformRef encryptSymRef = NULL;
+               encryptSymRef = SecEncryptTransformCreate(testKey, &error);
+               if (NULL != error)
+               {
+                       STFail(@"Unable to create the encrypt transform iteration#%d error=%@", i, error);
+                       return;
+               }
+               if (NULL == encryptSymRef) {
+                       STFail(@"Unable to create the encrypt transform iteration#%d, error=NULL", i);
+                       return;
+               }
+               
+               SecTransformRef decryptSymRef = SecDecryptTransformCreate(testKey, &error);
+               if (NULL != error)
+               {
+                       CFRelease(encryptSymRef);
+                       STFail(@"Unable to create the decrypt transform iteration#%d error=%@", i, error);
+                       return;
+               }
+               if (NULL == decryptSymRef) {
+                       CFRelease(encryptSymRef);
+                       STFail(@"Unable to create the decrypt transform iteration#%d, error=NULL", i);
+                       return;
+               }
+               
+               // connect the output of the encryption to the input of the decryption transform
+               
+               SecGroupTransformRef group = SecTransformCreateGroupTransform();
+               (void)SecTransformConnectTransforms(encryptSymRef, kSecTransformOutputAttributeName,
+                                                                                       decryptSymRef, kSecTransformInputAttributeName, 
+                                                                                       group, &error);
+               if (NULL != error)
+               {
+                       CFRelease(encryptSymRef);
+                       CFRelease(decryptSymRef);
+                       STFail(@"Unable to connect transforms on iteration %d error=%@", i, error);
+                       return;
+               }
+               
+               
+               NSInputStream* dataStream = [NSInputStream inputStreamWithData:testData];
+               [dataStream open];
+               
+               SecTransformSetAttribute(encryptSymRef, kSecTransformInputAttributeName, (CFTypeRef)dataStream, &error);
+               if (NULL != error)
+               {
+                       CFRelease(encryptSymRef);
+                       CFRelease(decryptSymRef);
+                       STFail(@"Unable to set the input on iteration %d error=%@", i, error);
+                       return;
+               }
+               
+               CFTypeRef transformResult = SecTransformExecute(encryptSymRef, &error);
+               CFRelease(group);
+               
+               if (NULL != error)
+               {
+                       STFail(@"returned an error on iteration %d error=%@", i, error);
+                       CFRelease(error);
+                       return;
+               }
+               
+               if (NULL == transformResult || 0 == [(NSData*)transformResult length])
+               {
+                       STFail(@"transformResult was NULL or empty for iteration %d", i);
+                       return;
+               }
+               
+               NSData* resultData = nil;
+               if (CFGetTypeID(transformResult) == CFDataGetTypeID())
+               {
+                       resultData = (NSData*)transformResult;                  
+                       [resultData autorelease];
+               }
+               
+               CFRelease(encryptSymRef);
+               CFRelease(decryptSymRef);
+               
+               STAssertEqualObjects(testData, resultData, @"The output of the decrypt transform does NOT match the original input iteration %d", i);           
+       });
+       
+       CFRelease(testKey);
+}
+                                                                 
+static SecTransformInstanceBlock RoundTripCheck(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               __block CFDataRef remainder = NULL;
+               __block SecTransformStringOrAttributeRef ahead = NULL;
+               __block int eof_count = 0;
+               __block bool drain = false;
+
+               SecTransformCustomSetAttribute(ref, CFSTR("INPUT2"), kSecTransformMetaAttributeDeferred, kCFBooleanTrue);
+               SecTransformCustomSetAttribute(ref, CFSTR("INPUT2"), kSecTransformMetaAttributeStream, kCFBooleanTrue);
+
+               dispatch_block_t not_equal = 
+               ^{
+                       // not equal
+                       SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, kCFBooleanFalse);
+                       SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, NULL);
+                       drain = true;
+               };
+
+               SecTransformAttributeActionBlock action = 
+               ^(SecTransformAttributeRef ah, CFTypeRef value) 
+               {
+            if (drain) 
+                       {
+                               return (CFTypeRef)NULL;
+                       } 
+
+                       if (ahead == ah) 
+                       {
+                               SecTransformPushbackAttribute(ref, ah, value);
+                       } 
+                       else if (value) 
+                       {
+                               CFDataRef d = (CFDataRef)value;
+                               if (remainder) 
+                               {
+                                       CFIndex compare_length;
+                                       CFIndex remainder_length = CFDataGetLength(remainder);
+                                       CFIndex d_length = CFDataGetLength(d);
+                                       CFDataRef new_remainder = NULL;
+                                       SecTransformAttributeRef new_ahead = NULL;
+
+                                       if (remainder_length == d_length) 
+                                       {
+                                               compare_length = d_length;
+                                       } 
+                                       else if (remainder_length < d_length) 
+                                       {
+                                               new_remainder = CFDataCreate(NULL, CFDataGetBytePtr(d) + remainder_length, d_length - remainder_length);
+                                               compare_length = remainder_length;
+                                               new_ahead = ah;
+                                       } else 
+                                       {
+                                               new_remainder = CFDataCreate(NULL, CFDataGetBytePtr(remainder) + d_length, remainder_length - d_length);
+                                               compare_length = d_length;
+                                               new_ahead = ahead;
+                                       }
+
+                                       if (bcmp(CFDataGetBytePtr(d), CFDataGetBytePtr(remainder), compare_length)) {
+                                               not_equal();
+                                       } else 
+                                       {
+                                               // same, keep going
+                                               CFRelease(remainder);
+                                               remainder = new_remainder;
+                                               ahead = new_ahead;
+                                       }
+                               } 
+                               else 
+                               {
+                                       if (!eof_count) 
+                                       {
+                                               ahead = ah;
+                                               remainder = CFDataCreateCopy(NULL, d);
+                                       } 
+                                       else 
+                                       {
+                                               if (CFDataGetLength(d)) 
+                                               {
+                                                       not_equal();
+                                               }
+                                       }
+                               }
+                       } 
+                       else 
+                       { // EOF case
+                               ahead = NULL;
+                               if (++eof_count == 2) 
+                               {
+                                       if (remainder) 
+                                       {
+                                               SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
+                                                               kSecTransformMetaAttributeValue, 
+                                                               CFDataGetLength(remainder) ? kCFBooleanFalse : kCFBooleanTrue);
+                                               
+                                               CFRelease(remainder);
+                                       } 
+                                       else 
+                                       {
+                                               SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
+                                                                       kSecTransformMetaAttributeValue, kCFBooleanTrue);
+                                       }
+                                       
+                                       SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, 
+                                                                       kSecTransformMetaAttributeValue, NULL);
+                               }
+                       }
+
+                       return (CFTypeRef)NULL;
+               };
+
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT2"), action);
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, action);
+
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);
+}
+
+static BOOL RoundTrip(CFStringRef fname, SecTransformRef in, SecTransformRef out, BOOL share)
+{
+       static dispatch_once_t once;
+       CFStringRef name = CFSTR("com.apple.examples.cmp");
+       // concepts: pushback, SecTransformSetAttributeAction vs. ProcessData, ah==, & send value to output
+
+       dispatch_once(&once,
+               ^{
+                       SecTransformRegister(name, &RoundTripCheck, NULL);
+               });
+       
+       SecTransformRef cmp = SecTransformCreate(name, NULL);
+       SecTransformRef group = SecTransformCreateGroupTransform();
+       CFErrorRef err = NULL;
+       SecTransformConnectTransforms(in, kSecTransformOutputAttributeName, out, kSecTransformInputAttributeName, group, NULL);
+       SecTransformConnectTransforms(out, kSecTransformOutputAttributeName, cmp, kSecTransformInputAttributeName, group, NULL);
+       NSInputStream *is = [NSInputStream inputStreamWithFileAtPath:(NSString *)fname];
+       // XXX: failure to do this seem to crash SecTransformExecute when it releases the error, track down & fix or file radar
+       [is open];      
+       
+       NSInputStream *is2 = nil;
+       
+       if (share) 
+       {
+               SecTransformRef tee = SecNullTransformCreate();
+               SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, in, kSecTransformInputAttributeName, group, NULL);
+               SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, cmp, CFSTR("INPUT2"), group, NULL);
+               SecTransformSetAttribute(tee, kSecTransformInputAttributeName, (CFTypeRef)is, NULL);
+               CFRelease(tee);
+       } else {
+               is2 = [NSInputStream inputStreamWithFileAtPath:(NSString *)fname];
+               [is2 open];     
+               SecTransformSetAttribute(in, kSecTransformInputAttributeName, (CFTypeRef)is, &err);
+               SecTransformSetAttribute(cmp, CFSTR("INPUT2"), (CFTypeRef)is2, &err);
+       }
+       
+       assert(err == NULL);
+       CFTypeRef r = SecTransformExecute(group, &err);
+       
+       if (err)
+       {
+               CFRelease(err);
+       }
+       
+       CFRelease(group);
+       CFRelease(cmp);
+       
+       if (is2)
+       {
+               [is2 close];
+       }
+       
+       if (is)
+       {
+               [is close];
+       }
+       
+       if (r) 
+       {
+               return r == kCFBooleanTrue;
+       } 
+       else 
+       {
+               CFfprintf(stderr, "round trip error: %@", err);
+               return NO;
+       }
+}
+
+static SecTransformInstanceBlock LineLengthCheck(CFStringRef name, SecTransformRef newTransform, SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = ^{
+               CFErrorRef result = NULL;
+               __block int bytesPastLastEOL = 0;
+               __block int max = 0;
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("MAX"), ^(SecTransformAttributeRef ah, CFTypeRef value) {
+                       CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &max);
+                       return value;
+               });
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, kSecTransformInputAttributeName, ^(SecTransformAttributeRef ah, CFTypeRef value) {
+                       if (NULL != value) {
+                               CFDataRef d = (CFDataRef)value;
+                               CFIndex len = CFDataGetLength(d);
+                               const UInt8 *bytes = CFDataGetBytePtr(d);
+                               
+                               for(int i = 0; i < len; i++) {
+                                       if (bytes[i] == '\n') {
+                                               bytesPastLastEOL = 0;
+                                       } else {
+                                               bytesPastLastEOL++;
+                                               if (bytesPastLastEOL > max) {
+                                                       SecTransformCustomSetAttribute(ref, kSecTransformAbortAttributeName, kSecTransformMetaAttributeValue, CreateSecTransformErrorRef(kSecTransformErrorInvalidInput, "MAX line length of %d exceeded", max));
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       SecTransformCustomSetAttribute(ref, kSecTransformOutputAttributeName, kSecTransformMetaAttributeValue, value);
+                       return value;
+               });
+
+               return result;
+       };
+
+       return Block_copy(instanceBlock);
+}
+                                                                 
+-(void)testLargeChunkEncode 
+{
+       NSError *err = NULL;
+       NSData *d = [NSData dataWithContentsOfFile:@"/usr/share/dict/web2a" options:NSDataReadingMapped error: &err];
+       STAssertNil(err, @"dataWithContentsOfFile %@", err);
+       CFStringRef types[] = {kSecZLibEncoding, kSecBase64Encoding, kSecBase32Encoding, NULL};
+       
+       dispatch_group_t dg = dispatch_group_create();
+       
+       CFStringRef lengthCheckName = CFSTR("com.apple.security.unit-test.lineLengthCheck");
+       SecTransformRegister(lengthCheckName, LineLengthCheck, (CFErrorRef *)&err);
+       STAssertNil(err, @"Expected to register %@", lengthCheckName);
+       
+       for(int i = 0; types[i]; i++) {
+               int max_j = 80;
+               CFStringRef etype = types[i];
+
+               void (^trial)(NSString *testName, id lineLength, int maxLineLength) = ^(NSString *testName, id lineLength, int maxLineLength) {
+                       SecGroupTransformRef group = SecTransformCreateGroupTransform();
+                       
+                       SecTransformRef et = SecEncodeTransformCreate(etype, (CFErrorRef *)&err);
+                       SecTransformRef dt = SecDecodeTransformCreate(etype, (CFErrorRef *)&err);
+
+                       SecTransformRef lineLengthChecker = (etype == kSecZLibEncoding) ? SecNullTransformCreate() : SecTransformCreate(lengthCheckName, NULL);
+                       STAssertNotNil((id)lineLengthChecker, @"Expected to create line length checker");
+                       SecTransformSetAttribute(lineLengthChecker, CFSTR("MAX"), [NSNumber numberWithInt:maxLineLength], NULL);
+                       
+                       SecTransformConnectTransforms(et, kSecTransformOutputAttributeName, lineLengthChecker, kSecTransformInputAttributeName, group, (CFErrorRef *)&err);
+                       SecTransformConnectTransforms(lineLengthChecker, kSecTransformOutputAttributeName, dt, kSecTransformInputAttributeName, group, (CFErrorRef *)&err);
+                       
+                       SecTransformSetAttribute(et, kSecTransformInputAttributeName, (CFDataRef)d, (CFErrorRef *)&err);
+                       SecTransformSetAttribute(et, kSecEncodeLineLengthAttribute, lineLength, (CFErrorRef *)&err);
+                       SecTransformSetAttribute(et, CFSTR("NAME"), (CFStringRef)testName, (CFErrorRef *)&err);
+                       
+                       dispatch_group_async(dg, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                               CFDataRef result = (CFDataRef)SecTransformExecute(group, (CFErrorRef *)&err);
+                               
+                               STAssertNil(err, @"execute for %@ got %@", testName, err);
+                               STAssertNotNil((id)result, @"No result from execute of %@", testName);
+                               
+                               if (result) {
+                                       STAssertEqualObjects(d, (id)result, @"test %@ failed", testName);
+                               }
+                       });
+               };
+
+               for(int j = max_j; j > 70; --j) {
+                       if (etype == kSecZLibEncoding && j != max_j) {
+                               break;
+                       }
+                       trial([NSString stringWithFormat:@"%@-%d", etype, j], [NSNumber numberWithInt:j], j);
+               }
+               
+               if (etype != kSecZLibEncoding) {
+                       trial([NSString stringWithFormat:@"%@-LL64", etype], (id)kSecLineLength64, 64);
+                       trial([NSString stringWithFormat:@"%@-LL76", etype], (id)kSecLineLength76, 76);
+               }
+       }
+       
+       dispatch_group_wait(dg, DISPATCH_TIME_FOREVER);
+}
+
+-(void)testZLib {
+       SecTransformRef et = SecEncodeTransformCreate(kSecZLibEncoding, NULL);
+       SecTransformRef dt = SecDecodeTransformCreate(kSecZLibEncoding, NULL);
+       
+       // using a tee would require >10 buffered items (we need to buffer about 64K), so we pass share=NO
+       STAssertTrue(RoundTrip((CFStringRef)@"/usr/share/dict/web2a", et, dt, NO), @"Roundtrip /usr/share/dict/web2a");
+       
+       CFRelease(et);
+       CFRelease(dt);
+       
+       /*
+       If we want this we need a 'new' custom transform that will get receive the ratio data and be able to 
+       query that data.
+       
+       CFNumberRef r1 = (CFNumberRef)SecTransformGetAttribute(et, kSecCompressionRatio);
+       CFNumberRef r2 = (CFNumberRef)SecTransformGetAttribute(dt, kSecCompressionRatio);
+       
+       STAssertNotNil((NSNumber *)r1, @"encode ratio");
+       STAssertNotNil((NSNumber *)r2, @"decode ratio");
+       STAssertEqualObjects((NSNumber *)r1, (NSNumber *)r2, @"same ratios");
+       */
+}
+
+static SecTransformInstanceBlock CycleCheckTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               int zero = 0;
+               __block CFNumberRef feedback = CFNumberCreate(NULL, kCFNumberIntType, &zero);
+               
+               SecTransformCustomSetAttribute(ref, CFSTR("FEEDBACK"), kSecTransformMetaAttributeCanCycle, kCFBooleanTrue);
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("INPUT"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (value == NULL) {
+                                       SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, value);
+                                       return value;
+                               }
+                               if (feedback == NULL) {
+                                       SecTransformPushbackAttribute(ref, ah, value);
+                                       return (CFTypeRef)NULL;
+                               }
+                       
+                               int x, y;
+                               CFNumberGetValue((CFNumberRef)value, kCFNumberIntType, &x);
+                               CFNumberGetValue(feedback, kCFNumberIntType, &y);
+                               x ^= y;
+                               CFNumberRef res = CFNumberCreate(NULL, kCFNumberIntType, &x);
+                               SecTransformCustomSetAttribute(ref, CFSTR("OUTPUT"), kSecTransformMetaAttributeValue, res);
+                               CFRelease(res);
+                               CFRelease(feedback);
+                               feedback = NULL;
+                               return (CFTypeRef)NULL;
+                       });
+               
+               SecTransformSetAttributeAction(ref, kSecTransformActionAttributeNotification, CFSTR("FEEDBACK"), 
+                       ^(SecTransformAttributeRef ah, CFTypeRef value) 
+                       {
+                               if (value) {
+                                       if (feedback) {
+                                               SecTransformPushbackAttribute(ref, ah, value);
+                                       } else {
+                                               feedback = (CFNumberRef)CFRetain(value);
+                                       }
+                               }
+                       
+                               return (CFTypeRef)NULL;
+                       });
+       
+               return result;
+
+       };
+       
+       return Block_copy(instanceBlock);
+}
+
+-(void)testCycleCheck {
+
+       SecTransformRef cat = SecNullTransformCreate();
+       SecTransformRef group = SecTransformCreateGroupTransform();
+       CFErrorRef err = NULL;
+       
+       CFStringRef name = CFSTR("com.apple.examples.unit-test.loop-test");
+       
+       SecTransformRegister(name, &CycleCheckTest, NULL);
+       
+       SecTransformRef twenty = count_transform(20);
+       
+       // this is getting an internal error, but it's being ignored.
+       SecTransformRef xxor = SecTransformCreate(name, &err);
+       
+       SecTransformConnectTransforms(xxor, CFSTR("OUTPUT"), cat, CFSTR("INPUT"), group, &err);
+       STAssertNil((id)err, @"xor->cat");
+       SecTransformConnectTransforms(xxor, CFSTR("OUTPUT"), xxor, CFSTR("FEEDBACK"), group, &err);
+       STAssertNil((id)err, @"xor->xor");
+       SecTransformConnectTransforms(twenty, CFSTR("OUTPUT"), xxor, CFSTR("INPUT"), group, &err);
+       STAssertNil((id)err, @"twenty->xor");
+       
+       //SecTransformSetAttribute(xxor, CFSTR("DEBUG"), kCFBooleanTrue, &err);
+
+       CFTypeRef r = SecTransformExecute(group, &err);
+       STAssertNil((id)err, @"execute err=%@", err);
+       STAssertNotNil((id)r, @"no results from execute");
+
+       if (r) {
+               CFNumberRef z = (CFNumberRef)[NSNumber numberWithInt:0];
+               CFIndex n = CFArrayGetCountOfValue((CFArrayRef)r, CFRangeMake(0, CFArrayGetCount((CFArrayRef)r)), z);
+               // There should be six zeros in the xor->feedback chain from 0 to 19.
+               STAssertEquals(n, (CFIndex) 6, @"There should be six zeros in %@", r);
+       }
+
+       CFRelease(r);
+       CFRelease(group);
+       CFRelease(twenty);
+       CFRelease(xxor);
+       CFRelease(cat);
+}
+
+-(void)testValidate {
+       SecTransformRef group = SecTransformCreateGroupTransform();
+       CFErrorRef err = NULL;
+       
+       CFStringRef data_or_null_name = CFSTR("com.apple.examples.unit-test.data-or-null");
+       SecTransformCreateBlock data_or_null = ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params) {
+               params->overrideAttribute(kSecTransformActionAttributeValidation, CFSTR("INPUT"), SecTransformCreateValidatorForCFtype(CFDataGetTypeID(), YES));
+       };
+       
+       
+       SecTransformRef makes_numbers = count_transform(20);
+       SecTransformRef wants_data = custom_transform(data_or_null_name, data_or_null);
+       
+       SecTransformConnectTransforms(makes_numbers, CFSTR("OUTPUT"), wants_data, CFSTR("INPUT"), group, NULL);
+       STAssertNil((id)err, @"unexpected connect error: %@", err);
+       CFTypeRef r = SecTransformExecute(group, &err);
+       STAssertNil((id)r, @"Got non-null result (%@) when expecting null!", r);
+       STAssertNotNil((id)err, @"Expected an error!", err);
+       STAssertErrorHas((id)err, @"/INPUT", @"Error indicated attribute that was set incorrectly");
+       STAssertErrorHas((id)err, @" type CFNumber", @"Error indicated provided type");
+       STAssertErrorHas((id)err, @" a CFData", @"Error indicated required type");
+       
+       if (err) {
+               CFRelease(err);
+       }
+       err = NULL;
+       CFRelease(wants_data);
+       
+       wants_data = custom_transform(data_or_null_name, data_or_null);
+       
+       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.";
+       CFDataRef the_data = CFDataCreate(NULL, (UInt8*)raw_data, strlen(raw_data));
+       SecTransformSetAttribute(wants_data, kSecTransformInputAttributeName, the_data, &err);
+       CFRelease(the_data);
+       
+       STAssertNil((id)err, @"unexpected set error: %@", err);
+       r = SecTransformExecute(wants_data, &err);
+       STAssertNotNil((id)r, @"Expected a result, got error: %@", err);
+       if (r) {
+               STAssertEqualObjects((id)the_data, (id)r, @"Invalid result");
+       }
+       
+       CFStringRef numbers_only_name = CFSTR("com.apple.examples.unit-test.numbers-only");
+       SecTransformCreateBlock numbers_only = ^(CFStringRef name, SecTransformRef new_transform, const SecTransformCreateBlockParameters *params) {
+               params->overrideAttribute(kSecTransformActionAttributeValidation, CFSTR("INPUT"), SecTransformCreateValidatorForCFtype(CFNumberGetTypeID(), NO));
+       };
+       
+       CFRelease(group);
+       CFRelease(makes_numbers);
+       CFRelease(wants_data);
+       
+       group = SecTransformCreateGroupTransform();
+       makes_numbers = count_transform(20);
+       SecTransformRef wants_numbers = custom_transform(numbers_only_name, numbers_only);
+       
+       SecTransformConnectTransforms(makes_numbers, CFSTR("OUTPUT"), wants_numbers, CFSTR("INPUT"), group, NULL);
+       STAssertNil((id)err, @"unexpected connect error: %@", err);
+       r = SecTransformExecute(group, &err);
+       CFfprintf(stderr, "r=%@; err=%@\n", r, err);
+       STAssertNil((id)r, @"Got non-null result (%@) when expecting null!", r);
+       STAssertNotNil((id)err, @"Expected an error!", err);
+       STAssertErrorHas((id)err, @"/INPUT", @"Error indicated attribute that was set incorrectly");
+       STAssertErrorHas((id)err, @"received NULL value", @"Error indicated provided value is NULL");
+       STAssertErrorHas((id)err, @" a CFNumber", @"Error indicated required type");
+       
+       CFRelease(err);
+       CFRelease(group);
+       CFRelease(makes_numbers);
+       CFRelease(wants_numbers);
+}
+
+-(void)testCodeBase32 {
+       struct base32_test_vector {
+               const char *plain_text;
+               const char *base32_rfc4648;
+               const char *base32_fde;
+       };
+       
+       // RFC 4648 test vectors
+       static base32_test_vector base32_test_vectors[] = {
+               {"", "", ""},
+               {"f", "MY======", "MY======"},
+               {"fo", "MZXQ====", "MZXQ===="},
+               {"foo", "MZXW6===", "MZXW6==="},
+               {"foob", "MZXW6YQ=", "MZXW6YQ="},
+               {"fooba", "MZXW6YTB", "MZXW6YTB"},
+               {"foobar", "MZXW6YTBOI======", "MZXW6YTBO8======"}};
+       
+       void (^test)(NSString *test_name, SecTransformRef transform, const char *input, const char *expected_output, NSString *error_format) =
+       ^(NSString *test_name, SecTransformRef transform, const char *input, const char *expected_output, NSString *error_format)
+       {
+               if (!transform) {
+                       STFail(@"No transform for %@", test_name);
+                       return;
+               }
+               
+               CFErrorRef err = NULL;
+               NSData *input_data = [NSData dataWithBytes:input length:strlen(input)];
+               NSData *expected_output_data = [NSData dataWithBytes:expected_output length:strlen(expected_output)];
+               SecTransformSetAttribute(transform, kSecTransformInputAttributeName, input_data, &err);
+               STAssertNil((NSError *)err, @"unexpected error %@ from SecTransformSetAttribute for %@", err, test_name);
+               NSData *output_data = (NSData *)SecTransformExecute(transform, &err);
+               [output_data autorelease];
+               STAssertNil((NSError *)err, @"Error from %@ execute (in=%s, err=%s)", test_name, input, err);
+               STAssertNotNil(output_data, @"Unexpected nil output from %@ execute (in=%s)", test_name, input);
+               if (output_data) {
+                       NSString *output_string = [NSString alloc];
+                       output_string = [output_string initWithBytes:[output_data bytes] length:[output_data length] encoding:NSMacOSRomanStringEncoding];
+                       [output_string autorelease];
+                       NSString *msg = [NSString stringWithFormat:error_format, input, expected_output, output_string];
+                       STAssertEqualObjects(expected_output_data, output_data, @"%@ %@", test_name, msg);                                      
+               }
+               CFRelease(transform);
+       };
+       
+       for(int idx = 0; idx < sizeof(base32_test_vectors)/sizeof(*base32_test_vectors); idx++)
+       {
+               SecTransformRef base32encoder = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
+               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$@\"");
+               
+               SecTransformRef base32decoder = SecDecodeTransformCreate(kSecBase32Encoding, NULL);
+               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$@\"");
+               
+               SecTransformRef base32FDEencoder = SecEncodeTransformCreate(CFSTR("base32FDE"), NULL);
+               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$@\"");
+               
+               SecTransformRef base32FDEdecoder = SecDecodeTransformCreate(CFSTR("base32FDE"), NULL);
+               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$@\"");
+       }
+       
+       SecTransformRef bet = SecEncodeTransformCreate(kSecBase32Encoding, NULL);
+       STAssertNotNil((id)bet, @"got bulk base 32 encoder");
+       SecTransformRef bdt = SecDecodeTransformCreate(kSecBase32Encoding, NULL);
+       STAssertNotNil((id)bdt, @"got bulk base 32 decoder");
+       STAssertTrue(RoundTrip((CFStringRef)@"/usr/share/dict/words", bet, bdt, YES), @"Roundtrip base32 /usr/share/dict/words");
+       
+       CFRelease(bet);
+       CFRelease(bdt);
+       
+       // FDE uses a modified base32 alphabet, we want to test it here.
+       SecTransformRef FDE_encode_transform = SecEncodeTransformCreate(@"base32FDE", NULL);
+       STAssertNotNil((id)FDE_encode_transform, @"got FDE encoder");
+       SecTransformRef FDE_decode_transform = SecDecodeTransformCreate(@"base32FDE", NULL);
+       STAssertNotNil((id)FDE_decode_transform, @"got bulk base 32 decoder");
+       STAssertTrue(RoundTrip((CFStringRef)@"/usr/share/dict/words", FDE_encode_transform, FDE_decode_transform, YES), @"Roundtrip base32FDE /usr/share/dict/words");
+       
+       CFRelease(FDE_encode_transform);
+       CFRelease(FDE_decode_transform);
+}
+
+-(void)testCodeBase64 {
+       CFErrorRef error = NULL;
+       
+#if 0
+       SecTransformRef tr = SecDecodeTransformCreate(@"Not a real encoding", &error);
+       // XXX: known failure in Transform::SetAttribute 7707822 -- I would fix on this branch, but I think that code has diverged
+       STAssertTrue(tr == NULL, @"Checks for invalid encodings");
+       NSLog(@"Error: %@", error);
+#endif
+       
+       SecTransformRef dt = SecDecodeTransformCreate(kSecBase64Encoding, NULL);
+       STAssertNotNil((id)dt, @"Got decoder");
+       
+       const char raw_data0[] = "Tm90IHV1ZW5jb2RlZAo=";
+       const char raw_data1[] = "Not uuencoded\n";
+       CFDataRef data0 = CFDataCreate(NULL, (const UInt8*)raw_data0, strlen(raw_data0));
+       CFDataRef data1 = CFDataCreate(NULL, (const UInt8*)raw_data1, strlen(raw_data1));
+    SecTransformSetAttribute(dt, kSecTransformInputAttributeName, data0, NULL);
+
+    CFDataRef decoded_data = (CFDataRef)SecTransformExecute(dt, &error);
+    STAssertNotNil((NSData *)decoded_data, @"Got a decode result");
+    STAssertEqualObjects((NSData *)decoded_data, (NSData *)data1, @"Proper decode results");
+
+       SecTransformRef et = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
+       STAssertNotNil((id)et, @"Got encoder");
+       
+       SecTransformSetAttribute(et, kSecTransformInputAttributeName, data1, NULL);
+
+       CFDataRef encoded_data = (CFDataRef)SecTransformExecute(et, NULL);
+       STAssertNotNil((NSData *)encoded_data, @"Got an encode result");
+               
+       STAssertEqualObjects((NSData *)encoded_data, (NSData *)data0, @"Proper encode results");
+
+    // Negative testing, assume the following struct
+    //   struct __CFData {
+    //        CFRuntimeBase _base;
+    //        CFIndex _length;    /* number of bytes */
+    //        CFIndex _capacity;    /* maximum number of bytes */
+    //        CFAllocatorRef _bytesDeallocator;    /* used only for immutable; if NULL, no deallocation */
+    //        uint8_t *_bytes;    /* compaction: direct access to _bytes is only valid when data is not inline */
+    //        };
+    SecTransformRef et_neg = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
+    STAssertNotNil((id)et_neg, @"Got encoder for negative testing");
+
+    CFIndex *data1_length=(CFIndex*)((unsigned char *)data1 + sizeof(CFRuntimeBase));
+    CFIndex data1_backup=*data1_length;
+    if (sizeof(CFIndex)==8)
+    {
+        *data1_length=0x5ffffffffffffff7;
+    }
+    else if (sizeof(CFIndex)==4)
+    {
+        *data1_length=0x5ffffff7;
+    }
+    else
+    {
+        STAssertTrue(false, @"Test error, representation of CFIndex not supported. Sizeof(CFIndex)=%d. Only support 4 or 8",sizeof(CFIndex));
+    }
+    STAssertTrue(CFDataGetLength(data1)==*data1_length, @"Length not properly set - test bug - reads %lu expect %lu",CFDataGetLength(data1),*data1_length);
+    SecTransformSetAttribute(et_neg, kSecTransformInputAttributeName, data1, NULL);
+    CFDataRef encoded_data2 = (CFDataRef)SecTransformExecute(et_neg, &error);
+    STAssertNil((id)encoded_data2, @"No encoded data for negative testing");
+    STAssertNotNil((id)error, @"Got error for negative testing");
+    *data1_length=data1_backup;
+    if (error!=NULL)
+    {
+        STAssertTrue((CFErrorGetCode(error)==kSecTransformErrorInvalidLength),
+                 @"Error for invalid length, got %lu expect %lu",CFErrorGetCode(error),kSecTransformErrorInvalidLength);
+    }
+       // XXX also for general testing we want a "RandomChunkSizer" that copies INPUT to OUTPUT, but makes random size chunks (incl 0) as it goes.
+       
+       SecTransformRef dt2 = SecDecodeTransformCreate(kSecBase64Encoding, NULL);
+       SecTransformRef et2 = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
+       int ll = 75;
+       SecTransformSetAttribute(et2, kSecEncodeLineLengthAttribute, CFNumberCreate(NULL, kCFNumberIntType, &ll), NULL);
+
+       STAssertTrue(RoundTrip((CFStringRef)@"/usr/share/dict/words", et2, dt2, YES), @"Roundtrip base64 /usr/share/dict/words");
+       
+       CFRelease(et2);
+       CFRelease(dt2);
+    CFRelease(et);
+    CFRelease(et_neg);
+    CFRelease(dt);
+}
+
+static SecTransformInstanceBlock ErrorResultsTest(CFStringRef name, 
+                                                       SecTransformRef newTransform, 
+                                                       SecTransformImplementationRef ref)
+{
+       SecTransformInstanceBlock instanceBlock = 
+       ^{
+               CFErrorRef result = NULL;
+               SecTransformSetDataAction(ref, kSecTransformActionProcessData, 
+               ^(CFTypeRef value) 
+               {
+                       if (value != NULL)
+                       {
+                               return (CFTypeRef)CFErrorCreate(NULL, CFSTR("expected error"), 42, NULL);
+                       }
+                       else
+                       {
+                               return SecTransformNoData();
+                       }
+               });
+
+               return result;
+       };
+       
+       return Block_copy(instanceBlock);       
+}
+
+
+-(void)testErrorResults {
+       CFStringRef name = CFSTR("com.apple.security.unit-test.error-results");
+       SecTransformRegister(name, &ErrorResultsTest, NULL);
+       
+       SecTransformRef tr = SecTransformCreate(name, NULL);
+       CFDataRef data = CFDataCreate(NULL, NULL, 0);
+       SecTransformSetAttribute(tr, kSecTransformInputAttributeName, data, NULL);
+       
+       CFErrorRef err = NULL;
+       CFTypeRef no_result = SecTransformExecute(tr, &err);
+       
+       STAssertErrorHas((id)err, @"expected error", @"Signaled error has original string");
+       STAssertErrorHas((id)err, @"42", @"Signaled error has original error code");
+       STAssertNil((id)no_result, @"No result from erroring transform");
+       CFRelease(data);
+       CFRelease(tr);
+       CFRelease(err);
+}
+
+-(void)testErrorExecutesInRightQueue {
+       // testExecuteBlock checks to see if blocks are generally executed on the proper queue, this specifically checks
+       // for an error while starting (which was originally improperly coded).
+       
+       SecTransformRef unassigned_input = SecNullTransformCreate();
+       dispatch_queue_t q = dispatch_queue_create("com.apple.unit-test.ErrorExecutesInRightQueue", NULL);
+       dispatch_group_t got_final = dispatch_group_create();
+       dispatch_group_enter(got_final);
+       __block bool saw_data = false;
+       __block bool saw_error = false;
+       
+       SecTransformExecuteAsync(unassigned_input, q, ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) {
+               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());
+               saw_data = saw_data || (message != NULL);
+               saw_error = saw_error || (error != NULL);
+               if (isFinal) {
+                       dispatch_group_leave(got_final);
+               }
+       });
+       
+       STAssertFalse(dispatch_group_wait(got_final, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"Execute completed");
+       STAssertFalse(saw_data, @"Should have seen no data (but did)");
+       STAssertTrue(saw_error, @"Should have seen error (but didn't)");
+       
+       CFRelease(unassigned_input);
+       dispatch_group_notify(got_final, q, ^{
+               dispatch_release(got_final);
+               dispatch_release(q);
+       });
+       
+}
+
+-(void)testSignVerify {
+       unsigned char *raw_message = (unsigned char *)"Controlling complexity is the essence of computer programming. - Brian Kernigan";
+       dispatch_group_t dg = dispatch_group_create();
+       CFErrorRef err = NULL;
+       CFDataRef message = CFDataCreate(NULL, raw_message, strlen((const char *)raw_message));
+       __block SecKeyRef rsa_pub_key = NULL;
+       __block SecKeyRef rsa_priv_key = NULL;
+       __block SecKeyRef ecdsa_pub_key = NULL;
+       __block SecKeyRef ecdsa_priv_key = NULL;
+       __block SecKeyRef dsa_pub_key = NULL;
+       __block SecKeyRef dsa_priv_key = NULL;
+       
+       char *tmp_dir;
+       asprintf(&tmp_dir, "%s/sign-verify-test-keychain-", getenv("TMPDIR") ? getenv("TMPDIR") : "/tmp");
+       
+       unsigned char *raw_bad_message = (unsigned char *)"Standards are great, there are so many to choose from - Andrew S. Tanenbaum (maybe)";
+       CFDataRef bad_message = CFDataCreate(NULL, raw_bad_message, strlen((const char *)raw_bad_message));
+
+       // when safe replace with a concurrent queue
+       dispatch_queue_t key_q = dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+       
+       dispatch_group_async(dg, key_q, 
+       ^{
+               NSAutoreleasePool *pool = [NSAutoreleasePool new];
+                               
+               // (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)
+               NSDictionary *key_opts = [NSDictionary dictionaryWithObjectsAndKeys:
+                                                                 (NSString *)kSecAttrKeyTypeRSA, (NSString *)kSecAttrKeyType,
+                                                                 [NSNumber numberWithInt:1024], (NSString *)kSecAttrKeySizeInBits,
+                                                                 @"RSA transform unit test key", (NSString *)kSecAttrLabel,
+                                                                 nil];
+               OSStatus gp_status = SecKeyGeneratePair((CFDictionaryRef)key_opts, &rsa_pub_key, &rsa_priv_key);
+               STAssertTrue(gp_status == 0, @"RSA (gp_status=0x%x)", gp_status);
+               [pool drain];
+       });
+
+       dispatch_group_async(dg, key_q, 
+       ^{
+               OSStatus gp_status;
+#if 0
+               // 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
+               NSDictionary *key_opts = [NSDictionary dictionaryWithObjectsAndKeys:
+                                                                 (NSString *)kSecAttrKeyTypeECDSA, (NSString *)kSecAttrKeyType,
+                                                                 [NSNumber numberWithInt:512], (NSString *)kSecAttrKeySizeInBits,
+                                                                 @"ECDSA transform unit test key", (NSString *)kSecAttrLabel,
+                                                                 nil];
+               gp_status = SecKeyGeneratePair((CFDictionaryRef)key_opts, &ecdsa_pub_key, &ecdsa_priv_key);
+#else
+               {
+                       SecKeychainRef tmp_keychain = NULL;
+                       gp_status = SecKeyCreatePair(tmp_keychain, CSSM_ALGID_ECDSA, 256, NULL, 
+                               CSSM_KEYUSE_VERIFY, 
+                               CSSM_KEYATTR_EXTRACTABLE|CSSM_KEYATTR_PERMANENT, 
+                               CSSM_KEYUSE_SIGN, 
+                               CSSM_KEYATTR_EXTRACTABLE|CSSM_KEYATTR_PERMANENT, 
+                               NULL, &ecdsa_pub_key, &ecdsa_priv_key);
+               }
+#endif
+               if (gp_status) 
+               {
+                       STAssertTrue(gp_status == 0, @"ECDSA (gp_status=0x%x)", gp_status);
+               }
+       });
+
+       dispatch_group_async(dg, key_q, 
+       ^{
+               OSStatus gp_status;
+#if 0
+               // 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
+               NSDictionary *key_opts = [NSDictionary dictionaryWithObjectsAndKeys:(NSString *)kSecAttrKeyTypeDSA, 
+                                       (NSString *)kSecAttrKeyType, [NSNumber numberWithInt:512], (NSString *)kSecAttrKeySizeInBits, nil];
+               gp_status = SecKeyGeneratePair((CFDictionaryRef)key_opts, &ecdsa_pub_key, &ecdsa_priv_key);
+#else
+               {
+                       const char *passwd = "this is not secret";
+                       SecKeychainRef tmp_keychain = NULL;
+                       char *kcfname;
+                       asprintf(&kcfname, "%s-DSA-XXXXXXXXXX", tmp_dir);
+                       // NOTE: "mktemp" isn't as safe as you might think...but this is test code and doesn't have to be, but
+                       // if you copy it elsewhere you may well need to rewrite it.  (use mkstemp)
+                       mktemp(kcfname);                        
+                       gp_status = SecKeychainCreate(kcfname, (UInt32) strlen(passwd), passwd, NO, NULL, &tmp_keychain);
+                       STAssertTrue(gp_status == 0, @"SecKeychainCreate (gp_status=0x%x)", gp_status);
+                       gp_status = SecKeyCreatePair(tmp_keychain, CSSM_ALGID_DSA, 512, NULL, 
+                               CSSM_KEYUSE_VERIFY|CSSM_KEYUSE_ENCRYPT|CSSM_KEYUSE_WRAP, 
+                               CSSM_KEYATTR_EXTRACTABLE|CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_RETURN_REF, 
+                               CSSM_KEYUSE_SIGN|CSSM_KEYUSE_DECRYPT|CSSM_KEYUSE_UNWRAP, 
+                               CSSM_KEYATTR_EXTRACTABLE|CSSM_KEYATTR_PERMANENT|CSSM_KEYATTR_RETURN_REF, 
+                               NULL, &dsa_pub_key, &dsa_priv_key);
+                       free(kcfname);
+               }
+#endif
+               STAssertTrue(gp_status == 0, @"DSA (gp_status=0x%x)", gp_status);
+       });
+       
+       struct sv_test {
+               NSString *name;
+               SecKeyRef pub_key, priv_key;
+               CFDataRef msg_sign, msg_verify;
+               CFTypeRef dalgo_sign, dalgo_verify;
+               int dlen_sign, dlen_verify;
+               BOOL pass;
+       };
+       
+       dispatch_group_wait(dg, DISPATCH_TIME_FOREVER);
+
+       struct sv_test sv_tests[] = 
+       {
+               {@"Basic RSA", rsa_pub_key, rsa_priv_key, message, message, NULL, NULL, 0, 0, YES},
+               {@"Basic RSA (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, NULL, NULL, 0, 0, NO},
+               {@"RSA, mismatched digest algos", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA1, 0, 0, NO},
+               
+               {@"RSA SHA1 MD5", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, YES},
+               {@"RSA SHA1 MD5 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, NO},
+               
+               {@"RSA MD5", rsa_pub_key, rsa_priv_key, message, message, kSecDigestMD5, kSecDigestMD5, 0, 0, YES},
+               {@"RSA MD5 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestMD5, kSecDigestMD5, 0, 0, NO},
+               
+               {@"RSA MD2", rsa_pub_key, rsa_priv_key, message, message, kSecDigestMD2, kSecDigestMD2, 0, 0, YES},
+               {@"RSA MD2 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestMD2, kSecDigestMD2, 0, 0, NO},
+               
+               {@"RSA SHA2 512", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 512, 512, YES},
+               {@"RSA SHA2 512 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 512, 512, NO},
+               {@"RSA SHA2 512 vs. 384", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 512, 384, NO},
+               
+               {@"RSA SHA2 384", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 384, 384, YES},
+               {@"RSA SHA2 384 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 384, 384, NO},
+               
+               {@"RSA SHA2 256", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 256, 256, YES},
+               {@"RSA SHA2 256 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 256, 256, NO},
+               
+               {@"RSA SHA2 224", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 224, 224, YES},
+               {@"RSA SHA2 224 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 224, 224, NO},
+               
+               {@"RSA SHA2 0", rsa_pub_key, rsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 0, 0, YES},
+               {@"RSA SHA2 0 (tampered data)", rsa_pub_key, rsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 0, 0, NO},
+               
+               {@"Basic ECDSA", ecdsa_pub_key, ecdsa_priv_key, message, message, NULL, NULL, 0, 0, YES},
+               {@"Basic ECDSA (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, NULL, NULL, 0, 0, NO},
+               {@"ECDSA (mismatched digest algos)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA1, 0, 0, NO},
+               
+               {@"ECDSA SHA1", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, YES},
+               {@"ECDSA SHA1 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, NO},
+               
+               {@"ECDSA SHA2 224", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 224, 224, YES},
+               {@"ECDSA SHA2 224 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 224, 224, NO},
+               
+               {@"ECDSA SHA2 256", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 256, 256, YES},
+               {@"ECDSA SHA2 256 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 256, 256, NO},
+               
+               {@"ECDSA SHA2 384", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 384, 384, YES},
+               {@"ECDSA SHA2 384 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 384, 384, NO},
+               
+               {@"ECDSA SHA2 512", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 512, 512, YES},
+               {@"ECDSA SHA2 512 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 512, 512, NO},
+               
+               {@"ECDSA SHA2 0", ecdsa_pub_key, ecdsa_priv_key, message, message, kSecDigestSHA2, kSecDigestSHA2, 0, 0, YES},
+               {@"ECDSA SHA2 0 (tampered data)", ecdsa_pub_key, ecdsa_priv_key, message, bad_message, kSecDigestSHA2, kSecDigestSHA2, 0, 0, NO},
+               
+               {@"Basic DSA", dsa_pub_key, dsa_priv_key, message, message, NULL, NULL, 0, 0, YES},
+               {@"Basic DSA (tampered data)", dsa_pub_key, dsa_priv_key, message, bad_message, NULL, NULL, 0, 0, NO},
+               // only SHA1 is supported, so no mismatched digest algo test is available
+               
+               {@"DSA SHA1", dsa_pub_key, dsa_priv_key, message, message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, YES},
+               {@"DSA SHA1 (tampered data)", dsa_pub_key, dsa_priv_key, message, bad_message, kSecDigestSHA1, kSecDigestSHA1, 0, 0, NO},
+       };
+
+       free(tmp_dir);
+
+       int i;
+       for(i = 0; i < sizeof(sv_tests)/sizeof(sv_test); i++) 
+       {
+               CFStringRef input_cases[] = {kSecInputIsPlainText, kSecInputIsDigest};
+               //CFStringRef input_cases[] = {kSecInputIsPlainText, kSecInputIsDigest, kSecInputIsRaw};
+               const int ilim = sizeof(input_cases)/sizeof(input_cases[0]);
+               int ii = 0, ij = 0;
+               for(; ii < ilim; ++ii) 
+               {
+                       for(ij = 0; ij < ilim; ++ij) 
+                       {
+                               err = NULL;
+                               struct sv_test *tst = sv_tests + i;
+                               NSString *tname = [NSString stringWithFormat:@"%@ %@ %@", tst->name, input_cases[ii], input_cases[ij]];
+                               
+                               CFStringRef sign_input_is = input_cases[ii];
+                               CFStringRef verify_input_is = input_cases[ij];
+                               
+                               if (sign_input_is != kSecInputIsPlainText && tst->dalgo_sign == NULL) {
+                                       continue;
+                               }
+                               if (verify_input_is != kSecInputIsPlainText && tst->dalgo_verify == NULL) {
+                                       continue;
+                               }
+                               
+                               if ((sign_input_is == kSecInputIsRaw || verify_input_is == kSecInputIsRaw) && [tst->name rangeOfString:@"RSA"].location == NSNotFound) {
+                                       // we can only synthesize these tests for RSA
+                                       NSLog(@"No %@ test", tname);
+                                       continue;
+                               }
+                               
+                               STAssertNotNil((id)tst->pub_key, @"Have pub_key for %@", tname);
+                               STAssertNotNil((id)tst->priv_key, @"Have priv_key for %@", tname);
+                               
+                               if (tst->pub_key == nil || tst->priv_key == nil) {
+                                       continue;
+                               }
+                               
+                               SecTransformRef sign = SecSignTransformCreate(tst->priv_key, &err);
+                               STAssertNil((NSError *)err, @"creating sign for %@", tname);
+                               STAssertNotNil((id)sign, @"Creating sign for %@", tname);
+                               
+                               if (sign == NULL) {
+                                       continue;
+                               }
+                               
+                               SecTransformRef verify = SecVerifyTransformCreate(tst->pub_key, NULL, &err);
+                               STAssertNotNil((id)verify, @"Creating verify for %@", tname);
+                               STAssertNil((NSError *)err, @"Creating verify for %@", tname);
+                               
+                               if (verify == NULL) {
+                                       continue;
+                               }
+                               
+                               SecTransformRef sign_digest = NULL;
+                               SecTransformRef verify_digest = NULL;
+                               SecTransformRef sign2 = NULL;
+                               
+                               if (tst->dalgo_sign) 
+                               {
+                                       SecTransformSetAttribute(sign, kSecDigestTypeAttribute, tst->dalgo_sign, &err);
+                                       STAssertNil((NSError *)err, @"Setting sign's digest type for %@", tname);
+                                       SecTransformSetAttribute(sign, kSecDigestLengthAttribute, [NSNumber numberWithInt:tst->dlen_sign], &err);
+                                       STAssertNil((NSError *)err, @"Setting sign's digest length for %@", tname);
+                                       
+                                       if (sign_input_is == kSecInputIsDigest) 
+                                       {
+                                               sign_digest = SecDigestTransformCreate(tst->dalgo_sign, tst->dlen_sign, &err);
+                                               STAssertNotNil((id)sign_digest, @"Create sign's %@-%d digest transform (for %@)", tst->dalgo_sign, tst->dlen_sign, tname);
+                                               STAssertNil((NSError *)err, @"Making sign's digester (for %@) - err=%@", tname, err);
+                                       
+                                               SecTransformSetAttribute(sign, kSecInputIsAttributeName, sign_input_is, &err);
+                                               STAssertNil((NSError *)err, @"Setting sign's InputIs (for %@) - err=%@", tname, err);
+                                       }
+                               }
+                               
+                               if (tst->dalgo_verify) {
+                                       SecTransformSetAttribute(verify, kSecDigestTypeAttribute, tst->dalgo_verify, &err);
+                                       STAssertNil((NSError *)err, @"Setting verify's digest type for %@", tname);
+                                       SecTransformSetAttribute(verify, kSecDigestLengthAttribute, [NSNumber numberWithInt:tst->dlen_verify], &err);
+                                       STAssertNil((NSError *)err, @"Setting verify's digest length for %@", tname);
+                                       
+                                       if (verify_input_is == kSecInputIsDigest) {
+                                               verify_digest = SecDigestTransformCreate(tst->dalgo_verify, tst->dlen_verify, &err);
+                                               STAssertNotNil((id)verify_digest, @"Create verify's %@-%d digest transform (for %@)", tst->dalgo_verify, tst->dlen_verify, tname);
+                                               STAssertNil((NSError *)err, @"Making verify's digester (for %@) - err=%@", tname, err);
+                                               
+                                               SecTransformSetAttribute(verify, kSecInputIsAttributeName, verify_input_is, &err);
+                                               STAssertNil((NSError *)err, @"Setting verify's InputIs (for %@) - err=%@", tname, err);
+                                       }
+                               }
+                               
+                               SecGroupTransformRef group = SecTransformCreateGroupTransform();
+                               SecTransformSetAttribute(sign_digest ? sign_digest : sign, kSecTransformInputAttributeName, tst->msg_sign, (CFErrorRef *)&err);
+                               if (sign_digest) {
+                                       STAssertNil((NSError *)err, @"Setting sign's digest's input for %@", tname);
+                                       SecTransformConnectTransforms(sign_digest, kSecTransformOutputAttributeName, 
+                                                                       sign, kSecTransformInputAttributeName, group, NULL);
+                               } else {
+                                       STAssertNil((NSError *)err, @"Setting sign's input for %@", tname);
+                               }
+                               
+                               
+                               SecTransformSetAttribute(verify_digest ? verify_digest : verify, kSecTransformInputAttributeName, tst->msg_verify, (CFErrorRef *)&err);
+                               if (verify_digest) {
+                                       STAssertNil((NSError *)err, @"Setting verify's digest's input for %@", tname);
+                                       SecTransformConnectTransforms(verify_digest, kSecTransformOutputAttributeName, 
+                                                       verify, kSecTransformInputAttributeName, group, NULL);
+                               } else {
+                                       STAssertNil((NSError *)err, @"Setting verify's input for %@", tname);
+                               }
+                               
+                               SecTransformConnectTransforms(sign2 ? sign2 : sign, kSecTransformOutputAttributeName, verify, kSecSignatureAttributeName, group, NULL);
+                               
+                               dispatch_group_enter(dg);
+                               dispatch_queue_t temp_q = dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
+                               SecTransformExecuteAsync(sign, temp_q, 
+                                       ^(CFTypeRef message, CFErrorRef error, Boolean isFinal) 
+                                       {
+                                               if (message) 
+                                               {
+                                                       if (tst->pass) 
+                                                       {
+                                                               STAssertTrue(message == kCFBooleanTrue, @"Failed to verify proper signature %@; message = %@", tname, message);
+                                                       } else 
+                                                       {
+                                                               STAssertTrue(message == kCFBooleanFalse, @"Failed to detect tampering %@; message = %@", tname, message);
+                                                       }
+                                               }
+                                       
+                                               STAssertNil((NSError *)err, @"Executed ok for %@ (err=%@)", tname, error);
+                                       
+                                               if (isFinal)
+                                               {
+                                                       dispatch_group_leave(dg);
+                                               }
+                                       });
+
+                               CFRelease(sign);
+                               CFRelease(verify);
+                               CFRelease(group);
+                       }
+               }
+       }
+       
+       struct raw_test {
+               SecKeyRef pub, priv;
+               NSString *name;
+       } raw_tests[] = {
+               {rsa_pub_key, rsa_priv_key, @"RSA raw test"},
+               {dsa_pub_key, dsa_priv_key, @"DSA raw test"},
+               {ecdsa_pub_key, ecdsa_priv_key, @"ECDSA raw test"},
+       };
+       
+       for(i = 0; i < sizeof(raw_tests)/sizeof(raw_tests[0]); ++i) {
+               raw_test *t = raw_tests + i;
+               SecTransformRef tee = SecNullTransformCreate();
+               const char *raw_bytes = "some bytes";
+               CFDataRef bytes = CFDataCreate(NULL, (UInt8*)raw_bytes, strlen(raw_bytes));
+               CFErrorRef err = NULL;
+               
+               SecTransformRef sign = SecSignTransformCreate(t->priv, &err);
+               STAssertNil((id)err, @"%@ test sign create err=%@", t->name, err);
+               
+               SecTransformRef verify = SecVerifyTransformCreate(t->pub, NULL, &err);
+               STAssertNil((id)err, @"%@ test verify create err=%@", t->name, err);
+               
+               SecGroupTransformRef group = SecTransformCreateGroupTransform();
+               SecTransformConnectTransforms(sign, kSecTransformOutputAttributeName, verify, kSecSignatureAttributeName, group, &err);
+               SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, sign, kSecTransformInputAttributeName, group, &err);
+               SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, verify, kSecTransformInputAttributeName, group, &err);
+               SecTransformSetAttribute(tee, kSecTransformInputAttributeName, bytes, &err);
+               STAssertNil((id)err, @"%@ setup error=%@", t->name, err);
+               CFRetain(group);
+               dispatch_group_async(dg, dispatch_queue_create(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+                       CFErrorRef xerr = NULL;
+                       CFTypeRef result = SecTransformExecute(group, &xerr);
+                       CFRelease(group);
+                       
+                       if (result) {
+                               STAssertTrue(result == kCFBooleanTrue, @"%@ sign result=%@", t->name, result);
+                       } else {
+                               STFail(@"%@ no result", t->name);
+                       }
+                       STAssertNil((id)err, @"%@ execute error=%@", t->name, xerr);
+               });
+               CFRelease(group);
+       }
+       
+       // Test some things we want to fail for:
+       
+       SecTransformRef tee = SecNullTransformCreate();
+       SecTransformSetAttribute(tee, kSecTransformInputAttributeName, message, NULL);
+       SecTransformRef vrfy = SecVerifyTransformCreate(ecdsa_pub_key, NULL, NULL);
+       
+       SecGroupTransformRef group = SecTransformCreateGroupTransform();
+       SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, vrfy, kSecSignatureAttributeName, group, NULL);
+       SecTransformSetAttribute(vrfy, kSecDigestTypeAttribute, CFSTR("No such type"), NULL);
+       SecTransformSetAttribute(vrfy, kSecTransformInputAttributeName, message, NULL);
+       err = NULL;
+       CFTypeRef no_result = SecTransformExecute(group, (CFErrorRef*)&err);
+       CFRelease(group);
+       
+       STAssertNil((id)no_result, @"No result from nonexistent digest");
+       STAssertErrorHas((id)err, @"[Ii]nvalid digest algorithm", @"Error message describes nature of error (%@)", err);
+       STAssertErrorHas((id)err, @"ECDSA signature", @"Error is not overly general (%@)", err);
+       STAssertErrorHas((id)err, @"ECDSA signature", @"Error is not overly general (%@)", err);
+       STAssertErrorHas((id)err, @"SHA1.*SHA2", @"Error describes valid algorithms (%@)", err);
+       CFRelease(vrfy);
+       
+       // It would be awesome if we supported all the digests, and this test went away.
+       vrfy = SecVerifyTransformCreate(dsa_pub_key, message, NULL);
+       tee = SecNullTransformCreate();
+       
+       group = SecTransformCreateGroupTransform();
+       SecTransformConnectTransforms(vrfy, kSecSignatureAttributeName, tee, kSecTransformOutputAttributeName, group, NULL);
+       SecTransformConnectTransforms(tee, kSecTransformOutputAttributeName, vrfy, kSecTransformInputAttributeName, group, NULL);
+       SecTransformSetAttribute(vrfy, kSecDigestTypeAttribute, kSecDigestSHA2, NULL);
+       SecTransformSetAttribute(tee, kSecTransformInputAttributeName, message, NULL);
+       err = NULL;
+       no_result = SecTransformExecute(group, (CFErrorRef*)&err);
+       CFRelease(group);
+       
+       STAssertNil((id)no_result, @"No result from invalid digest");
+       STAssertErrorHas((id)err, @"[Ii]nvalid digest algorithm", @"Error message gives problem statement (%@)", err);
+       STAssertErrorHas((id)err, @"[^A-Z]DSA signature", @"Error is not overly general (%@)", err);
+       STAssertErrorHas((id)err, @"SHA1", @"Correct algorithm is named (%@)", err);
+       
+       dispatch_group_wait(dg, DISPATCH_TIME_FOREVER);
+}
+
+static BOOL keyWithBytes(CFDataRef keyData, SecKeyRef* key, CFTypeRef keyClass) {
+       CFErrorRef errorRef=NULL;
+       CFMutableDictionaryRef parameters;
+       parameters = CFDictionaryCreateMutable(kCFAllocatorDefault, 10, NULL, NULL);
+       
+       /* 
+        kSecAttrKeyClass values:
+        kSecAttrKeyClassPublic
+        kSecAttrKeyClassPrivate
+        kSecAttrKeyClassSymmetric
+        */
+       CFDictionaryAddValue(parameters, kSecAttrKeyClass, keyClass);
+       CFDictionaryAddValue(parameters, kSecAttrIsPermanent, kCFBooleanFalse); /* also means we have raw bits */
+       CFDictionaryAddValue(parameters, kSecAttrKeyType, kSecAttrKeyTypeRSA); /* also means we have raw bits */
+       *key = SecKeyCreateFromData(parameters, keyData, &errorRef);
+       CFRelease(parameters);
+       return (key != NULL);
+}
+
+-(void)testVerifyWithKeyFromBytes  {
+    /*
+       static const uint8_t original_pubKeyData[] =
+       {
+               0x30, 0x48, 0x02, 0x41, 0x00, 0xd1, 0x4d, 0x1c, 0xe6, 0xbd, 0xd6, 0x8c, 0x4b, 0x77, 0x1e, 0x9f,
+               0xbc, 0xe1, 0xf6, 0x96, 0xf2, 0x55, 0xa2, 0xdc, 0x28, 0x36, 0x39, 0xf4, 0xec, 0x5b, 0x85, 0x9b,
+               0x3c, 0x7f, 0x98, 0xe0, 0xed, 0x49, 0xf5, 0x44, 0xb1, 0x87, 0xa8, 0xf6, 0x7f, 0x55, 0xc0, 0x39,
+               0xf0, 0xe7, 0xcc, 0x9c, 0x84, 0xde, 0x7d, 0x9a, 0x87, 0x38, 0xf2, 0x4b, 0x11, 0x6f, 0x63, 0x90,
+               0xfc, 0x72, 0x2c, 0x86, 0xa3, 0x02, 0x03, 0x01, 0x00, 0x01
+       }; */
+       
+       // openssl genrsa -out /tmp/rsa512.pem
+       // openssl rsa -inform PEM -in /tmp/rsa512.pem -outform DER -out /tmp/rsa512.der
+       // hexdump -C /tmp/rsa512.der | cut -c10-58 | tr -s ' ' ' ' | sed -e 's/ /, 0x/g' -e 's/$/,/' | cut -c3-|pbcopy
+       static const uint8_t pubKeyData[] = {
+               0x30, 0x5c, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05,
+               0x00, 0x03, 0x4b, 0x00, 0x30, 0x48, 0x02, 0x41, 0x00, 0xbf, 0xd5, 0xce, 0x43, 0x59, 0xd5, 0xf8,
+               0x41, 0xb2, 0xe1, 0x16, 0x02, 0x2a, 0x16, 0xcb, 0xef, 0x49, 0xea, 0x98, 0x71, 0xf8, 0xfb, 0x94,
+               0x23, 0x12, 0xf7, 0xbc, 0x80, 0xd0, 0x8b, 0xfd, 0x29, 0xb8, 0xfc, 0x2c, 0x3d, 0x13, 0x6f, 0x37,
+               0xef, 0xa7, 0x1e, 0xf9, 0x4c, 0x3d, 0x38, 0x3a, 0x2f, 0x6b, 0xa8, 0x16, 0x00, 0x27, 0x5a, 0xbe,
+               0x3d, 0x61, 0xdd, 0x18, 0x45, 0x22, 0xdb, 0x1a, 0xff, 0x02, 0x03, 0x01, 0x00, 0x01, 
+       };
+       static const uint8_t signatureData[] =
+       {
+               0xbc, 0x76, 0x2a, 0x50, 0x4e, 0x17, 0x0b, 0xa9, 0x31, 0x3b, 0xc5, 0xb0, 0x4d, 0x2a, 0x01, 0x9a,
+               0xbb, 0x5e, 0x7b, 0x6e, 0x90, 0x2f, 0xaf, 0x3f, 0x40, 0xdb, 0xb0, 0xfc, 0x49, 0xcf, 0xbb, 0xb6,
+               0x08, 0xf0, 0xbb, 0x04, 0x5f, 0x89, 0x0b, 0x10, 0x47, 0x06, 0x93, 0xb3, 0xb7, 0x0b, 0x4e, 0x17,
+               0xe9, 0xb1, 0x55, 0x94, 0x63, 0x30, 0x0b, 0xa3, 0xb1, 0x28, 0xba, 0xe8, 0xef, 0xb4, 0xbd, 0xc5
+       };
+       
+       const char *raw_data = "Data to verify";
+       CFDataRef data = CFDataCreate(NULL, (UInt8*) raw_data, strlen(raw_data));
+
+       SecKeyRef key;
+       CFDataRef cfkeybytes;
+       CFErrorRef error;
+       cfkeybytes = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, pubKeyData, sizeof(pubKeyData),kCFAllocatorNull);
+       
+       if(keyWithBytes(cfkeybytes, &key, kSecAttrKeyClassPublic)){
+               CFDataRef signature = CFDataCreateWithBytesNoCopy(kCFAllocatorDefault, signatureData, sizeof(signatureData),kCFAllocatorNull);
+               SecTransformRef vt = SecVerifyTransformCreate(key,signature,&error);
+               SecTransformSetAttribute(vt, kSecTransformDebugAttributeName, @"YES", NULL);
+               SecTransformSetAttribute(vt, kSecTransformInputAttributeName, data, &error);
+               CFBooleanRef signature_ok = (CFBooleanRef) SecTransformExecute(vt, &error);
+               
+               CFRelease(vt);
+               CFRelease(key);
+               CFRelease(signature);
+               CFRelease(data);
+               CFRelease(cfkeybytes);
+               
+               NSLog(@"STE result %@, err=%@", signature_ok, error);
+               STAssertNil((id)error, @"Error from SecTransformExecute: %@", error);
+       } else {
+               STFail(@"Can't get SecKeyCreateFromData to work");
+       }
+}
+
+-(void)testAESAndCastKeysFromBytes  {
+       CFErrorRef err = NULL;
+       struct tcase {
+               const char *name;
+               CFTypeRef key_type;
+               NSData *key_data;
+       };
+       const char *aes_kbytes = "0123456789012345";
+       const char *cast_kbytes = "01234567";
+
+       struct tcase cases[] = {
+               {"AES", kSecAttrKeyTypeAES, [NSData dataWithBytes:aes_kbytes length:strlen(aes_kbytes)]},
+               {"CAST", kSecAttrKeyTypeCAST, [NSData dataWithBytes:cast_kbytes length:strlen(cast_kbytes)]},
+       };
+       
+       int i;
+       for(i = 0; i < sizeof(cases)/sizeof(cases[0]); ++i) {
+               NSDictionary *parm = [NSDictionary dictionaryWithObjectsAndKeys:
+                                                         (id)kSecAttrKeyClassSymmetric, kSecAttrKeyClass,
+                                                         (id)cases[i].key_type, kSecAttrKeyType,
+                                                         (id)kCFBooleanFalse, kSecAttrIsPermanent,
+                                                         NULL];
+               
+               SecKeyRef k = SecKeyCreateFromData((CFDictionaryRef)parm, (CFDataRef)cases[i].key_data, (CFErrorRef *)&err);
+               STAssertNotNil((id)k, @"%s SecKeyCreateFromData didn't", cases[i].name);
+               STAssertNil((id)err, @"%s SecKeyCreateFromData err=%@", err);
+               
+               SecTransformRef et = SecEncryptTransformCreate(k, &err);
+               STAssertNotNil((id)et, @"No %s EncryptTransform created", cases[i].name);
+               STAssertNil((id)err, @"Error from %s SecEncryptTransformCreate err=%@", cases[i].name, err);
+               
+               SecTransformRef dt = SecDecryptTransformCreate(k, &err);
+               STAssertNotNil((id)dt, @"No %s DecryptTransform created", cases[i].name);
+               STAssertNil((id)err, @"Error from %s SecDecryptTransformCreate err=%@", cases[i].name, err);
+               
+               if (k) {
+                       BOOL rt_ok = RoundTrip(CFSTR("/usr/share/dict/propernames"), et, dt, YES);
+                       CFRelease(et);
+                       CFRelease(dt);
+                       STAssertTrue(rt_ok, @"%s's round trip", cases[i].name);
+               }
+       }
+}
+
+-(void)testDispatchAsumptions {
+    // Failures here don't directly indicate we have a bug.  It would indicate that
+    // either dispatch has one, or that we rely on something dispatch never promised
+    // and has changed.
+    
+    dispatch_semaphore_t pre_sem = dispatch_semaphore_create(0);
+    dispatch_semaphore_t post_sem = dispatch_semaphore_create(0);
+    __block bool pre_wait_works = false;
+    
+    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 0.1 * NSEC_PER_SEC), dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void){
+        STAssertTrue(0 == dispatch_semaphore_wait(pre_sem, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"semaphore signal prior to wait pre-wakes");
+        pre_wait_works = true;
+        dispatch_semaphore_signal(post_sem);
+    });
+    dispatch_semaphore_signal(pre_sem);
+    STAssertTrue(0 == dispatch_semaphore_wait(post_sem, dispatch_time(DISPATCH_TIME_NOW, NSEC_PER_SEC)), @"signal after wait wakes");
+    STAssertTrue(pre_wait_works, @"pre-wait worked");
+    
+    
+}
+
+// Build a group containing 3 subgroups, G1 which has 2 encoders, G2 and G3 which have one
+// decoder each.   Exports and attributes are hooked up so execution results in a CFData
+// with the same contents as input_data.   "self" is used by the STAssert macros.
+// The various transforms are assigned names: G1, G2, G3, E64, EZLIB, DZLIB, D64.
+static SecTransformRef build_nested_groups(id self, CFDataRef input_data) {
+    SecGroupTransformRef outer = SecTransformCreateGroupTransform();
+    SecGroupTransformRef g1 = SecTransformCreateGroupTransform();
+    SecGroupTransformRef g2 = SecTransformCreateGroupTransform();
+    SecGroupTransformRef g3 = SecTransformCreateGroupTransform();
+    
+    CFErrorRef err = NULL;
+    
+    SecTransformSetAttribute(outer, kSecTransformTransformName, CFSTR("OUTER"), &err);
+    STAssertNil((id)err, @"Can't set outer's name: %@", err);
+    SecTransformSetAttribute(g1, kSecTransformTransformName, CFSTR("G1"), &err);
+    STAssertNil((id)err, @"Can't set g1's name: %@", err);
+    SecTransformSetAttribute(g2, kSecTransformTransformName, CFSTR("G2"), &err);
+    STAssertNil((id)err, @"Can't set g2's name: %@", err);
+    SecTransformSetAttribute(g3, kSecTransformTransformName, CFSTR("G3"), &err);
+    STAssertNil((id)err, @"Can't set g3's name: %@", err);
+    
+    SecTransformRef e64 = SecEncodeTransformCreate(kSecBase64Encoding, &err);
+    STAssertNil((id)err, @"Expected err to be nil, got: %@", err);
+    STAssertNotNil((id)e64, @"Could not make Encode64 transform");
+    SecTransformSetAttribute(e64, kSecTransformTransformName, CFSTR("E64"), NULL);
+    SecTransformRef ezlib = SecEncodeTransformCreate(kSecZLibEncoding, &err);
+    STAssertNil((id)err, @"Expected err to be nil, got: %@", err);
+    STAssertNotNil((id)ezlib, @"Could not make Encode ZLib transform");
+    SecTransformSetAttribute(ezlib, kSecTransformTransformName, CFSTR("EZLIB"), NULL);
+    
+    SecTransformConnectTransforms(e64, kSecTransformOutputAttributeName, ezlib, kSecTransformInputAttributeName, g1, &err);
+    STAssertNil((id)err, @"Can't connect e64 to ezlib: %@", err);
+    SecTransformConnectTransforms(g1, kSecTransformInputAttributeName, e64, kSecTransformInputAttributeName, g1, &err);
+    STAssertNil((id)err, @"Can't connect g1's input to e64's input: %@", err);
+    SecTransformConnectTransforms(ezlib, kSecTransformOutputAttributeName, g1, kSecTransformOutputAttributeName, g1, &err);
+    STAssertNil((id)err, @"Can't connect ezlib's output to g1's output: %@", err);
+    
+    SecTransformRef dzlib = SecDecodeTransformCreate(kSecZLibEncoding, &err);
+    STAssertNil((id)err, @"Expected err to be nil, got: %@", err);
+    STAssertNotNil((id)dzlib, @"Could not make Decode ZLib transform");
+    SecTransformSetAttribute(dzlib, kSecTransformTransformName, CFSTR("dzlib"), NULL);
+    SecTransformRef d64 = SecDecodeTransformCreate(kSecBase64Encoding, &err);
+    STAssertNil((id)err, @"Expected err to be nil, got: %@", err);
+    STAssertNotNil((id)d64, @"Could not make Decode64 transform");
+    SecTransformSetAttribute(dzlib, kSecTransformTransformName, CFSTR("D64"), NULL);
+    
+    // putting just one transform in g2 and g3
+    SecTransformConnectTransforms(g2, kSecTransformInputAttributeName, dzlib, kSecTransformInputAttributeName, g2, &err);
+    STAssertNil((id)err, @"Can't connect g2's input to dzlib's input: %@", err);
+    SecTransformConnectTransforms(dzlib, kSecTransformOutputAttributeName, g2, kSecTransformOutputAttributeName, g2, &err);
+    STAssertNil((id)err, @"Can't connect dzlib's output to g2's output: %@", err);
+    
+    SecTransformConnectTransforms(g3, kSecTransformInputAttributeName, d64, kSecTransformInputAttributeName, g3, &err);
+    STAssertNil((id)err, @"Can't connect g2's input to d64's input: %@", err);
+    SecTransformConnectTransforms(d64, kSecTransformOutputAttributeName, g3, kSecTransformOutputAttributeName, g3, &err);
+    STAssertNil((id)err, @"Can't connect d64's output to g2's output: %@", err);
+    
+    SecTransformConnectTransforms(g1, kSecTransformOutputAttributeName, g2, kSecTransformInputAttributeName, outer, &err);
+    STAssertNil((id)err, @"Can't connect g1 to g2 (dzlib): %@", err);
+    SecTransformConnectTransforms(g2, kSecTransformOutputAttributeName, g3, kSecTransformInputAttributeName, outer, &err);
+    STAssertNil((id)err, @"Can't connect g2 (dzlib) to g3 (d64): %@", err);
+    
+    SecTransformSetAttribute(g1, kSecTransformInputAttributeName, input_data, &err);
+    STAssertNil((id)err, @"Can't set g1's input: %@", err);
+    
+    
+    CFRelease(e64);
+    CFRelease(ezlib);
+    CFRelease(dzlib);
+    CFRelease(d64);
+    CFRelease(g1);
+    CFRelease(g2);
+    CFRelease(g3);
+    return outer;
+}
+
+-(void)testGroupsInGroups {
+    UInt8 original_bytes[] = "'Twas brillig and the...was that smiley toads?   Something with chives?  Aw heck!";
+    CFDataRef original = CFDataCreate(NULL, original_bytes, sizeof(original_bytes));
+
+    // Test executing the top group, a sub group, and a non-group member.
+    for (NSString *name in [NSArray arrayWithObjects:@"OUTER", @"G1", @"D64", nil]) {
+        CFErrorRef err = NULL;
+        SecGroupTransformRef outer = build_nested_groups(self, original);
+        SecTransformRef start_at = SecTransformFindByName(outer, (CFStringRef)name);
+        STAssertNotNil((id)start_at, @"Expected to find %@", name);
+        
+        CFDataRef output = (CFDataRef)SecTransformExecute(start_at, &err);
+        STAssertNil((id)err, @"Can't execute directly created nested transform starting at %@: %@", start_at, err);
+        STAssertEqualObjects((id)output, (id)original, @"Output and original should match (started at %@)", start_at);
+        CFRelease(outer);
+        if (err) {
+            CFRelease(err);
+        }
+    }
+    
+    {
+        SecGroupTransformRef bad_outer = build_nested_groups(self, original);
+        SecTransformRef d64 = SecTransformFindByName(bad_outer, CFSTR("D64"));
+        STAssertNotNil((id)d64, @"Expected to find d64");
+        CFErrorRef err = NULL;
+        // d64 is in a group in bad_outer, we set things up to fail
+        // and later expect execute to fail because of it.
+        SecTransformSetAttribute(d64, kSecDecodeTypeAttribute, CFSTR("NOT valid"), &err);
+        if (err) {
+            // It can fail right away
+            ErrorHas((NSError*)err, @"Unsupported decode type");
+        } else {
+            // Or later (see below)
+            STAssertNil((id)err, @"Expected to set decode type: %@", err);
+        }
+        
+        SecTransformRef e64 = SecTransformFindByName(bad_outer, CFSTR("E64"));
+        STAssertNotNil((id)e64, @"Expected to find e64");
+        CFStringRef any = CFSTR("ANY");
+        // e64 and d64 aren't in the same groups, but they are in outer.
+        // There should be no way to (directly) connect them, so try all
+        // 4 groups and make sure none work.
+        for (NSString *group_name in [NSArray arrayWithObjects:@"OUTER", @"G1", @"G2", @"G3", nil]) {
+            SecTransformRef connect_in = SecTransformFindByName(bad_outer, (CFStringRef)group_name);
+            STAssertNotNil((id)connect_in, @"Expected to find %@", group_name);
+            err = NULL;
+            SecTransformConnectTransforms(d64, any, e64, any, bad_outer, &err);
+            STAssertNotNil((id)err, @"Expected error on cross group connect (in %@)", group_name);
+            if (err) {
+                STAssertEquals(CFErrorGetCode(err), (CFIndex)kSecTransformErrorInvalidConnection, @"error code (in %@)", group_name);
+                STAssertEqualObjects((id)CFErrorGetDomain(err), (id)kSecTransformErrorDomain, @"error domain (in %@)", group_name);
+                CFRelease(err);
+                err = NULL;
+            }
+            
+            // While we are here, make sure we can't set a non-exported group attribute
+            SecTransformSetAttribute((SecTransformRef)connect_in, CFSTR("nobody-exports-me"), CFSTR("VALUE"), &err);
+            STAssertNotNil((id)err, @"Expected an error setting a non-exported attribute on %@", connect_in);
+            // Make sure this is the error we expect, not something unrelated to our transgression
+            ErrorHas((NSError*)err, @"non-exported attribute");
+            // Error should have the name of the offending attribute
+            ErrorHas((NSError*)err, @"nobody-exports-me");
+            if (err) {
+                CFRelease(err);
+                err = NULL;
+            }
+        }
+        
+        CFTypeRef no_result = SecTransformExecute(bad_outer, &err);
+        STAssertNotNil((id)err, @"Expected error");
+        ErrorHas((NSError*)err, @"Unsupported decode type");
+        STAssertNil((id)no_result, @"Expected no result, got: %@", no_result);
+        CFRelease(bad_outer);
+        
+        // Make sure we can't connect to or from non-exported group attributes
+        bad_outer = build_nested_groups(self, original);
+        STAssertNotNil((id)bad_outer, @"Expected to build nested transform");
+        SecTransformRef g1 = SecTransformFindByName(bad_outer, CFSTR("G1"));
+        STAssertNotNil((id)g1, @"Expected to find g1");
+        SecTransformRef appendix = SecNullTransformCreate();
+        SecTransformConnectTransforms(appendix, kSecTransformOutputAttributeName, g1, CFSTR("NONE"), bad_outer, &err);
+        STAssertNotNil((id)err, @"Expected to fail connecting appendix to g1, but didn't");
+        ErrorHas((NSError*)err, @"non-exported attribute");
+        if (err) {
+            CFRelease(err);
+            err = NULL;
+        }
+        SecTransformConnectTransforms(g1, CFSTR("DOES_NOT_EXIST"), appendix, kSecTransformInputAttributeName, bad_outer, &err);
+        STAssertNotNil((id)err, @"Expected to fail connecting g1 to appendix, but didn't");
+        ErrorHas((NSError*)err, @"non-exported attribute");
+        if (err) {
+            CFRelease(err);
+            err = NULL;
+        }
+        
+        CFRelease(bad_outer);
+        CFRelease(appendix);
+    }
+}
+
+// 10080968 covers this case.   It isn't a regression (it was impossible to create nested groups
+// until recently), but it needs to be addressed before we ship.
+-(void)disabledUntilPR_10080968_testExternalizeGroupsInGroups {
+    CFErrorRef err = NULL;
+    UInt8 original_bytes[] = "Sic Semper Tyrannosaurus!";
+    CFDataRef original = CFDataCreate(NULL, original_bytes, sizeof(original_bytes));
+    
+    SecGroupTransformRef outer = build_nested_groups(self, original);
+    NSLog(@"outer=%@", SecTransformDotForDebugging(outer));
+    SecTransformRef d64 = SecTransformFindByName(outer, CFSTR("D64"));
+    STAssertNotNil((id)d64, @"Expected to find d64");
+    
+    CFDictionaryRef freezeDriedNestedGroups = SecTransformCopyExternalRepresentation(d64);
+    STAssertNotNil((id)freezeDriedNestedGroups, @"Expected to externalize group");
+    
+    SecTransformRef outer2 = SecTransformCreateFromExternalRepresentation(freezeDriedNestedGroups, &err);
+    STAssertNil((id)err, @"Can't create nested group err: %@", err);
+    STAssertNotNil((id)outer2, @"Expected transform fron xrep: %@", freezeDriedNestedGroups);
+    NSLog(@"outer2=%@", SecTransformDotForDebugging(outer2));
+    
+    CFTypeRef output2 = SecTransformExecute(outer2, &err);
+    STAssertNil((id)err, @"Can't execute outer2: %@", err);
+    STAssertEqualObjects((id)output2, (id)original, @"Output2 and original should match");
+}
+
+static NSString *CopyLeakLine()
+{
+    static char os_build[16];
+    static dispatch_once_t get_os_build_once;
+    static BOOL broken_leaks_command = NO;
+    
+    dispatch_once(&get_os_build_once, ^{
+        int mib[] = { CTL_KERN, KERN_OSVERSION };
+        size_t bufsz = sizeof(os_build);
+        sysctl(mib, 2, os_build, &bufsz, NULL, 0);
+        
+        if (4 == sizeof(char*) && 0 == strcmp(os_build, "12A75")) {
+            // 12A75's leaks command was badly broken for 32 bit.
+            // Running it suspends otest, and it is too hard to
+            // recover.
+            broken_leaks_command = YES;
+        }
+    });
+
+    if (broken_leaks_command) {
+        return [NSString stringWithFormat:@"Leaks command is broken in %s", os_build];
+    }
+    
+    NSRegularExpression *matchLeaksLine = [NSRegularExpression regularExpressionWithPattern:@"^Process \\d+: \\d+ leaks for \\d+ total leaked bytes.$" options:NSRegularExpressionAnchorsMatchLines error:NULL];
+    
+       char *leak_command = NULL;
+       NSString *fname = [NSString stringWithFormat:@"/tmp/L%d-%d", getpid(), (int)arc4random()];
+       asprintf(&leak_command, "(/usr/bin/leaks %d >%s || (echo OOPS; kill -CONT %d))", getpid(), [fname UTF8String], getpid());
+       system(leak_command);
+       free(leak_command);
+       NSString *output = [NSString stringWithContentsOfFile:fname encoding:NSUTF8StringEncoding error:NULL];
+       NSTextCheckingResult *result = [matchLeaksLine firstMatchInString:output options:0 range:NSMakeRange(0, [output length])];
+       if (result.range.location == NSNotFound) {
+               return NULL;
+       }
+       NSRange matchRange = result.range;
+       return [output substringWithRange:matchRange];
+}
+                                                                 
+-(void)testAAASimpleLeakTest {
+       NSString *starting_leaks = CopyLeakLine();
+       STAssertNotNil(starting_leaks, @"Found initial leaks");
+       for(int i = 0; i < 10; i++) {
+               CFRelease(SecTransformCreateGroupTransform());
+       }
+       
+       NSString *current_leaks = NULL;
+       
+       // Some of the destruction is async, so if they don't pan out the same, a little sleep and retry
+       // can legitimately fix it.
+       for(int i = 0; i < 10; i++) {
+               current_leaks = CopyLeakLine();
+               if ([current_leaks isEqualToString:starting_leaks]) {
+                       break;
+               } else {
+                       sleep(1);
+               }
+       }
+       
+       STAssertNotNil(current_leaks, @"Found current leaks");
+       STAssertEqualObjects(current_leaks, starting_leaks, @"Expected no new leaks");
+}
+
+-(void)testAAASimpleishLeakTest {
+    NSLog(@"pid=%d", getpid());
+       NSString *starting_leaks = CopyLeakLine();
+       STAssertNotNil(starting_leaks, @"Found initial leaks");
+    CFErrorRef err = NULL;
+    
+    // Derived from Matt Wright's 10242560 test.c
+    int fd = open("/dev/random", O_RDONLY);
+    SecTransformRef b64encode = SecEncodeTransformCreate(kSecBase64Encoding, NULL);
+    const int buffer_size = 1024;
+    void *buffer = malloc(buffer_size);
+    // For this test, ignore short reads
+    read(fd, buffer, buffer_size);
+    CFDataRef data = CFDataCreateWithBytesNoCopy(NULL, (UInt8*)buffer, buffer_size, kCFAllocatorMalloc);
+    SecTransformSetAttribute(b64encode, kSecTransformInputAttributeName, data, &err);
+    STAssertNil((id)err, @"Expected no SecTransformSetAttribute error, got: %@", err);
+    CFRelease(data);
+    CFTypeRef output = SecTransformExecute(b64encode, &err);
+    STAssertNotNil((id)output, @"Expected result");
+    STAssertNil((id)err, @"Expected no execute error, got: %@", err);
+    CFRelease(output);
+    CFRelease(b64encode);
+    
+       NSString *current_leaks = NULL;
+       
+       // Some of the destruction is async, so if they don't pan out the same, a little sleep and retry
+       // can legitimately fix it.
+       for(int i = 0; i < 10; i++) {
+               current_leaks = CopyLeakLine();
+               if ([current_leaks isEqualToString:starting_leaks]) {
+                       break;
+               } else {
+                       sleep(1);
+               }
+       }
+       
+       STAssertNotNil(current_leaks, @"Found current leaks");
+       STAssertEqualObjects(current_leaks, starting_leaks, @"Expected no new leaks");
+}
+
+@end