MYCryptoTest.m
author snej@snej.local
Sun Apr 12 22:16:14 2009 -0700 (2009-04-12)
changeset 9 aa5eb3fd6ebf
parent 5 b2e360b78189
child 11 3568d5fd4b6a
permissions -rw-r--r--
Doc touch-up
     1 //
     2 //  MYCryptoTest.m
     3 //  MYCrypto-iPhone
     4 //
     5 //  Created by Jens Alfke on 4/1/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYPublicKey.h"
    10 #import "MYPrivateKey.h"
    11 #import "MYKeychain.h"
    12 #import "MYDigest.h"
    13 #import "MYIdentity.h"
    14 #import "MYCrypto+Cocoa.h"
    15 #import "MYCrypto_Private.h"
    16 
    17 
    18 #if DEBUG
    19 
    20 #pragma mark -
    21 #pragma mark KEYCHAIN:
    22 
    23 
    24 TestCase(MYKeychain) {
    25     MYKeychain *kc = [MYKeychain defaultKeychain];
    26     Log(@"Default keychain = %@", kc);
    27     CAssert(kc);
    28 #if !MYCRYPTO_USE_IPHONE_API
    29     CAssert(kc.path);
    30 #endif
    31     
    32     kc = [MYKeychain allKeychains];
    33     Log(@"All-keychains = %@", kc);
    34     CAssert(kc);
    35 #if !MYCRYPTO_USE_IPHONE_API
    36     CAssertEq(kc.path,nil);
    37 #endif
    38 }
    39 
    40 
    41 TestCase(EnumerateKeys) {
    42     RequireTestCase(MYKeychain);
    43     NSEnumerator *e = [[MYKeychain allKeychains] enumeratePublicKeys];
    44     Log(@"Public Key Enumerator = %@", e);
    45     CAssert(e);
    46     for (MYPublicKey *key in e) {
    47         Log(@"Found %@ -- name=%@", key, key.name);
    48     }
    49     
    50     e = [[MYKeychain allKeychains] enumeratePrivateKeys];
    51     Log(@"Key-Pair Enumerator = %@", e);
    52     CAssert(e);
    53     for (MYPrivateKey *key in e) {
    54         Log(@"Found %@ -- name=%@", key, key.name);
    55     }
    56     
    57     e = [[MYKeychain allKeychains] enumerateSymmetricKeys];
    58     Log(@"Symmetric Key Enumerator = %@", e);
    59     CAssert(e);
    60     for (MYSymmetricKey *key in e) {
    61         Log(@"Found %@ -- name=%@", key, key.name);
    62     }
    63 }
    64 
    65 
    66 TestCase(EnumerateCerts) {
    67     RequireTestCase(MYKeychain);
    68     NSEnumerator *e = [[MYKeychain allKeychains] enumerateCertificates];
    69     Log(@"Enumerator = %@", e);
    70     CAssert(e);
    71     for (MYCertificate *cert in e) {
    72         //Log(@"Found %@ -- name=%@, email=%@", cert, cert.commonName, cert.emailAddresses);
    73     }
    74 }
    75 
    76 TestCase(EnumerateIdentities) {
    77     RequireTestCase(MYKeychain);
    78     NSEnumerator *e = [[MYKeychain allKeychains] enumerateIdentities];
    79     Log(@"Enumerator = %@", e);
    80     CAssert(e);
    81     for (MYIdentity *ident in e) {
    82         Log(@"Found %@\n\tcommonName=%@\n\temails=(%@)\n\tkey=%@",
    83             ident, ident.commonName, 
    84 #if TARGET_OS_IPHONE
    85             nil,
    86 #else
    87             [ident.emailAddresses componentsJoinedByString: @", "],
    88 #endif
    89             ident.privateKey);
    90     }
    91 }
    92 
    93 
    94 #pragma mark -
    95 #pragma mark SYMMETRIC KEYS:
    96 
    97 
    98 static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits ) {
    99     NSAutoreleasePool *pool = [NSAutoreleasePool new];
   100     Log(@"--- Testing %3u-bit #%i", sizeInBits, (int)algorithm);
   101     // Generate key:
   102     MYSymmetricKey *key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits
   103                                                            algorithm: algorithm];
   104     Log(@"Created %@", key);
   105     CAssert(key);
   106     CAssertEq(key.algorithm, algorithm);
   107     CAssertEq(key.keySizeInBits, sizeInBits);
   108 #if !TARGET_OS_IPHONE
   109     CAssert(key.cssmKey != NULL);
   110 #endif
   111     
   112     NSData *keyData = key.keyData;
   113     Log(@"Key data = %@", keyData);
   114     CAssertEq(keyData.length, sizeInBits/8);
   115     
   116     // Encrypt a small amount of text:
   117     NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
   118     NSData *encrypted = [key encryptData: cleartext];
   119     Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
   120     CAssert(encrypted.length >= cleartext.length);
   121     NSData *decrypted = [key decryptData: encrypted];
   122     CAssertEqual(decrypted, cleartext);
   123     
   124     // Encrypt large binary data:
   125     cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
   126     CAssert(cleartext);
   127     encrypted = [key encryptData: cleartext];
   128     Log(@"Encrypted = %u bytes", encrypted.length);
   129     CAssert(encrypted.length >= cleartext.length);
   130     decrypted = [key decryptData: encrypted];
   131     CAssertEqual(decrypted, cleartext);
   132     
   133 #if !TARGET_OS_IPHONE
   134     // Try reconstituting the key from its data:
   135     NSData *exported = [key exportKeyInFormat: kSecFormatWrappedPKCS8 withPEM: NO];
   136     Log(@"Exported key: %@", exported);
   137     // CAssert(exported);
   138     //FIX: Exporting symmetric keys isn't working. Temporarily making this optional.
   139     if (exported) {
   140         CAssert(exported);
   141         MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: exported algorithm: algorithm];
   142         Log(@"Reconstituted as %@", key2);
   143         CAssertEqual(key2,key);
   144         decrypted = [key2 decryptData: encrypted];
   145         CAssertEqual(decrypted, cleartext);
   146     } else
   147         Warn(@"Unable to export key in PKCS8");
   148 #endif
   149     [pool drain];
   150 }
   151 
   152 
   153 TestCase(MYSymmetricKey) {
   154     #define kNTests 11
   155     static const CCAlgorithm kTestAlgorithms[kNTests] = {
   156         kCCAlgorithmAES128, kCCAlgorithmAES128, kCCAlgorithmAES128,
   157         kCCAlgorithmDES, kCCAlgorithm3DES,
   158         kCCAlgorithmCAST, kCCAlgorithmCAST, kCCAlgorithmCAST,
   159         kCCAlgorithmRC4, kCCAlgorithmRC4, kCCAlgorithmRC4};
   160     
   161     static const unsigned kTestBitSizes[kNTests] = {
   162         128, 192, 256,
   163         64, 3*64,
   164         40, 80, 128,
   165         32, 200, 512*8};
   166 
   167     for (int i=0; i<kNTests; i++) 
   168         testSymmetricKey(kTestAlgorithms[i], kTestBitSizes[i]);
   169 }
   170 
   171 
   172 #pragma mark -
   173 #pragma mark KEY-PAIRS:
   174 
   175 
   176 static void TestUseKeyPair(MYPrivateKey *pair) {
   177     Log(@"---- TestUseKeyPair { %@ , %@ }.", pair, pair.publicKey);
   178     CAssert(pair);
   179     CAssert(pair.keyRef);
   180     MYPublicKey *publicKey = pair.publicKey;
   181     CAssert(publicKey.keyRef);
   182     
   183     NSData *pubKeyData = publicKey.keyData;
   184     Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
   185     CAssert(pubKeyData);
   186     
   187     MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
   188     Log(@"Public key digest = %@",pubKeyDigest);
   189     CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
   190     
   191     Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
   192     
   193     // Let's sign data:
   194     NSData *data = [@"This is a test. This is only a test!" dataUsingEncoding: NSUTF8StringEncoding];
   195     NSData *sig = [pair signData: data];
   196     Log(@"Signature = %@ (%u bytes)",sig,sig.length);
   197     CAssert(sig);
   198     CAssert( [publicKey verifySignature: sig ofData: data] );
   199     
   200     // Now let's encrypt...
   201     NSData *crypted = [publicKey encryptData: data];
   202     Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
   203     CAssert(crypted);
   204     CAssertEqual([pair decryptData: crypted], data);
   205     Log(@"Verified decryption.");
   206     
   207     // Test creating a standalone public key:
   208     MYPublicKey *pub = [[MYPublicKey alloc] initWithKeyRef: publicKey.keyRef];
   209     CAssert( [pub verifySignature: sig ofData: data] );
   210     Log(@"Verified signature.");
   211     
   212     // Test creating a public key from data:
   213     Log(@"Reconstituting public key from data...");
   214     pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
   215     CAssert(pub);
   216     CAssertEqual(pub.keyData, pubKeyData);
   217     CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
   218     CAssert( [pub verifySignature: sig ofData: data] );
   219     Log(@"Verified signature from reconstituted key.");
   220 }
   221 
   222 
   223 TestCase(MYGenerateKeyPair) {
   224     RequireTestCase(MYKeychain);
   225     
   226     Log(@"Generating key pair...");
   227     MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
   228     MYPublicKey *publicKey = pair.publicKey;
   229     Log(@"...created { %@ , %@ }.", pair, publicKey);
   230     
   231     @try{
   232         TestUseKeyPair(pair);
   233         
   234         [pair setName: @"Test KeyPair Label"];
   235         CAssertEqual(pair.name, @"Test KeyPair Label");
   236         CAssertEqual(publicKey.name, @"Test KeyPair Label");
   237 #if !TARGET_OS_IPHONE
   238         [pair setComment: @"This key-pair was generated automatically by a test case."];
   239         CAssertEqual(pair.comment, @"This key-pair was generated automatically by a test case.");
   240         CAssertEqual(publicKey.comment, @"This key-pair was generated automatically by a test case.");
   241 #endif
   242         [pair setAlias: @"TestCase@mooseyard.com"];
   243         CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
   244         CAssertEqual(publicKey.alias, @"TestCase@mooseyard.com");
   245         
   246         CAssert([pair removeFromKeychain]);
   247         Log(@"Removed key-pair.");
   248         pair = nil;
   249         
   250     }@finally {
   251         if (pair) {
   252             if ([pair removeFromKeychain])
   253                 Log(@"Removed key-pair from keychain.");
   254             else
   255                 Warn(@"Unable to remove test key-pair from keychain");
   256         }
   257     }
   258 }
   259 
   260 
   261 TestCase(MYUseIdentity) {
   262     MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
   263     if (!me) {
   264         NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects];
   265         SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
   266         [panel setAlternateButtonTitle: @"Cancel"];
   267         if ([panel my_runModalForIdentities: idents 
   268                                     message: @"Choose an identity for the MYEncoder test case:"]
   269             != NSOKButton) {
   270             [NSException raise: NSGenericException format: @"User canceled"];
   271         }
   272         me = [panel my_identity];
   273         [me makePreferredIdentityForName: @"MYCryptoTest"];
   274     }
   275     CAssert(me,@"No default identity has been set up in the Keychain");
   276     TestUseKeyPair(me.privateKey);
   277 }
   278 
   279 
   280 #pragma mark -
   281 #pragma mark KEYPAIR EXPORT:
   282 
   283 
   284 static void testKeyPairExportWithPrompt(BOOL withPrompt) {
   285     MYKeychain *keychain = [MYKeychain allKeychains];
   286     Log(@"Generating key pair...");
   287     MYPrivateKey *pair = [keychain generateRSAKeyPairOfSize: 512];
   288     CAssert(pair);
   289     CAssert(pair.keyRef);
   290     CAssert(pair.publicKey.keyRef);
   291     Log(@"...created pair.");
   292     
   293     @try{
   294         NSData *pubKeyData = pair.publicKey.keyData;
   295         CAssert(pubKeyData.length >= 512/8);
   296         [pair setName: @"Test KeyPair Label"];
   297         CAssertEqual(pair.name, @"Test KeyPair Label");
   298 #if !TARGET_OS_IPHONE
   299         [pair setComment: @"This key-pair was generated automatically by a test case."];
   300         CAssertEqual(pair.comment, @"This key-pair was generated automatically by a test case.");
   301 #endif
   302         [pair setAlias: @"TestCase@mooseyard.com"];
   303         CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
   304         
   305 #if !TARGET_OS_IPHONE
   306         Log(@"Exporting key-pair...");
   307         NSString *passphrase = @"passphrase";
   308         NSData *privKeyData;
   309         if (withPrompt)
   310             privKeyData = [pair exportKey];
   311         else
   312             privKeyData = [pair _exportKeyInFormat: kSecFormatWrappedOpenSSL
   313                                           withPEM: YES
   314                                        passphrase: passphrase];
   315         Log(@"Exported data = %@ (%u bytes)", privKeyData,privKeyData.length);
   316         CAssert(privKeyData);
   317         [privKeyData writeToFile: @"ExportedPrivKey" atomically: YES];
   318 #endif
   319         
   320         // Check key lookup:
   321         Log(@"Looking up public key of pair in keychain...");
   322         MYSHA1Digest *digest = pair.publicKeyDigest;
   323         MYPublicKey *foundKey = [keychain publicKeyWithDigest: digest];
   324         CAssertEqual(foundKey, pair.publicKey);
   325         CAssert([keychain.enumeratePublicKeys.allObjects containsObject: pair.publicKey]);
   326         MYPrivateKey *foundPair = [keychain privateKeyWithDigest: digest];
   327         CAssertEqual(foundPair, pair);
   328         CAssert([keychain.enumeratePrivateKeys.allObjects containsObject: pair]);
   329         
   330         Log(@"Removing key-pair from keychain...");
   331         CAssert([pair removeFromKeychain]);
   332         pair = nil;
   333         CAssert([keychain publicKeyWithDigest: digest] == nil);
   334         
   335 #if !TARGET_OS_IPHONE
   336         Log(@"Importing key-pair...");
   337         if (withPrompt) {
   338             pair = [keychain importPublicKey: pubKeyData 
   339                                   privateKey: privKeyData];
   340         } else {
   341             pair = [[[MYPrivateKey alloc] _initWithKeyData: privKeyData
   342                                              publicKeyData: pubKeyData
   343                                                forKeychain: keychain.keychainRefOrDefault
   344                                                 passphrase: passphrase]
   345                     autorelease];
   346         }
   347         CAssert(pair);
   348         CAssertEqual(pair.publicKey.keyData, pubKeyData);
   349 #endif
   350     }@finally {
   351         if (pair) {
   352             if ([pair removeFromKeychain])
   353                 Log(@"Removed key-pair from keychain.");
   354             else
   355                 Warn(@"Unable to remove test key-pair from keychain");
   356         }
   357     }
   358 }
   359 
   360 TestCase(KeyPairExport) {
   361     RequireTestCase(MYKeychain);
   362     RequireTestCase(MYPrivateKey);
   363     testKeyPairExportWithPrompt(NO);
   364 }
   365 
   366 TestCase(KeyPairExportWithUI) {
   367     RequireTestCase(KeyPairExport);
   368     testKeyPairExportWithPrompt(YES);
   369 }
   370 
   371 
   372 #endif DEBUG
   373