MYCryptoTest.m
author Jens Alfke <jens@mooseyard.com>
Thu Jun 04 18:36:30 2009 -0700 (2009-06-04)
changeset 19 f6c91b9da05b
parent 14 3af1d1c0ceb5
child 21 2c300b15b381
permissions -rw-r--r--
Whew! MYParsedCertificate can now generate certs from scratch. Also added improvements and fixes to the BER/DER codecs.
     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 #if !TARGET_OS_IPHONE
    15 #import "MYCrypto+Cocoa.h"
    16 #endif
    17 #import "MYCrypto_Private.h"
    18 
    19 
    20 #if DEBUG
    21 
    22 
    23 #define kTestCaseRSAKeySize 2048
    24 
    25 #pragma mark -
    26 #pragma mark KEYCHAIN:
    27 
    28 
    29 TestCase(MYKeychain) {
    30     MYKeychain *kc = [MYKeychain defaultKeychain];
    31     Log(@"Default keychain = %@", kc);
    32     CAssert(kc);
    33 #if !MYCRYPTO_USE_IPHONE_API
    34     CAssert(kc.path);
    35 #endif
    36     
    37     kc = [MYKeychain allKeychains];
    38     Log(@"All-keychains = %@", kc);
    39     CAssert(kc);
    40 #if !MYCRYPTO_USE_IPHONE_API
    41     CAssertEq(kc.path,nil);
    42 #endif
    43 }
    44 
    45 
    46 TestCase(EnumerateKeys) {
    47     RequireTestCase(MYKeychain);
    48     NSEnumerator *e = [[MYKeychain allKeychains] enumeratePublicKeys];
    49     Log(@"Public Key Enumerator = %@", e);
    50     CAssert(e);
    51     for (MYPublicKey *key in e) {
    52         Log(@"Found %@ -- name=%@", key, key.name);
    53     }
    54     
    55     e = [[MYKeychain allKeychains] enumeratePrivateKeys];
    56     Log(@"Key-Pair Enumerator = %@", e);
    57     CAssert(e);
    58     for (MYPrivateKey *key in e) {
    59         Log(@"Found %@ -- name=%@", key, key.name);
    60     }
    61     
    62     e = [[MYKeychain allKeychains] enumerateSymmetricKeys];
    63     Log(@"Symmetric Key Enumerator = %@", e);
    64     CAssert(e);
    65     for (MYSymmetricKey *key in e) {
    66         Log(@"Found %@ -- name=%@", key, key.name);
    67     }
    68 }
    69 
    70 
    71 TestCase(EnumerateCerts) {
    72     RequireTestCase(MYKeychain);
    73     NSEnumerator *e = [[MYKeychain allKeychains] enumerateCertificates];
    74     Log(@"Enumerator = %@", e);
    75     CAssert(e);
    76     for (MYCertificate *cert in e) {
    77         //Log(@"Found %@ -- name=%@, email=%@", cert, cert.commonName, cert.emailAddresses);
    78     }
    79 }
    80 
    81 TestCase(EnumerateIdentities) {
    82     RequireTestCase(MYKeychain);
    83     NSEnumerator *e = [[MYKeychain allKeychains] enumerateIdentities];
    84     Log(@"Enumerator = %@", e);
    85     CAssert(e);
    86     for (MYIdentity *ident in e) {
    87         Log(@"Found %@\n\tcommonName=%@\n\temails=(%@)\n\tkey=%@",
    88             ident, ident.commonName, 
    89 #if TARGET_OS_IPHONE
    90             nil,
    91 #else
    92             [ident.emailAddresses componentsJoinedByString: @", "],
    93 #endif
    94             ident.privateKey);
    95     }
    96 }
    97 
    98 
    99 #pragma mark -
   100 #pragma mark SYMMETRIC KEYS:
   101 
   102 
   103 static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits, MYKeychain *inKeychain ) {
   104     NSAutoreleasePool *pool = [NSAutoreleasePool new];
   105     MYSymmetricKey *key = nil;
   106     @try{
   107         Log(@"--- Testing %3u-bit #%i %s", sizeInBits, (int)algorithm,
   108             (inKeychain ?", in keychain" :""));
   109         // Generate key:
   110         if (inKeychain)
   111             key = [inKeychain generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm];
   112         else
   113             key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm];
   114         Log(@"Created %@", key);
   115     CAssert(key);
   116         CAssertEq(key.algorithm, algorithm);
   117         CAssertEq(key.keySizeInBits, sizeInBits);
   118     #if !TARGET_OS_IPHONE
   119         CAssert(key.cssmKey != NULL);
   120     #endif
   121         
   122         NSData *keyData = key.keyData;
   123         Log(@"Key data = %@", keyData);
   124         CAssertEq(keyData.length, sizeInBits/8);
   125         
   126         // Encrypt a small amount of text:
   127         Log(@"Testing encryption / decryption ...");
   128         NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
   129         NSData *encrypted = [key encryptData: cleartext];
   130         Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
   131         CAssert(encrypted.length >= cleartext.length);
   132         NSData *decrypted = [key decryptData: encrypted];
   133         CAssertEqual(decrypted, cleartext);
   134         
   135         // Encrypt large binary data:
   136         cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
   137         CAssert(cleartext);
   138         encrypted = [key encryptData: cleartext];
   139         Log(@"Encrypted = %u bytes", encrypted.length);
   140         CAssert(encrypted.length >= cleartext.length);
   141         decrypted = [key decryptData: encrypted];
   142         CAssertEqual(decrypted, cleartext);
   143         
   144     #if 1
   145         Log(@"Testing initWithKeyData:...");
   146         MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: keyData algorithm: algorithm];
   147         CAssert(key2);
   148         Log(@"Key from data = %@",key2);
   149         CAssertEqual(key2.keyData, keyData);
   150         CAssertEq(key2.algorithm, algorithm);
   151         CAssertEq(key2.keySizeInBits, sizeInBits);
   152         decrypted = [key2 decryptData: encrypted];
   153         CAssertEqual(decrypted, cleartext);
   154         [key2 release];
   155     #endif
   156 
   157     #if !TARGET_OS_IPHONE
   158 #if 1 // TEMP-ORARILY OUT OF ORDER
   159         // Try exporting and importing a wrapped key:
   160         Log(@"Testing export/import...");
   161         NSData *exported = [key exportWrappedKeyWithPassphrasePrompt: @"Export symmetric key with passphrase:"];
   162         Log(@"Exported key: %@", exported);
   163     #if 1
   164         CAssert(exported);
   165     #else
   166         if (!exported)
   167             Warn(@"Unable to export wrapped key");
   168         else
   169     #endif
   170         {
   171             CAssert(exported);
   172             MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithWrappedKeyData: exported];
   173             Log(@"Reconstituted as %@", key2);
   174             CAssertEqual(key2.keyData,key.keyData);
   175             decrypted = [key2 decryptData: encrypted];
   176             CAssertEqual(decrypted, cleartext);
   177         }
   178 #endif 0
   179     #endif
   180     }@finally{
   181         [key removeFromKeychain];
   182     }
   183     [pool drain];
   184 }
   185 
   186 
   187 TestCase(MYSymmetricKey) {
   188     #define kNTests 11
   189     static const CCAlgorithm kTestAlgorithms[kNTests] = {
   190         kCCAlgorithmAES128, kCCAlgorithmAES128, kCCAlgorithmAES128,
   191         kCCAlgorithmDES, kCCAlgorithm3DES,
   192         kCCAlgorithmCAST, kCCAlgorithmCAST, kCCAlgorithmCAST,
   193         kCCAlgorithmRC4, kCCAlgorithmRC4, kCCAlgorithmRC4};
   194     
   195     static const unsigned kTestBitSizes[kNTests] = {
   196         128, 192, 256,
   197         64, 3*64,
   198         40, 80, 128,
   199         32, 200, 512*8};
   200 
   201     for (int useKeychain=0; useKeychain<=1; useKeychain++)
   202         for (int testNo=0; testNo<kNTests; testNo++) 
   203             testSymmetricKey(kTestAlgorithms[testNo], 
   204                              kTestBitSizes[testNo],
   205                              useKeychain ?[MYKeychain defaultKeychain] :nil);
   206 }
   207 
   208 
   209 #if !TARGET_OS_IPHONE
   210 TestCase(MYSymmetricKeyPassphrase) {
   211     Log(@"Prompting for raw passphrase --");
   212     NSString *rawPassphrase = [MYSymmetricKey promptForPassphraseWithAlertTitle: @"Raw Passphrase Test" 
   213                                                                     alertPrompt: @"Enter the passphrase 'Testing':"
   214                                                                        creating: YES];
   215     Log(@"You entered: '%@'", rawPassphrase);
   216     CAssertEqual(rawPassphrase, @"Testing");
   217     
   218     Log(@"Prompting for passphrase for key --");
   219     MYSymmetricKey *key = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Symmetric Key Passphrase Test Case" 
   220                                                                        alertPrompt: @"Please enter a passphrase to generate a key:"
   221                                                                           creating: YES
   222                                                                               salt: @"wahooma"];
   223     Log(@"Key from passphrase = %@", key);
   224     CAssert(key);
   225 
   226     // Encrypt a small amount of text:
   227     Log(@"Testing encryption / decryption ...");
   228     NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
   229     NSData *encrypted = [key encryptData: cleartext];
   230     Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
   231     CAssert(encrypted.length >= cleartext.length);
   232     NSData *decrypted = [key decryptData: encrypted];
   233     CAssertEqual(decrypted, cleartext);
   234     
   235     // Now test decryption by re-entered passphrase:
   236     Log(@"Testing decryption using re-entered passphrase...");
   237     MYSymmetricKey *key2 = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Symmetric Key Passphrase Test Case" 
   238                                                                         alertPrompt: @"Please re-enter the same passphrase:" 
   239                                                                            creating: NO
   240                                                                                salt: @"wahooma"];
   241     Log(@"Key from passphrase = %@", key2);
   242     CAssert(key2);
   243     decrypted = [key2 decryptData: encrypted];
   244     CAssertEqual(decrypted, cleartext);
   245 }
   246 #endif
   247 
   248 
   249 #pragma mark -
   250 #pragma mark KEY-PAIRS:
   251 
   252 
   253 static void TestUseKeyPair(MYPrivateKey *pair) {
   254     Log(@"---- TestUseKeyPair { %@ , %@ }.", pair, pair.publicKey);
   255     CAssert(pair);
   256     CAssert(pair.keyRef);
   257     MYPublicKey *publicKey = pair.publicKey;
   258     CAssert(publicKey.keyRef);
   259     
   260     NSData *pubKeyData = publicKey.keyData;
   261     Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
   262     CAssert(pubKeyData);
   263     
   264     MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
   265     Log(@"Public key digest = %@",pubKeyDigest);
   266     CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
   267     
   268     Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
   269     CAssertEqual(pubKeyData.my_SHA1Digest,pubKeyDigest);
   270     
   271     // Let's sign data:
   272     NSData *data = [@"This is a test. This is only a test!" dataUsingEncoding: NSUTF8StringEncoding];
   273     NSData *sig = [pair signData: data];
   274     Log(@"Signature = %@ (%u bytes)",sig,sig.length);
   275     CAssert(sig);
   276     CAssert( [publicKey verifySignature: sig ofData: data] );
   277     
   278     // Now let's encrypt...
   279     NSData *crypted = [publicKey rawEncryptData: data];
   280     Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
   281     CAssert(crypted);
   282     CAssertEqual([pair rawDecryptData: crypted], data);
   283     Log(@"Verified decryption.");
   284     
   285     // Test creating a standalone public key:
   286     MYPublicKey *pub = [[MYPublicKey alloc] initWithKeyRef: publicKey.keyRef];
   287     CAssert( [pub verifySignature: sig ofData: data] );
   288     Log(@"Verified signature.");
   289     
   290     // Test creating a public key from data:
   291     Log(@"Reconstituting public key from data...");
   292     pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
   293     CAssert(pub);
   294     CAssertEqual(pub.keyData, pubKeyData);
   295     CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
   296     CAssert( [pub verifySignature: sig ofData: data] );
   297     Log(@"Verified signature from reconstituted key.");
   298 }
   299 
   300 
   301 static void TestWrapSessionKey( MYPrivateKey *privateKey ) {
   302 #if !TARGET_OS_IPHONE
   303     MYSymmetricKey *sessionKey = [MYSymmetricKey generateSymmetricKeyOfSize: 128 algorithm:kCCAlgorithmAES128];
   304     CAssert(sessionKey);
   305     NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
   306     NSData *encrypted = [sessionKey encryptData: cleartext];
   307 
   308     Log(@"Wrapping session key %@, %@", sessionKey, sessionKey.keyData);
   309     NSData *wrapped = [privateKey.publicKey wrapSessionKey: sessionKey];
   310     Log(@"Wrapped session key = %u bytes: %@", wrapped.length,wrapped);
   311     CAssert(wrapped.length >= 128/8);
   312     
   313     MYSymmetricKey *unwrappedKey = [privateKey unwrapSessionKey: wrapped
   314                                                   withAlgorithm: kCCAlgorithmAES128
   315                                                      sizeInBits: 128];
   316     Log(@"Unwrapped session key = %@, %@", unwrappedKey, unwrappedKey.keyData);
   317     CAssert(unwrappedKey);
   318     CAssertEq(unwrappedKey.algorithm, sessionKey.algorithm);
   319     CAssertEq(unwrappedKey.keySizeInBits, sessionKey.keySizeInBits);
   320     CAssertEqual(unwrappedKey.keyData, sessionKey.keyData);
   321 
   322     Log(@"Verifying that unwrapped key works");
   323     NSData *decrypted = [unwrappedKey decryptData: encrypted];
   324     CAssertEqual(decrypted, cleartext);
   325 #endif
   326 }
   327 
   328 
   329 TestCase(MYGenerateKeyPair) {
   330     RequireTestCase(MYKeychain);
   331     
   332     Log(@"Generating key pair...");
   333     MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: kTestCaseRSAKeySize];
   334     MYPublicKey *publicKey = pair.publicKey;
   335     Log(@"...created { %@ , %@ }.", pair, publicKey);
   336     
   337     @try{
   338         TestUseKeyPair(pair);
   339         TestWrapSessionKey(pair);
   340         
   341         [pair setName: @"Test KeyPair Label"];
   342         CAssertEqual(pair.name, @"Test KeyPair Label");
   343         CAssertEqual(publicKey.name, @"Test KeyPair Label");
   344 #if !TARGET_OS_IPHONE
   345         [pair setComment: @"This key-pair was generated automatically by a test case."];
   346         CAssertEqual(pair.comment, @"This key-pair was generated automatically by a test case.");
   347         CAssertEqual(publicKey.comment, @"This key-pair was generated automatically by a test case.");
   348 #endif
   349         [pair setAlias: @"TestCase@mooseyard.com"];
   350         CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
   351         CAssertEqual(publicKey.alias, @"TestCase@mooseyard.com");
   352         
   353         CAssert([pair removeFromKeychain]);
   354         Log(@"Removed key-pair.");
   355         pair = nil;
   356         
   357     }@finally {
   358         if (pair) {
   359             if ([pair removeFromKeychain])
   360                 Log(@"Removed key-pair from keychain.");
   361             else
   362                 Warn(@"Unable to remove test key-pair from keychain");
   363         }
   364     }
   365 }
   366 
   367 
   368 #if !TARGET_OS_IPHONE
   369 TestCase(MYUseIdentity) {
   370     MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
   371     if (!me) {
   372         NSArray *idents = [[[MYKeychain allKeychains] enumerateIdentities] allObjects];
   373         SFChooseIdentityPanel *panel = [SFChooseIdentityPanel sharedChooseIdentityPanel];
   374         [panel setAlternateButtonTitle: @"Cancel"];
   375         if ([panel my_runModalForIdentities: idents 
   376                                     message: @"Choose an identity for the MYEncoder test case:"]
   377             != NSOKButton) {
   378             [NSException raise: NSGenericException format: @"User canceled"];
   379         }
   380         me = [panel my_identity];
   381         [me makePreferredIdentityForName: @"MYCryptoTest"];
   382     }
   383     CAssert(me,@"No default identity has been set up in the Keychain");
   384     TestUseKeyPair(me.privateKey);
   385 }
   386 #endif
   387 
   388 
   389 #pragma mark -
   390 #pragma mark KEYPAIR EXPORT:
   391 
   392 
   393 static void testKeyPairExportWithPrompt(BOOL withPrompt) {
   394     MYKeychain *keychain = [MYKeychain allKeychains];
   395     Log(@"Generating key pair...");
   396     MYPrivateKey *pair = [keychain generateRSAKeyPairOfSize: kTestCaseRSAKeySize];
   397     CAssert(pair);
   398     CAssert(pair.keyRef);
   399     CAssert(pair.publicKey.keyRef);
   400     Log(@"...created pair.");
   401     
   402     @try{
   403         NSData *pubKeyData = pair.publicKey.keyData;
   404         CAssert(pubKeyData.length >= kTestCaseRSAKeySize/8);
   405         [pair setName: @"Test KeyPair Label"];
   406         CAssertEqual(pair.name, @"Test KeyPair Label");
   407 #if !TARGET_OS_IPHONE
   408         [pair setComment: @"This key-pair was generated automatically by a test case."];
   409         CAssertEqual(pair.comment, @"This key-pair was generated automatically by a test case.");
   410 #endif
   411         [pair setAlias: @"TestCase@mooseyard.com"];
   412         CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
   413         
   414 #if !TARGET_OS_IPHONE
   415         Log(@"Exporting key-pair...");
   416         NSString *passphrase = @"passphrase";
   417         NSData *privKeyData;
   418         if (withPrompt)
   419             privKeyData = [pair exportKey];
   420         else
   421             privKeyData = [pair _exportKeyInFormat: kSecFormatWrappedOpenSSL
   422                                           withPEM: YES
   423                                        passphrase: passphrase];
   424         Log(@"Exported data = %@ (%u bytes)", privKeyData,privKeyData.length);
   425         CAssert(privKeyData);
   426         [privKeyData writeToFile: @"ExportedPrivKey" atomically: YES];
   427 #endif
   428         
   429         // Check key lookup:
   430         Log(@"Looking up public key of pair in keychain...");
   431         MYSHA1Digest *digest = pair.publicKeyDigest;
   432         MYPublicKey *foundKey = [keychain publicKeyWithDigest: digest];
   433         CAssertEqual(foundKey, pair.publicKey);
   434         CAssert([keychain.enumeratePublicKeys.allObjects containsObject: pair.publicKey]);
   435         MYPrivateKey *foundPair = [keychain privateKeyWithDigest: digest];
   436         CAssertEqual(foundPair, pair);
   437         CAssert([keychain.enumeratePrivateKeys.allObjects containsObject: pair]);
   438         
   439         Log(@"Removing key-pair from keychain...");
   440         CAssert([pair removeFromKeychain]);
   441         pair = nil;
   442         CAssert([keychain publicKeyWithDigest: digest] == nil);
   443         
   444 #if !TARGET_OS_IPHONE
   445         Log(@"Importing key-pair...");
   446         if (withPrompt) {
   447             pair = [keychain importPublicKey: pubKeyData 
   448                                   privateKey: privKeyData];
   449         } else {
   450             pair = [[[MYPrivateKey alloc] _initWithKeyData: privKeyData
   451                                              publicKeyData: pubKeyData
   452                                                forKeychain: keychain.keychainRefOrDefault
   453                                                 passphrase: passphrase]
   454                     autorelease];
   455         }
   456         CAssert(pair);
   457         CAssertEqual(pair.publicKey.keyData, pubKeyData);
   458 #endif
   459     }@finally {
   460         if (pair) {
   461             if ([pair removeFromKeychain])
   462                 Log(@"Removed key-pair from keychain.");
   463             else
   464                 Warn(@"Unable to remove test key-pair from keychain");
   465         }
   466     }
   467 }
   468 
   469 TestCase(KeyPairExport) {
   470     RequireTestCase(MYKeychain);
   471     RequireTestCase(MYGenerateKeyPair);
   472     testKeyPairExportWithPrompt(NO);
   473 }
   474 
   475 TestCase(KeyPairExportWithUI) {
   476     RequireTestCase(KeyPairExport);
   477     testKeyPairExportWithPrompt(YES);
   478 }
   479 
   480 
   481 #endif DEBUG
   482