Whew! MYParsedCertificate can now generate certs from scratch. Also added improvements and fixes to the BER/DER codecs.
5 // Created by Jens Alfke on 4/1/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYPublicKey.h"
10 #import "MYPrivateKey.h"
11 #import "MYKeychain.h"
13 #import "MYIdentity.h"
15 #import "MYCrypto+Cocoa.h"
17 #import "MYCrypto_Private.h"
23 #define kTestCaseRSAKeySize 2048
26 #pragma mark KEYCHAIN:
29 TestCase(MYKeychain) {
30 MYKeychain *kc = [MYKeychain defaultKeychain];
31 Log(@"Default keychain = %@", kc);
33 #if !MYCRYPTO_USE_IPHONE_API
37 kc = [MYKeychain allKeychains];
38 Log(@"All-keychains = %@", kc);
40 #if !MYCRYPTO_USE_IPHONE_API
41 CAssertEq(kc.path,nil);
46 TestCase(EnumerateKeys) {
47 RequireTestCase(MYKeychain);
48 NSEnumerator *e = [[MYKeychain allKeychains] enumeratePublicKeys];
49 Log(@"Public Key Enumerator = %@", e);
51 for (MYPublicKey *key in e) {
52 Log(@"Found %@ -- name=%@", key, key.name);
55 e = [[MYKeychain allKeychains] enumeratePrivateKeys];
56 Log(@"Key-Pair Enumerator = %@", e);
58 for (MYPrivateKey *key in e) {
59 Log(@"Found %@ -- name=%@", key, key.name);
62 e = [[MYKeychain allKeychains] enumerateSymmetricKeys];
63 Log(@"Symmetric Key Enumerator = %@", e);
65 for (MYSymmetricKey *key in e) {
66 Log(@"Found %@ -- name=%@", key, key.name);
71 TestCase(EnumerateCerts) {
72 RequireTestCase(MYKeychain);
73 NSEnumerator *e = [[MYKeychain allKeychains] enumerateCertificates];
74 Log(@"Enumerator = %@", e);
76 for (MYCertificate *cert in e) {
77 //Log(@"Found %@ -- name=%@, email=%@", cert, cert.commonName, cert.emailAddresses);
81 TestCase(EnumerateIdentities) {
82 RequireTestCase(MYKeychain);
83 NSEnumerator *e = [[MYKeychain allKeychains] enumerateIdentities];
84 Log(@"Enumerator = %@", e);
86 for (MYIdentity *ident in e) {
87 Log(@"Found %@\n\tcommonName=%@\n\temails=(%@)\n\tkey=%@",
88 ident, ident.commonName,
92 [ident.emailAddresses componentsJoinedByString: @", "],
100 #pragma mark SYMMETRIC KEYS:
103 static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits, MYKeychain *inKeychain ) {
104 NSAutoreleasePool *pool = [NSAutoreleasePool new];
105 MYSymmetricKey *key = nil;
107 Log(@"--- Testing %3u-bit #%i %s", sizeInBits, (int)algorithm,
108 (inKeychain ?", in keychain" :""));
111 key = [inKeychain generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm];
113 key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm];
114 Log(@"Created %@", key);
116 CAssertEq(key.algorithm, algorithm);
117 CAssertEq(key.keySizeInBits, sizeInBits);
118 #if !TARGET_OS_IPHONE
119 CAssert(key.cssmKey != NULL);
122 NSData *keyData = key.keyData;
123 Log(@"Key data = %@", keyData);
124 CAssertEq(keyData.length, sizeInBits/8);
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);
135 // Encrypt large binary data:
136 cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
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);
145 Log(@"Testing initWithKeyData:...");
146 MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: keyData algorithm: algorithm];
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);
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);
167 Warn(@"Unable to export wrapped key");
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);
181 [key removeFromKeychain];
187 TestCase(MYSymmetricKey) {
189 static const CCAlgorithm kTestAlgorithms[kNTests] = {
190 kCCAlgorithmAES128, kCCAlgorithmAES128, kCCAlgorithmAES128,
191 kCCAlgorithmDES, kCCAlgorithm3DES,
192 kCCAlgorithmCAST, kCCAlgorithmCAST, kCCAlgorithmCAST,
193 kCCAlgorithmRC4, kCCAlgorithmRC4, kCCAlgorithmRC4};
195 static const unsigned kTestBitSizes[kNTests] = {
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);
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':"
215 Log(@"You entered: '%@'", rawPassphrase);
216 CAssertEqual(rawPassphrase, @"Testing");
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:"
223 Log(@"Key from passphrase = %@", key);
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);
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:"
241 Log(@"Key from passphrase = %@", key2);
243 decrypted = [key2 decryptData: encrypted];
244 CAssertEqual(decrypted, cleartext);
250 #pragma mark KEY-PAIRS:
253 static void TestUseKeyPair(MYPrivateKey *pair) {
254 Log(@"---- TestUseKeyPair { %@ , %@ }.", pair, pair.publicKey);
256 CAssert(pair.keyRef);
257 MYPublicKey *publicKey = pair.publicKey;
258 CAssert(publicKey.keyRef);
260 NSData *pubKeyData = publicKey.keyData;
261 Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
264 MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
265 Log(@"Public key digest = %@",pubKeyDigest);
266 CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
268 Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
269 CAssertEqual(pubKeyData.my_SHA1Digest,pubKeyDigest);
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);
276 CAssert( [publicKey verifySignature: sig ofData: data] );
278 // Now let's encrypt...
279 NSData *crypted = [publicKey rawEncryptData: data];
280 Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
282 CAssertEqual([pair rawDecryptData: crypted], data);
283 Log(@"Verified decryption.");
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.");
290 // Test creating a public key from data:
291 Log(@"Reconstituting public key from data...");
292 pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
294 CAssertEqual(pub.keyData, pubKeyData);
295 CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
296 CAssert( [pub verifySignature: sig ofData: data] );
297 Log(@"Verified signature from reconstituted key.");
301 static void TestWrapSessionKey( MYPrivateKey *privateKey ) {
302 #if !TARGET_OS_IPHONE
303 MYSymmetricKey *sessionKey = [MYSymmetricKey generateSymmetricKeyOfSize: 128 algorithm:kCCAlgorithmAES128];
305 NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
306 NSData *encrypted = [sessionKey encryptData: cleartext];
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);
313 MYSymmetricKey *unwrappedKey = [privateKey unwrapSessionKey: wrapped
314 withAlgorithm: kCCAlgorithmAES128
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);
322 Log(@"Verifying that unwrapped key works");
323 NSData *decrypted = [unwrappedKey decryptData: encrypted];
324 CAssertEqual(decrypted, cleartext);
329 TestCase(MYGenerateKeyPair) {
330 RequireTestCase(MYKeychain);
332 Log(@"Generating key pair...");
333 MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: kTestCaseRSAKeySize];
334 MYPublicKey *publicKey = pair.publicKey;
335 Log(@"...created { %@ , %@ }.", pair, publicKey);
338 TestUseKeyPair(pair);
339 TestWrapSessionKey(pair);
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.");
349 [pair setAlias: @"TestCase@mooseyard.com"];
350 CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
351 CAssertEqual(publicKey.alias, @"TestCase@mooseyard.com");
353 CAssert([pair removeFromKeychain]);
354 Log(@"Removed key-pair.");
359 if ([pair removeFromKeychain])
360 Log(@"Removed key-pair from keychain.");
362 Warn(@"Unable to remove test key-pair from keychain");
368 #if !TARGET_OS_IPHONE
369 TestCase(MYUseIdentity) {
370 MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
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:"]
378 [NSException raise: NSGenericException format: @"User canceled"];
380 me = [panel my_identity];
381 [me makePreferredIdentityForName: @"MYCryptoTest"];
383 CAssert(me,@"No default identity has been set up in the Keychain");
384 TestUseKeyPair(me.privateKey);
390 #pragma mark KEYPAIR EXPORT:
393 static void testKeyPairExportWithPrompt(BOOL withPrompt) {
394 MYKeychain *keychain = [MYKeychain allKeychains];
395 Log(@"Generating key pair...");
396 MYPrivateKey *pair = [keychain generateRSAKeyPairOfSize: kTestCaseRSAKeySize];
398 CAssert(pair.keyRef);
399 CAssert(pair.publicKey.keyRef);
400 Log(@"...created pair.");
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.");
411 [pair setAlias: @"TestCase@mooseyard.com"];
412 CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
414 #if !TARGET_OS_IPHONE
415 Log(@"Exporting key-pair...");
416 NSString *passphrase = @"passphrase";
419 privKeyData = [pair exportKey];
421 privKeyData = [pair _exportKeyInFormat: kSecFormatWrappedOpenSSL
423 passphrase: passphrase];
424 Log(@"Exported data = %@ (%u bytes)", privKeyData,privKeyData.length);
425 CAssert(privKeyData);
426 [privKeyData writeToFile: @"ExportedPrivKey" atomically: YES];
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]);
439 Log(@"Removing key-pair from keychain...");
440 CAssert([pair removeFromKeychain]);
442 CAssert([keychain publicKeyWithDigest: digest] == nil);
444 #if !TARGET_OS_IPHONE
445 Log(@"Importing key-pair...");
447 pair = [keychain importPublicKey: pubKeyData
448 privateKey: privKeyData];
450 pair = [[[MYPrivateKey alloc] _initWithKeyData: privKeyData
451 publicKeyData: pubKeyData
452 forKeychain: keychain.keychainRefOrDefault
453 passphrase: passphrase]
457 CAssertEqual(pair.publicKey.keyData, pubKeyData);
461 if ([pair removeFromKeychain])
462 Log(@"Removed key-pair from keychain.");
464 Warn(@"Unable to remove test key-pair from keychain");
469 TestCase(KeyPairExport) {
470 RequireTestCase(MYKeychain);
471 RequireTestCase(MYGenerateKeyPair);
472 testKeyPairExportWithPrompt(NO);
475 TestCase(KeyPairExportWithUI) {
476 RequireTestCase(KeyPairExport);
477 testKeyPairExportWithPrompt(YES);