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@8: #import "MYCrypto+Cocoa.h"
snej@0: #import "MYCrypto_Private.h"
snej@0: 
snej@0: 
snej@0: #if DEBUG
snej@0: 
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@2: static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits ) {
snej@2:     NSAutoreleasePool *pool = [NSAutoreleasePool new];
snej@2:     Log(@"--- Testing %3u-bit #%i", sizeInBits, (int)algorithm);
snej@2:     // Generate key:
snej@2:     MYSymmetricKey *key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits
snej@2:                                                            algorithm: algorithm];
snej@2:     Log(@"Created %@", key);
snej@2:     CAssert(key);
snej@2:     CAssertEq(key.algorithm, algorithm);
snej@2:     CAssertEq(key.keySizeInBits, sizeInBits);
snej@2: #if !TARGET_OS_IPHONE
snej@2:     CAssert(key.cssmKey != NULL);
snej@2: #endif
snej@2:     
snej@2:     NSData *keyData = key.keyData;
snej@2:     Log(@"Key data = %@", keyData);
snej@2:     CAssertEq(keyData.length, sizeInBits/8);
snej@2:     
snej@2:     // Encrypt a small amount of text:
snej@2:     NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
snej@2:     NSData *encrypted = [key encryptData: cleartext];
snej@2:     Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
snej@2:     CAssert(encrypted.length >= cleartext.length);
snej@2:     NSData *decrypted = [key decryptData: encrypted];
snej@2:     CAssertEqual(decrypted, cleartext);
snej@2:     
snej@2:     // Encrypt large binary data:
snej@2:     cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
snej@2:     CAssert(cleartext);
snej@2:     encrypted = [key encryptData: cleartext];
snej@2:     Log(@"Encrypted = %u bytes", encrypted.length);
snej@2:     CAssert(encrypted.length >= cleartext.length);
snej@2:     decrypted = [key decryptData: encrypted];
snej@2:     CAssertEqual(decrypted, cleartext);
snej@2:     
snej@2: #if !TARGET_OS_IPHONE
snej@2:     // Try reconstituting the key from its data:
snej@2:     NSData *exported = [key exportKeyInFormat: kSecFormatWrappedPKCS8 withPEM: NO];
snej@2:     Log(@"Exported key: %@", exported);
snej@2:     // CAssert(exported);
snej@2:     //FIX: Exporting symmetric keys isn't working. Temporarily making this optional.
snej@2:     if (exported) {
snej@2:         CAssert(exported);
snej@2:         MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: exported algorithm: algorithm];
snej@2:         Log(@"Reconstituted as %@", key2);
snej@2:         CAssertEqual(key2,key);
snej@2:         decrypted = [key2 decryptData: encrypted];
snej@2:         CAssertEqual(decrypted, cleartext);
snej@2:     } else
snej@2:         Warn(@"Unable to export key in PKCS8");
snej@2: #endif
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@2:     for (int i=0; i<kNTests; i++) 
snej@2:         testSymmetricKey(kTestAlgorithms[i], kTestBitSizes[i]);
snej@0: }
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@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@8:     NSData *crypted = [publicKey encryptData: data];
snej@8:     Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
snej@8:     CAssert(crypted);
snej@8:     CAssertEqual([pair decryptData: 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@8: TestCase(MYGenerateKeyPair) {
snej@8:     RequireTestCase(MYKeychain);
snej@8:     
snej@8:     Log(@"Generating key pair...");
snej@8:     MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
snej@8:     MYPublicKey *publicKey = pair.publicKey;
snej@8:     Log(@"...created { %@ , %@ }.", pair, publicKey);
snej@8:     
snej@0:     @try{
snej@8:         TestUseKeyPair(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@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@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@3:     MYPrivateKey *pair = [keychain generateRSAKeyPairOfSize: 512];
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@0:         CAssert(pubKeyData.length >= 512/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@3:     RequireTestCase(MYPrivateKey);
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: