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