--- /dev/null
+/*
+ * 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