snej@0: // snej@0: // MYCryptoTest.m snej@0: // MYCrypto-iPhone snej@0: // snej@0: // Created by Jens Alfke on 4/1/09. snej@0: // Copyright 2009 Jens Alfke. All rights reserved. snej@0: // snej@0: snej@3: #import "MYPublicKey.h" snej@3: #import "MYPrivateKey.h" snej@0: #import "MYKeychain.h" snej@0: #import "MYDigest.h" snej@4: #import "MYIdentity.h" snej@12: #if !TARGET_OS_IPHONE snej@8: #import "MYCrypto+Cocoa.h" snej@12: #endif snej@0: #import "MYCrypto_Private.h" snej@0: snej@0: snej@0: #if DEBUG snej@0: snej@11: snej@11: #define kTestCaseRSAKeySize 2048 snej@11: snej@0: #pragma mark - snej@0: #pragma mark KEYCHAIN: snej@0: snej@0: snej@0: TestCase(MYKeychain) { snej@0: MYKeychain *kc = [MYKeychain defaultKeychain]; snej@0: Log(@"Default keychain = %@", kc); snej@0: CAssert(kc); snej@2: #if !MYCRYPTO_USE_IPHONE_API snej@0: CAssert(kc.path); snej@0: #endif snej@0: snej@0: kc = [MYKeychain allKeychains]; snej@0: Log(@"All-keychains = %@", kc); snej@0: CAssert(kc); snej@2: #if !MYCRYPTO_USE_IPHONE_API snej@0: CAssertEq(kc.path,nil); snej@1: #endif snej@0: } snej@0: snej@0: snej@0: TestCase(EnumerateKeys) { snej@0: RequireTestCase(MYKeychain); snej@0: NSEnumerator *e = [[MYKeychain allKeychains] enumeratePublicKeys]; snej@0: Log(@"Public Key Enumerator = %@", e); snej@0: CAssert(e); snej@0: for (MYPublicKey *key in e) { snej@0: Log(@"Found %@ -- name=%@", key, key.name); snej@0: } snej@0: snej@3: e = [[MYKeychain allKeychains] enumeratePrivateKeys]; snej@0: Log(@"Key-Pair Enumerator = %@", e); snej@0: CAssert(e); snej@3: for (MYPrivateKey *key in e) { snej@0: Log(@"Found %@ -- name=%@", key, key.name); snej@0: } snej@0: snej@0: e = [[MYKeychain allKeychains] enumerateSymmetricKeys]; snej@0: Log(@"Symmetric Key Enumerator = %@", e); snej@0: CAssert(e); snej@0: for (MYSymmetricKey *key in e) { snej@0: Log(@"Found %@ -- name=%@", key, key.name); snej@0: } snej@0: } snej@0: snej@0: snej@0: TestCase(EnumerateCerts) { snej@0: RequireTestCase(MYKeychain); snej@0: NSEnumerator *e = [[MYKeychain allKeychains] enumerateCertificates]; snej@0: Log(@"Enumerator = %@", e); snej@0: CAssert(e); snej@0: for (MYCertificate *cert in e) { snej@0: //Log(@"Found %@ -- name=%@, email=%@", cert, cert.commonName, cert.emailAddresses); snej@0: } snej@0: } snej@0: snej@4: TestCase(EnumerateIdentities) { snej@4: RequireTestCase(MYKeychain); snej@4: NSEnumerator *e = [[MYKeychain allKeychains] enumerateIdentities]; snej@4: Log(@"Enumerator = %@", e); snej@4: CAssert(e); snej@4: for (MYIdentity *ident in e) { snej@8: Log(@"Found %@\n\tcommonName=%@\n\temails=(%@)\n\tkey=%@", snej@4: ident, ident.commonName, snej@5: #if TARGET_OS_IPHONE snej@5: nil, snej@5: #else snej@4: [ident.emailAddresses componentsJoinedByString: @", "], snej@5: #endif snej@4: ident.privateKey); snej@4: } snej@4: } snej@4: snej@0: snej@0: #pragma mark - snej@0: #pragma mark SYMMETRIC KEYS: snej@0: snej@0: snej@12: static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits, MYKeychain *inKeychain ) { snej@2: NSAutoreleasePool *pool = [NSAutoreleasePool new]; snej@12: MYSymmetricKey *key = nil; snej@12: @try{ snej@12: Log(@"--- Testing %3u-bit #%i %s", sizeInBits, (int)algorithm, snej@12: (inKeychain ?", in keychain" :"")); snej@12: // Generate key: snej@12: if (inKeychain) snej@12: key = [inKeychain generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm]; snej@12: else snej@12: key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm]; snej@12: Log(@"Created %@", key); snej@2: CAssert(key); snej@12: CAssertEq(key.algorithm, algorithm); snej@12: CAssertEq(key.keySizeInBits, sizeInBits); snej@12: #if !TARGET_OS_IPHONE snej@12: CAssert(key.cssmKey != NULL); snej@12: #endif snej@12: snej@12: NSData *keyData = key.keyData; snej@12: Log(@"Key data = %@", keyData); snej@12: CAssertEq(keyData.length, sizeInBits/8); snej@12: snej@12: // Encrypt a small amount of text: snej@12: Log(@"Testing encryption / decryption ..."); snej@12: NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding]; snej@12: NSData *encrypted = [key encryptData: cleartext]; snej@12: Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted); snej@12: CAssert(encrypted.length >= cleartext.length); snej@12: NSData *decrypted = [key decryptData: encrypted]; snej@12: CAssertEqual(decrypted, cleartext); snej@12: snej@12: // Encrypt large binary data: snej@12: cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"]; snej@12: CAssert(cleartext); snej@12: encrypted = [key encryptData: cleartext]; snej@12: Log(@"Encrypted = %u bytes", encrypted.length); snej@12: CAssert(encrypted.length >= cleartext.length); snej@12: decrypted = [key decryptData: encrypted]; snej@12: CAssertEqual(decrypted, cleartext); snej@12: snej@12: #if 1 snej@12: Log(@"Testing initWithKeyData:..."); snej@12: MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: keyData algorithm: algorithm]; snej@12: CAssert(key2); snej@12: Log(@"Key from data = %@",key2); snej@12: CAssertEqual(key2.keyData, keyData); snej@12: CAssertEq(key2.algorithm, algorithm); snej@12: CAssertEq(key2.keySizeInBits, sizeInBits); snej@2: decrypted = [key2 decryptData: encrypted]; snej@2: CAssertEqual(decrypted, cleartext); snej@12: [key2 release]; snej@12: #endif snej@12: snej@12: #if !TARGET_OS_IPHONE jens@16: #if 1 // TEMP-ORARILY OUT OF ORDER snej@12: // Try exporting and importing a wrapped key: snej@12: Log(@"Testing export/import..."); snej@13: NSData *exported = [key exportWrappedKeyWithPassphrasePrompt: @"Export symmetric key with passphrase:"]; snej@12: Log(@"Exported key: %@", exported); snej@13: #if 1 snej@12: CAssert(exported); snej@12: #else snej@12: if (!exported) snej@12: Warn(@"Unable to export wrapped key"); snej@12: else snej@12: #endif snej@12: { snej@12: CAssert(exported); jens@16: MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithWrappedKeyData: exported]; snej@12: Log(@"Reconstituted as %@", key2); snej@12: CAssertEqual(key2.keyData,key.keyData); snej@12: decrypted = [key2 decryptData: encrypted]; snej@12: CAssertEqual(decrypted, cleartext); snej@12: } snej@13: #endif 0 snej@12: #endif snej@12: }@finally{ snej@12: [key removeFromKeychain]; snej@12: } snej@2: [pool drain]; snej@2: } snej@2: snej@2: snej@0: TestCase(MYSymmetricKey) { snej@0: #define kNTests 11 snej@0: static const CCAlgorithm kTestAlgorithms[kNTests] = { snej@0: kCCAlgorithmAES128, kCCAlgorithmAES128, kCCAlgorithmAES128, snej@0: kCCAlgorithmDES, kCCAlgorithm3DES, snej@0: kCCAlgorithmCAST, kCCAlgorithmCAST, kCCAlgorithmCAST, snej@0: kCCAlgorithmRC4, kCCAlgorithmRC4, kCCAlgorithmRC4}; snej@0: snej@0: static const unsigned kTestBitSizes[kNTests] = { snej@0: 128, 192, 256, snej@0: 64, 3*64, snej@0: 40, 80, 128, snej@0: 32, 200, 512*8}; snej@0: snej@12: for (int useKeychain=0; useKeychain<=1; useKeychain++) snej@12: for (int testNo=0; testNo= cleartext.length); snej@12: NSData *decrypted = [key decryptData: encrypted]; snej@12: CAssertEqual(decrypted, cleartext); snej@12: snej@12: // Now test decryption by re-entered passphrase: snej@12: Log(@"Testing decryption using re-entered passphrase..."); snej@12: MYSymmetricKey *key2 = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Symmetric Key Passphrase Test Case" snej@12: alertPrompt: @"Please re-enter the same passphrase:" snej@12: creating: NO snej@12: salt: @"wahooma"]; snej@12: Log(@"Key from passphrase = %@", key2); snej@12: CAssert(key2); snej@12: decrypted = [key2 decryptData: encrypted]; snej@12: CAssertEqual(decrypted, cleartext); snej@0: } snej@14: #endif snej@0: snej@0: snej@0: #pragma mark - snej@0: #pragma mark KEY-PAIRS: snej@0: snej@0: snej@8: static void TestUseKeyPair(MYPrivateKey *pair) { snej@8: Log(@"---- TestUseKeyPair { %@ , %@ }.", pair, pair.publicKey); snej@0: CAssert(pair); snej@0: CAssert(pair.keyRef); snej@3: MYPublicKey *publicKey = pair.publicKey; snej@3: CAssert(publicKey.keyRef); snej@0: snej@8: NSData *pubKeyData = publicKey.keyData; snej@8: Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length); snej@8: CAssert(pubKeyData); snej@8: snej@8: MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest; snej@8: Log(@"Public key digest = %@",pubKeyDigest); snej@8: CAssertEqual(pair.publicKeyDigest, pubKeyDigest); snej@8: snej@8: Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData); snej@14: CAssertEqual(pubKeyData.my_SHA1Digest,pubKeyDigest); snej@8: snej@8: // Let's sign data: snej@8: NSData *data = [@"This is a test. This is only a test!" dataUsingEncoding: NSUTF8StringEncoding]; snej@8: NSData *sig = [pair signData: data]; snej@8: Log(@"Signature = %@ (%u bytes)",sig,sig.length); snej@8: CAssert(sig); snej@8: CAssert( [publicKey verifySignature: sig ofData: data] ); snej@8: snej@8: // Now let's encrypt... snej@13: NSData *crypted = [publicKey rawEncryptData: data]; snej@8: Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length); snej@8: CAssert(crypted); snej@13: CAssertEqual([pair rawDecryptData: crypted], data); snej@8: Log(@"Verified decryption."); snej@8: snej@8: // Test creating a standalone public key: snej@8: MYPublicKey *pub = [[MYPublicKey alloc] initWithKeyRef: publicKey.keyRef]; snej@8: CAssert( [pub verifySignature: sig ofData: data] ); snej@8: Log(@"Verified signature."); snej@8: snej@8: // Test creating a public key from data: snej@8: Log(@"Reconstituting public key from data..."); snej@8: pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData]; snej@8: CAssert(pub); snej@8: CAssertEqual(pub.keyData, pubKeyData); snej@8: CAssertEqual(pub.publicKeyDigest, pubKeyDigest); snej@8: CAssert( [pub verifySignature: sig ofData: data] ); snej@8: Log(@"Verified signature from reconstituted key."); snej@8: } snej@8: snej@8: snej@14: static void TestWrapSessionKey( MYPrivateKey *privateKey ) { snej@14: #if !TARGET_OS_IPHONE snej@13: MYSymmetricKey *sessionKey = [MYSymmetricKey generateSymmetricKeyOfSize: 128 algorithm:kCCAlgorithmAES128]; snej@13: CAssert(sessionKey); snej@14: NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding]; snej@14: NSData *encrypted = [sessionKey encryptData: cleartext]; snej@14: snej@13: Log(@"Wrapping session key %@, %@", sessionKey, sessionKey.keyData); snej@13: NSData *wrapped = [privateKey.publicKey wrapSessionKey: sessionKey]; snej@13: Log(@"Wrapped session key = %u bytes: %@", wrapped.length,wrapped); snej@13: CAssert(wrapped.length >= 128/8); snej@13: snej@13: MYSymmetricKey *unwrappedKey = [privateKey unwrapSessionKey: wrapped snej@13: withAlgorithm: kCCAlgorithmAES128 snej@13: sizeInBits: 128]; snej@13: Log(@"Unwrapped session key = %@, %@", unwrappedKey, unwrappedKey.keyData); snej@13: CAssert(unwrappedKey); snej@14: CAssertEq(unwrappedKey.algorithm, sessionKey.algorithm); snej@14: CAssertEq(unwrappedKey.keySizeInBits, sessionKey.keySizeInBits); snej@13: CAssertEqual(unwrappedKey.keyData, sessionKey.keyData); snej@14: snej@14: Log(@"Verifying that unwrapped key works"); snej@14: NSData *decrypted = [unwrappedKey decryptData: encrypted]; snej@14: CAssertEqual(decrypted, cleartext); snej@14: #endif snej@13: } snej@13: snej@13: snej@8: TestCase(MYGenerateKeyPair) { snej@8: RequireTestCase(MYKeychain); snej@8: snej@8: Log(@"Generating key pair..."); snej@11: MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: kTestCaseRSAKeySize]; snej@8: MYPublicKey *publicKey = pair.publicKey; snej@8: Log(@"...created { %@ , %@ }.", pair, publicKey); snej@8: snej@0: @try{ snej@8: TestUseKeyPair(pair); snej@14: TestWrapSessionKey(pair); snej@3: snej@3: [pair setName: @"Test KeyPair Label"]; snej@3: CAssertEqual(pair.name, @"Test KeyPair Label"); snej@3: CAssertEqual(publicKey.name, @"Test KeyPair Label"); snej@3: #if !TARGET_OS_IPHONE snej@3: [pair setComment: @"This key-pair was generated automatically by a test case."]; snej@3: CAssertEqual(pair.comment, @"This key-pair was generated automatically by a test case."); snej@3: CAssertEqual(publicKey.comment, @"This key-pair was generated automatically by a test case."); snej@3: #endif snej@3: [pair setAlias: @"TestCase@mooseyard.com"]; snej@3: CAssertEqual(pair.alias, @"TestCase@mooseyard.com"); snej@3: CAssertEqual(publicKey.alias, @"TestCase@mooseyard.com"); snej@0: snej@0: CAssert([pair removeFromKeychain]); snej@0: Log(@"Removed key-pair."); snej@0: pair = nil; snej@0: snej@0: }@finally { snej@0: if (pair) { snej@0: if ([pair removeFromKeychain]) snej@0: Log(@"Removed key-pair from keychain."); snej@0: else snej@0: Warn(@"Unable to remove test key-pair from keychain"); snej@0: } snej@0: } snej@0: } snej@0: snej@0: snej@12: #if !TARGET_OS_IPHONE snej@8: TestCase(MYUseIdentity) { snej@8: MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"]; snej@8: if (!me) { snej@8: NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects]; snej@8: SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel]; snej@8: [panel setAlternateButtonTitle: @"Cancel"]; snej@8: if ([panel my_runModalForIdentities: idents snej@8: message: @"Choose an identity for the MYEncoder test case:"] snej@8: != NSOKButton) { snej@8: [NSException raise: NSGenericException format: @"User canceled"]; snej@8: } snej@8: me = [panel my_identity]; snej@8: [me makePreferredIdentityForName: @"MYCryptoTest"]; snej@8: } snej@8: CAssert(me,@"No default identity has been set up in the Keychain"); snej@8: TestUseKeyPair(me.privateKey); snej@8: } snej@12: #endif snej@8: snej@0: snej@0: #pragma mark - snej@0: #pragma mark KEYPAIR EXPORT: snej@0: snej@0: snej@0: static void testKeyPairExportWithPrompt(BOOL withPrompt) { snej@0: MYKeychain *keychain = [MYKeychain allKeychains]; snej@0: Log(@"Generating key pair..."); snej@11: MYPrivateKey *pair = [keychain generateRSAKeyPairOfSize: kTestCaseRSAKeySize]; snej@0: CAssert(pair); snej@0: CAssert(pair.keyRef); snej@3: CAssert(pair.publicKey.keyRef); snej@0: Log(@"...created pair."); snej@0: snej@0: @try{ snej@3: NSData *pubKeyData = pair.publicKey.keyData; snej@11: CAssert(pubKeyData.length >= kTestCaseRSAKeySize/8); snej@0: [pair setName: @"Test KeyPair Label"]; snej@0: CAssertEqual(pair.name, @"Test KeyPair Label"); snej@0: #if !TARGET_OS_IPHONE snej@0: [pair setComment: @"This key-pair was generated automatically by a test case."]; snej@0: CAssertEqual(pair.comment, @"This key-pair was generated automatically by a test case."); snej@0: #endif snej@0: [pair setAlias: @"TestCase@mooseyard.com"]; snej@0: CAssertEqual(pair.alias, @"TestCase@mooseyard.com"); snej@0: snej@0: #if !TARGET_OS_IPHONE snej@0: Log(@"Exporting key-pair..."); snej@0: NSString *passphrase = @"passphrase"; snej@0: NSData *privKeyData; snej@0: if (withPrompt) snej@3: privKeyData = [pair exportKey]; snej@0: else snej@3: privKeyData = [pair _exportKeyInFormat: kSecFormatWrappedOpenSSL snej@3: withPEM: YES snej@3: passphrase: passphrase]; snej@0: Log(@"Exported data = %@ (%u bytes)", privKeyData,privKeyData.length); snej@0: CAssert(privKeyData); snej@0: [privKeyData writeToFile: @"ExportedPrivKey" atomically: YES]; snej@0: #endif snej@0: snej@0: // Check key lookup: snej@0: Log(@"Looking up public key of pair in keychain..."); snej@0: MYSHA1Digest *digest = pair.publicKeyDigest; snej@0: MYPublicKey *foundKey = [keychain publicKeyWithDigest: digest]; snej@3: CAssertEqual(foundKey, pair.publicKey); snej@3: CAssert([keychain.enumeratePublicKeys.allObjects containsObject: pair.publicKey]); snej@3: MYPrivateKey *foundPair = [keychain privateKeyWithDigest: digest]; snej@0: CAssertEqual(foundPair, pair); snej@3: CAssert([keychain.enumeratePrivateKeys.allObjects containsObject: pair]); snej@0: snej@0: Log(@"Removing key-pair from keychain..."); snej@0: CAssert([pair removeFromKeychain]); snej@0: pair = nil; snej@0: CAssert([keychain publicKeyWithDigest: digest] == nil); snej@0: snej@0: #if !TARGET_OS_IPHONE snej@0: Log(@"Importing key-pair..."); snej@0: if (withPrompt) { snej@0: pair = [keychain importPublicKey: pubKeyData snej@0: privateKey: privKeyData]; snej@0: } else { snej@3: pair = [[[MYPrivateKey alloc] _initWithKeyData: privKeyData snej@3: publicKeyData: pubKeyData snej@3: forKeychain: keychain.keychainRefOrDefault snej@3: passphrase: passphrase] snej@0: autorelease]; snej@0: } snej@0: CAssert(pair); snej@3: CAssertEqual(pair.publicKey.keyData, pubKeyData); snej@0: #endif snej@0: }@finally { snej@0: if (pair) { snej@0: if ([pair removeFromKeychain]) snej@0: Log(@"Removed key-pair from keychain."); snej@0: else snej@0: Warn(@"Unable to remove test key-pair from keychain"); snej@0: } snej@0: } snej@0: } snej@0: snej@0: TestCase(KeyPairExport) { snej@0: RequireTestCase(MYKeychain); snej@12: RequireTestCase(MYGenerateKeyPair); snej@0: testKeyPairExportWithPrompt(NO); snej@0: } snej@0: snej@0: TestCase(KeyPairExportWithUI) { snej@0: RequireTestCase(KeyPairExport); snej@0: testKeyPairExportWithPrompt(YES); snej@0: } snej@0: snej@0: snej@0: #endif DEBUG snej@0: