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"
14 #import "MYCrypto+Cocoa.h"
15 #import "MYCrypto_Private.h"
21 #pragma mark KEYCHAIN:
24 TestCase(MYKeychain) {
25 MYKeychain *kc = [MYKeychain defaultKeychain];
26 Log(@"Default keychain = %@", kc);
28 #if !MYCRYPTO_USE_IPHONE_API
32 kc = [MYKeychain allKeychains];
33 Log(@"All-keychains = %@", kc);
35 #if !MYCRYPTO_USE_IPHONE_API
36 CAssertEq(kc.path,nil);
41 TestCase(EnumerateKeys) {
42 RequireTestCase(MYKeychain);
43 NSEnumerator *e = [[MYKeychain allKeychains] enumeratePublicKeys];
44 Log(@"Public Key Enumerator = %@", e);
46 for (MYPublicKey *key in e) {
47 Log(@"Found %@ -- name=%@", key, key.name);
50 e = [[MYKeychain allKeychains] enumeratePrivateKeys];
51 Log(@"Key-Pair Enumerator = %@", e);
53 for (MYPrivateKey *key in e) {
54 Log(@"Found %@ -- name=%@", key, key.name);
57 e = [[MYKeychain allKeychains] enumerateSymmetricKeys];
58 Log(@"Symmetric Key Enumerator = %@", e);
60 for (MYSymmetricKey *key in e) {
61 Log(@"Found %@ -- name=%@", key, key.name);
66 TestCase(EnumerateCerts) {
67 RequireTestCase(MYKeychain);
68 NSEnumerator *e = [[MYKeychain allKeychains] enumerateCertificates];
69 Log(@"Enumerator = %@", e);
71 for (MYCertificate *cert in e) {
72 //Log(@"Found %@ -- name=%@, email=%@", cert, cert.commonName, cert.emailAddresses);
76 TestCase(EnumerateIdentities) {
77 RequireTestCase(MYKeychain);
78 NSEnumerator *e = [[MYKeychain allKeychains] enumerateIdentities];
79 Log(@"Enumerator = %@", e);
81 for (MYIdentity *ident in e) {
82 Log(@"Found %@\n\tcommonName=%@\n\temails=(%@)\n\tkey=%@",
83 ident, ident.commonName,
87 [ident.emailAddresses componentsJoinedByString: @", "],
95 #pragma mark SYMMETRIC KEYS:
98 static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits ) {
99 NSAutoreleasePool *pool = [NSAutoreleasePool new];
100 Log(@"--- Testing %3u-bit #%i", sizeInBits, (int)algorithm);
102 MYSymmetricKey *key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits
103 algorithm: algorithm];
104 Log(@"Created %@", key);
106 CAssertEq(key.algorithm, algorithm);
107 CAssertEq(key.keySizeInBits, sizeInBits);
108 #if !TARGET_OS_IPHONE
109 CAssert(key.cssmKey != NULL);
112 NSData *keyData = key.keyData;
113 Log(@"Key data = %@", keyData);
114 CAssertEq(keyData.length, sizeInBits/8);
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);
124 // Encrypt large binary data:
125 cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
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);
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.
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);
147 Warn(@"Unable to export key in PKCS8");
153 TestCase(MYSymmetricKey) {
155 static const CCAlgorithm kTestAlgorithms[kNTests] = {
156 kCCAlgorithmAES128, kCCAlgorithmAES128, kCCAlgorithmAES128,
157 kCCAlgorithmDES, kCCAlgorithm3DES,
158 kCCAlgorithmCAST, kCCAlgorithmCAST, kCCAlgorithmCAST,
159 kCCAlgorithmRC4, kCCAlgorithmRC4, kCCAlgorithmRC4};
161 static const unsigned kTestBitSizes[kNTests] = {
167 for (int i=0; i<kNTests; i++)
168 testSymmetricKey(kTestAlgorithms[i], kTestBitSizes[i]);
173 #pragma mark KEY-PAIRS:
176 static void TestUseKeyPair(MYPrivateKey *pair) {
177 Log(@"---- TestUseKeyPair { %@ , %@ }.", pair, pair.publicKey);
179 CAssert(pair.keyRef);
180 MYPublicKey *publicKey = pair.publicKey;
181 CAssert(publicKey.keyRef);
183 NSData *pubKeyData = publicKey.keyData;
184 Log(@"Public key = %@ (%u bytes)",pubKeyData,pubKeyData.length);
187 MYSHA1Digest *pubKeyDigest = publicKey.publicKeyDigest;
188 Log(@"Public key digest = %@",pubKeyDigest);
189 CAssertEqual(pair.publicKeyDigest, pubKeyDigest);
191 Log(@"SHA1 of pub key = %@", pubKeyData.my_SHA1Digest.asData);
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);
198 CAssert( [publicKey verifySignature: sig ofData: data] );
200 // Now let's encrypt...
201 NSData *crypted = [publicKey encryptData: data];
202 Log(@"Encrypted = %@ (%u bytes)",crypted,crypted.length);
204 CAssertEqual([pair decryptData: crypted], data);
205 Log(@"Verified decryption.");
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.");
212 // Test creating a public key from data:
213 Log(@"Reconstituting public key from data...");
214 pub = [[MYPublicKey alloc] initWithKeyData: pubKeyData];
216 CAssertEqual(pub.keyData, pubKeyData);
217 CAssertEqual(pub.publicKeyDigest, pubKeyDigest);
218 CAssert( [pub verifySignature: sig ofData: data] );
219 Log(@"Verified signature from reconstituted key.");
223 TestCase(MYGenerateKeyPair) {
224 RequireTestCase(MYKeychain);
226 Log(@"Generating key pair...");
227 MYPrivateKey *pair = [[MYKeychain defaultKeychain] generateRSAKeyPairOfSize: 512];
228 MYPublicKey *publicKey = pair.publicKey;
229 Log(@"...created { %@ , %@ }.", pair, publicKey);
232 TestUseKeyPair(pair);
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.");
242 [pair setAlias: @"TestCase@mooseyard.com"];
243 CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
244 CAssertEqual(publicKey.alias, @"TestCase@mooseyard.com");
246 CAssert([pair removeFromKeychain]);
247 Log(@"Removed key-pair.");
252 if ([pair removeFromKeychain])
253 Log(@"Removed key-pair from keychain.");
255 Warn(@"Unable to remove test key-pair from keychain");
261 TestCase(MYUseIdentity) {
262 MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
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:"]
270 [NSException raise: NSGenericException format: @"User canceled"];
272 me = [panel my_identity];
273 [me makePreferredIdentityForName: @"MYCryptoTest"];
275 CAssert(me,@"No default identity has been set up in the Keychain");
276 TestUseKeyPair(me.privateKey);
281 #pragma mark KEYPAIR EXPORT:
284 static void testKeyPairExportWithPrompt(BOOL withPrompt) {
285 MYKeychain *keychain = [MYKeychain allKeychains];
286 Log(@"Generating key pair...");
287 MYPrivateKey *pair = [keychain generateRSAKeyPairOfSize: 512];
289 CAssert(pair.keyRef);
290 CAssert(pair.publicKey.keyRef);
291 Log(@"...created pair.");
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.");
302 [pair setAlias: @"TestCase@mooseyard.com"];
303 CAssertEqual(pair.alias, @"TestCase@mooseyard.com");
305 #if !TARGET_OS_IPHONE
306 Log(@"Exporting key-pair...");
307 NSString *passphrase = @"passphrase";
310 privKeyData = [pair exportKey];
312 privKeyData = [pair _exportKeyInFormat: kSecFormatWrappedOpenSSL
314 passphrase: passphrase];
315 Log(@"Exported data = %@ (%u bytes)", privKeyData,privKeyData.length);
316 CAssert(privKeyData);
317 [privKeyData writeToFile: @"ExportedPrivKey" atomically: YES];
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]);
330 Log(@"Removing key-pair from keychain...");
331 CAssert([pair removeFromKeychain]);
333 CAssert([keychain publicKeyWithDigest: digest] == nil);
335 #if !TARGET_OS_IPHONE
336 Log(@"Importing key-pair...");
338 pair = [keychain importPublicKey: pubKeyData
339 privateKey: privKeyData];
341 pair = [[[MYPrivateKey alloc] _initWithKeyData: privKeyData
342 publicKeyData: pubKeyData
343 forKeychain: keychain.keychainRefOrDefault
344 passphrase: passphrase]
348 CAssertEqual(pair.publicKey.keyData, pubKeyData);
352 if ([pair removeFromKeychain])
353 Log(@"Removed key-pair from keychain.");
355 Warn(@"Unable to remove test key-pair from keychain");
360 TestCase(KeyPairExport) {
361 RequireTestCase(MYKeychain);
362 RequireTestCase(MYPrivateKey);
363 testKeyPairExportWithPrompt(NO);
366 TestCase(KeyPairExportWithUI) {
367 RequireTestCase(KeyPairExport);
368 testKeyPairExportWithPrompt(YES);