Working on export/import of symmetric keys, and passphrase entry. Not ready for release quite yet.
authorsnej@snej.local
Sat Apr 18 18:12:06 2009 -0700 (2009-04-18)
changeset 12e4c971be4079
parent 11 3568d5fd4b6a
child 13 6fd9177eb6da
Working on export/import of symmetric keys, and passphrase entry. Not ready for release quite yet.
MYCertificate.m
MYCrypto-iPhone.xcodeproj/project.pbxproj
MYCrypto.xcodeproj/project.pbxproj
MYCryptoTest.m
MYCrypto_Private.h
MYDecoder.m
MYKeychainItem.m
MYSymmetricKey-iPhone.m
MYSymmetricKey.h
MYSymmetricKey.m
     1.1 --- a/MYCertificate.m	Tue Apr 14 18:34:52 2009 -0700
     1.2 +++ b/MYCertificate.m	Sat Apr 18 18:12:06 2009 -0700
     1.3 @@ -236,6 +236,23 @@
     1.4  }
     1.5  
     1.6  
     1.7 +// Taken from Keychain.framework
     1.8 +NSString* OIDAsString(const CSSM_OID oid) {
     1.9 +    if ((NULL == oid.Data) || (0 >= oid.Length)) {
    1.10 +        return nil;
    1.11 +    } else {
    1.12 +        NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)];
    1.13 +        unsigned int i;
    1.14 +        
    1.15 +        for (i = 0; i < oid.Length; ++i) {
    1.16 +            [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]];
    1.17 +        }
    1.18 +        
    1.19 +        return result;
    1.20 +    }
    1.21 +}
    1.22 +
    1.23 +
    1.24  
    1.25  TestCase(Trust) {
    1.26      Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy]));
     2.1 --- a/MYCrypto-iPhone.xcodeproj/project.pbxproj	Tue Apr 14 18:34:52 2009 -0700
     2.2 +++ b/MYCrypto-iPhone.xcodeproj/project.pbxproj	Sat Apr 18 18:12:06 2009 -0700
     2.3 @@ -14,6 +14,7 @@
     2.4  		27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059CF20F8F0F9200A8422F /* MYIdentity.m */; };
     2.5  		273391CD0F81EC70009414D9 /* MYCertificate-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */; };
     2.6  		273392120F8283B8009414D9 /* MYKeychain-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 273392110F8283B8009414D9 /* MYKeychain-iPhone.m */; };
     2.7 +		274110090F99234100AD413F /* MYSymmetricKey-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */; };
     2.8  		2748607F0F8D5E0600FE617B /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 2748607E0F8D5E0600FE617B /* MYPrivateKey.m */; };
     2.9  		276FB13F0F84090900CB326E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 276FB13E0F84090900CB326E /* Security.framework */; };
    2.10  		276FB16E0F84152B00CB326E /* MYCryptoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB16D0F84152B00CB326E /* MYCryptoTest.m */; };
    2.11 @@ -48,6 +49,7 @@
    2.12  		27059CF20F8F0F9200A8422F /* MYIdentity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYIdentity.m; sourceTree = "<group>"; };
    2.13  		273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYCertificate-iPhone.m"; sourceTree = "<group>"; };
    2.14  		273392110F8283B8009414D9 /* MYKeychain-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYKeychain-iPhone.m"; sourceTree = "<group>"; };
    2.15 +		274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYSymmetricKey-iPhone.m"; sourceTree = "<group>"; };
    2.16  		2748607D0F8D5DF200FE617B /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = "<group>"; };
    2.17  		2748607E0F8D5E0600FE617B /* MYPrivateKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPrivateKey.m; sourceTree = "<group>"; };
    2.18  		276FB13E0F84090900CB326E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
    2.19 @@ -157,6 +159,7 @@
    2.20  				276FB3180F856AA700CB326E /* MYPublicKey.m */,
    2.21  				27A430150F87C6DB0063D362 /* MYSymmetricKey.h */,
    2.22  				27A430130F87C6D50063D362 /* MYSymmetricKey.m */,
    2.23 +				274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */,
    2.24  				27E8231B0F81D56E0019BE60 /* MYDigest.h */,
    2.25  				27E8231C0F81D56E0019BE60 /* MYDigest.m */,
    2.26  				27E8231A0F81D56E0019BE60 /* MYCrypto_Private.h */,
    2.27 @@ -286,6 +289,7 @@
    2.28  				2748607F0F8D5E0600FE617B /* MYPrivateKey.m in Sources */,
    2.29  				27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */,
    2.30  				27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */,
    2.31 +				274110090F99234100AD413F /* MYSymmetricKey-iPhone.m in Sources */,
    2.32  			);
    2.33  			runOnlyForDeploymentPostprocessing = 0;
    2.34  		};
     3.1 --- a/MYCrypto.xcodeproj/project.pbxproj	Tue Apr 14 18:34:52 2009 -0700
     3.2 +++ b/MYCrypto.xcodeproj/project.pbxproj	Sat Apr 18 18:12:06 2009 -0700
     3.3 @@ -12,6 +12,7 @@
     3.4  		27059D840F8FA82500A8422F /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27059D830F8FA82500A8422F /* SecurityInterface.framework */; };
     3.5  		27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D520F8F9BB500A8422F /* MYDecoder.m */; };
     3.6  		270B879F0F8C565000C56781 /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 270B879E0F8C565000C56781 /* MYPrivateKey.m */; };
     3.7 +		27410FF00F99200A00AD413F /* MYSymmetricKey-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */; };
     3.8  		274861D50F8E4B5200FE617B /* MYCertGen.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42ECD0F8689D30063D362 /* MYCertGen.m */; };
     3.9  		274863A20F8EF39400FE617B /* MYIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 274863A10F8EF39400FE617B /* MYIdentity.m */; };
    3.10  		27A42CBF0F8578B40063D362 /* MYCryptoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42CBE0F8578B40063D362 /* MYCryptoTest.m */; };
    3.11 @@ -57,6 +58,7 @@
    3.12  		27059D830F8FA82500A8422F /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = /System/Library/Frameworks/SecurityInterface.framework; sourceTree = "<absolute>"; };
    3.13  		270B879D0F8C565000C56781 /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = "<group>"; };
    3.14  		270B879E0F8C565000C56781 /* MYPrivateKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPrivateKey.m; sourceTree = "<group>"; };
    3.15 +		27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYSymmetricKey-iPhone.m"; sourceTree = "<group>"; };
    3.16  		2748604D0F8D5C4C00FE617B /* MYCrypto_Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = MYCrypto_Release.xcconfig; sourceTree = "<group>"; };
    3.17  		274863A00F8EF39400FE617B /* MYIdentity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYIdentity.h; sourceTree = "<group>"; };
    3.18  		274863A10F8EF39400FE617B /* MYIdentity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYIdentity.m; sourceTree = "<group>"; };
    3.19 @@ -155,6 +157,7 @@
    3.20  				270B879E0F8C565000C56781 /* MYPrivateKey.m */,
    3.21  				27A42D400F858ED80063D362 /* MYSymmetricKey.h */,
    3.22  				27A42D410F858ED80063D362 /* MYSymmetricKey.m */,
    3.23 +				27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */,
    3.24  				27AAD97B0F892A0D0064DD7C /* MYCryptoConfig.h */,
    3.25  				27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */,
    3.26  				27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */,
    3.27 @@ -321,6 +324,7 @@
    3.28  				27059D530F8F9BB500A8422F /* MYEncoder.m in Sources */,
    3.29  				27059D770F8FA23100A8422F /* MYCrypto+Cocoa.m in Sources */,
    3.30  				27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */,
    3.31 +				27410FF00F99200A00AD413F /* MYSymmetricKey-iPhone.m in Sources */,
    3.32  			);
    3.33  			runOnlyForDeploymentPostprocessing = 0;
    3.34  		};
     4.1 --- a/MYCryptoTest.m	Tue Apr 14 18:34:52 2009 -0700
     4.2 +++ b/MYCryptoTest.m	Sat Apr 18 18:12:06 2009 -0700
     4.3 @@ -11,7 +11,9 @@
     4.4  #import "MYKeychain.h"
     4.5  #import "MYDigest.h"
     4.6  #import "MYIdentity.h"
     4.7 +#if !TARGET_OS_IPHONE
     4.8  #import "MYCrypto+Cocoa.h"
     4.9 +#endif
    4.10  #import "MYCrypto_Private.h"
    4.11  
    4.12  
    4.13 @@ -98,57 +100,85 @@
    4.14  #pragma mark SYMMETRIC KEYS:
    4.15  
    4.16  
    4.17 -static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits ) {
    4.18 +static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits, MYKeychain *inKeychain ) {
    4.19      NSAutoreleasePool *pool = [NSAutoreleasePool new];
    4.20 -    Log(@"--- Testing %3u-bit #%i", sizeInBits, (int)algorithm);
    4.21 -    // Generate key:
    4.22 -    MYSymmetricKey *key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits
    4.23 -                                                           algorithm: algorithm];
    4.24 -    Log(@"Created %@", key);
    4.25 +    MYSymmetricKey *key = nil;
    4.26 +    @try{
    4.27 +        Log(@"--- Testing %3u-bit #%i %s", sizeInBits, (int)algorithm,
    4.28 +            (inKeychain ?", in keychain" :""));
    4.29 +        // Generate key:
    4.30 +        if (inKeychain)
    4.31 +            key = [inKeychain generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm];
    4.32 +        else
    4.33 +            key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm];
    4.34 +        Log(@"Created %@", key);
    4.35      CAssert(key);
    4.36 -    CAssertEq(key.algorithm, algorithm);
    4.37 -    CAssertEq(key.keySizeInBits, sizeInBits);
    4.38 -#if !TARGET_OS_IPHONE
    4.39 -    CAssert(key.cssmKey != NULL);
    4.40 -#endif
    4.41 -    
    4.42 -    NSData *keyData = key.keyData;
    4.43 -    Log(@"Key data = %@", keyData);
    4.44 -    CAssertEq(keyData.length, sizeInBits/8);
    4.45 -    
    4.46 -    // Encrypt a small amount of text:
    4.47 -    NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
    4.48 -    NSData *encrypted = [key encryptData: cleartext];
    4.49 -    Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
    4.50 -    CAssert(encrypted.length >= cleartext.length);
    4.51 -    NSData *decrypted = [key decryptData: encrypted];
    4.52 -    CAssertEqual(decrypted, cleartext);
    4.53 -    
    4.54 -    // Encrypt large binary data:
    4.55 -    cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
    4.56 -    CAssert(cleartext);
    4.57 -    encrypted = [key encryptData: cleartext];
    4.58 -    Log(@"Encrypted = %u bytes", encrypted.length);
    4.59 -    CAssert(encrypted.length >= cleartext.length);
    4.60 -    decrypted = [key decryptData: encrypted];
    4.61 -    CAssertEqual(decrypted, cleartext);
    4.62 -    
    4.63 -#if !TARGET_OS_IPHONE
    4.64 -    // Try reconstituting the key from its data:
    4.65 -    NSData *exported = [key exportKeyInFormat: kSecFormatWrappedPKCS8 withPEM: NO];
    4.66 -    Log(@"Exported key: %@", exported);
    4.67 -    // CAssert(exported);
    4.68 -    //FIX: Exporting symmetric keys isn't working. Temporarily making this optional.
    4.69 -    if (exported) {
    4.70 -        CAssert(exported);
    4.71 -        MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: exported algorithm: algorithm];
    4.72 -        Log(@"Reconstituted as %@", key2);
    4.73 -        CAssertEqual(key2,key);
    4.74 +        CAssertEq(key.algorithm, algorithm);
    4.75 +        CAssertEq(key.keySizeInBits, sizeInBits);
    4.76 +    #if !TARGET_OS_IPHONE
    4.77 +        CAssert(key.cssmKey != NULL);
    4.78 +    #endif
    4.79 +        
    4.80 +        NSData *keyData = key.keyData;
    4.81 +        Log(@"Key data = %@", keyData);
    4.82 +        CAssertEq(keyData.length, sizeInBits/8);
    4.83 +        
    4.84 +        // Encrypt a small amount of text:
    4.85 +        Log(@"Testing encryption / decryption ...");
    4.86 +        NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
    4.87 +        NSData *encrypted = [key encryptData: cleartext];
    4.88 +        Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
    4.89 +        CAssert(encrypted.length >= cleartext.length);
    4.90 +        NSData *decrypted = [key decryptData: encrypted];
    4.91 +        CAssertEqual(decrypted, cleartext);
    4.92 +        
    4.93 +        // Encrypt large binary data:
    4.94 +        cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"];
    4.95 +        CAssert(cleartext);
    4.96 +        encrypted = [key encryptData: cleartext];
    4.97 +        Log(@"Encrypted = %u bytes", encrypted.length);
    4.98 +        CAssert(encrypted.length >= cleartext.length);
    4.99 +        decrypted = [key decryptData: encrypted];
   4.100 +        CAssertEqual(decrypted, cleartext);
   4.101 +        
   4.102 +    #if 1
   4.103 +        Log(@"Testing initWithKeyData:...");
   4.104 +        MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: keyData algorithm: algorithm];
   4.105 +        CAssert(key2);
   4.106 +        Log(@"Key from data = %@",key2);
   4.107 +        CAssertEqual(key2.keyData, keyData);
   4.108 +        CAssertEq(key2.algorithm, algorithm);
   4.109 +        CAssertEq(key2.keySizeInBits, sizeInBits);
   4.110          decrypted = [key2 decryptData: encrypted];
   4.111          CAssertEqual(decrypted, cleartext);
   4.112 -    } else
   4.113 -        Warn(@"Unable to export key in PKCS8");
   4.114 -#endif
   4.115 +        [key2 release];
   4.116 +    #endif
   4.117 +
   4.118 +    #if !TARGET_OS_IPHONE
   4.119 +        // Try exporting and importing a wrapped key:
   4.120 +        Log(@"Testing export/import...");
   4.121 +        NSData *exported = [key exportKeyInFormat: kSecFormatWrappedPKCS8 withPEM: NO];
   4.122 +        Log(@"Exported key: %@", exported);
   4.123 +    #if 0
   4.124 +        CAssert(exported);
   4.125 +    #else
   4.126 +        //FIX: Exporting symmetric keys isn't working. Temporarily making this optional.
   4.127 +        if (!exported)
   4.128 +            Warn(@"Unable to export wrapped key");
   4.129 +        else
   4.130 +    #endif
   4.131 +        {
   4.132 +            CAssert(exported);
   4.133 +            MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: exported algorithm: algorithm];
   4.134 +            Log(@"Reconstituted as %@", key2);
   4.135 +            CAssertEqual(key2.keyData,key.keyData);
   4.136 +            decrypted = [key2 decryptData: encrypted];
   4.137 +            CAssertEqual(decrypted, cleartext);
   4.138 +        }
   4.139 +    #endif
   4.140 +    }@finally{
   4.141 +        [key removeFromKeychain];
   4.142 +    }
   4.143      [pool drain];
   4.144  }
   4.145  
   4.146 @@ -167,8 +197,49 @@
   4.147          40, 80, 128,
   4.148          32, 200, 512*8};
   4.149  
   4.150 -    for (int i=0; i<kNTests; i++) 
   4.151 -        testSymmetricKey(kTestAlgorithms[i], kTestBitSizes[i]);
   4.152 +    for (int useKeychain=0; useKeychain<=1; useKeychain++)
   4.153 +        for (int testNo=0; testNo<kNTests; testNo++) 
   4.154 +            testSymmetricKey(kTestAlgorithms[testNo], 
   4.155 +                             kTestBitSizes[testNo],
   4.156 +                             useKeychain ?[MYKeychain defaultKeychain] :nil);
   4.157 +}
   4.158 +
   4.159 +
   4.160 +TestCase(MYSymmetricKeyPassphrase) {
   4.161 +    Log(@"Prompting for raw passphrase --");
   4.162 +    NSString *rawPassphrase = [MYSymmetricKey promptForPassphraseWithAlertTitle: @"Raw Passphrase Test" 
   4.163 +                                                                    alertPrompt: @"Enter the passphrase 'Testing':"
   4.164 +                                                                       creating: YES];
   4.165 +    Log(@"You entered: '%@'", rawPassphrase);
   4.166 +    CAssertEqual(rawPassphrase, @"Testing");
   4.167 +    
   4.168 +    Log(@"Prompting for passphrase for key --");
   4.169 +    MYSymmetricKey *key = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Symmetric Key Passphrase Test Case" 
   4.170 +                                                                       alertPrompt: @"Please enter a passphrase to generate a key:"
   4.171 +                                                                          creating: YES
   4.172 +                                                                              salt: @"wahooma"];
   4.173 +    Log(@"Key from passphrase = %@", key);
   4.174 +    CAssert(key);
   4.175 +
   4.176 +    // Encrypt a small amount of text:
   4.177 +    Log(@"Testing encryption / decryption ...");
   4.178 +    NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding];
   4.179 +    NSData *encrypted = [key encryptData: cleartext];
   4.180 +    Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted);
   4.181 +    CAssert(encrypted.length >= cleartext.length);
   4.182 +    NSData *decrypted = [key decryptData: encrypted];
   4.183 +    CAssertEqual(decrypted, cleartext);
   4.184 +    
   4.185 +    // Now test decryption by re-entered passphrase:
   4.186 +    Log(@"Testing decryption using re-entered passphrase...");
   4.187 +    MYSymmetricKey *key2 = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Symmetric Key Passphrase Test Case" 
   4.188 +                                                                        alertPrompt: @"Please re-enter the same passphrase:" 
   4.189 +                                                                           creating: NO
   4.190 +                                                                               salt: @"wahooma"];
   4.191 +    Log(@"Key from passphrase = %@", key2);
   4.192 +    CAssert(key2);
   4.193 +    decrypted = [key2 decryptData: encrypted];
   4.194 +    CAssertEqual(decrypted, cleartext);
   4.195  }
   4.196  
   4.197  
   4.198 @@ -261,6 +332,7 @@
   4.199  }
   4.200  
   4.201  
   4.202 +#if !TARGET_OS_IPHONE
   4.203  TestCase(MYUseIdentity) {
   4.204      MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"];
   4.205      if (!me) {
   4.206 @@ -278,6 +350,7 @@
   4.207      CAssert(me,@"No default identity has been set up in the Keychain");
   4.208      TestUseKeyPair(me.privateKey);
   4.209  }
   4.210 +#endif
   4.211  
   4.212  
   4.213  #pragma mark -
   4.214 @@ -362,7 +435,7 @@
   4.215  
   4.216  TestCase(KeyPairExport) {
   4.217      RequireTestCase(MYKeychain);
   4.218 -    RequireTestCase(MYPrivateKey);
   4.219 +    RequireTestCase(MYGenerateKeyPair);
   4.220      testKeyPairExportWithPrompt(NO);
   4.221  }
   4.222  
     5.1 --- a/MYCrypto_Private.h	Tue Apr 14 18:34:52 2009 -0700
     5.2 +++ b/MYCrypto_Private.h	Sat Apr 18 18:12:06 2009 -0700
     5.3 @@ -57,6 +57,7 @@
     5.4  - (NSData*) _crypt: (NSData *)data operation: (BOOL) op;    // YES to encrypt, NO to decrypt
     5.5  #if !MYCRYPTO_USE_IPHONE_API
     5.6  @property (readonly) const CSSM_KEY* cssmKey;
     5.7 +@property (readonly) const CSSM_CSP_HANDLE cssmCSPHandle;
     5.8  - (NSData*) exportKeyInFormat: (SecExternalFormat)format withPEM: (BOOL)withPEM;
     5.9  - (CSSM_CC_HANDLE) _createSignatureContext: (CSSM_ALGORITHMS)algorithm;
    5.10  - (CSSM_CC_HANDLE) _createPassThroughContext;
     6.1 --- a/MYDecoder.m	Tue Apr 14 18:34:52 2009 -0700
     6.2 +++ b/MYDecoder.m	Sat Apr 18 18:12:06 2009 -0700
     6.3 @@ -308,23 +308,6 @@
     6.4  
     6.5  
     6.6  
     6.7 -// Taken from Keychain.framework
     6.8 -NSString* OIDAsString(const CSSM_OID oid) {
     6.9 -    if ((NULL == oid.Data) || (0 >= oid.Length)) {
    6.10 -        return nil;
    6.11 -    } else {
    6.12 -        NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)];
    6.13 -        unsigned int i;
    6.14 -        
    6.15 -        for (i = 0; i < oid.Length; ++i) {
    6.16 -            [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]];
    6.17 -        }
    6.18 -        
    6.19 -        return result;
    6.20 -    }
    6.21 -}
    6.22 -
    6.23 -
    6.24  
    6.25  #pragma mark -
    6.26  #pragma mark TEST CASE:
     7.1 --- a/MYKeychainItem.m	Tue Apr 14 18:34:52 2009 -0700
     7.2 +++ b/MYKeychainItem.m	Sat Apr 18 18:12:06 2009 -0700
     7.3 @@ -92,11 +92,13 @@
     7.4  }
     7.5  
     7.6  - (BOOL) removeFromKeychain {
     7.7 +    OSStatus err;
     7.8  #if MYCRYPTO_USE_IPHONE_API
     7.9 -    return check(SecItemDelete(self.asQuery), @"SecItemDelete");
    7.10 +    err = SecItemDelete(self.asQuery);
    7.11  #else
    7.12 -    return check(SecKeychainItemDelete((SecKeychainItemRef)_itemRef), @"SecKeychainItemDelete");
    7.13 +    err = SecKeychainItemDelete((SecKeychainItemRef)_itemRef);
    7.14  #endif
    7.15 +    return err==errSecItemNotFound || check(err, @"SecKeychainItemDelete");
    7.16  }
    7.17  
    7.18  
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/MYSymmetricKey-iPhone.m	Sat Apr 18 18:12:06 2009 -0700
     8.3 @@ -0,0 +1,173 @@
     8.4 +//
     8.5 +//  MYSymmetricKey-iPhone.m
     8.6 +//  MYCrypto
     8.7 +//
     8.8 +//  Created by Jens Alfke on 4/17/09.
     8.9 +//  Copyright 2009 Jens Alfke. All rights reserved.
    8.10 +//
    8.11 +
    8.12 +#import "MYSymmetricKey.h"
    8.13 +#import "MYCryptor.h"
    8.14 +#import "MYCrypto_Private.h"
    8.15 +
    8.16 +#if MYCRYPTO_USE_IPHONE_API
    8.17 +
    8.18 +
    8.19 +typedef uint32_t CSSM_ALGORITHMS;
    8.20 +enum {
    8.21 +// Taken from cssmtype.h in OS X 10.5 SDK:
    8.22 +	CSSM_ALGID_NONE =					0x00000000L,
    8.23 +	CSSM_ALGID_DES =					CSSM_ALGID_NONE + 14,
    8.24 +	CSSM_ALGID_3DES_3KEY_EDE =			CSSM_ALGID_NONE + 17,
    8.25 +	CSSM_ALGID_3DES_3KEY =           	CSSM_ALGID_3DES_3KEY_EDE,
    8.26 +	CSSM_ALGID_RC4 =					CSSM_ALGID_NONE + 25,
    8.27 +	CSSM_ALGID_CAST =					CSSM_ALGID_NONE + 27,
    8.28 +	CSSM_ALGID_VENDOR_DEFINED =			CSSM_ALGID_NONE + 0x80000000L,
    8.29 +	CSSM_ALGID_AES
    8.30 +};
    8.31 +
    8.32 +static const CSSM_ALGORITHMS kCSSMAlgorithms[] = {
    8.33 +CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4
    8.34 +};
    8.35 +
    8.36 +static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"};
    8.37 +
    8.38 +
    8.39 +@implementation MYSymmetricKey
    8.40 +
    8.41 +
    8.42 +- (id) _initWithKeyData: (NSData*)keyData
    8.43 +              algorithm: (CCAlgorithm)algorithm
    8.44 +             inKeychain: (MYKeychain*)keychain
    8.45 +{
    8.46 +    Assert(algorithm <= kCCAlgorithmRC4);
    8.47 +    Assert(keyData);
    8.48 +    NSNumber *keySizeInBits = [NSNumber numberWithUnsignedInt: keyData.length * 8];
    8.49 +    NSDictionary *keyAttrs = $dict( {(id)kSecClass, (id)kSecClassKey},
    8.50 +                                    //{(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
    8.51 +                                    {(id)kSecAttrKeyType, [NSNumber numberWithUnsignedInt: kCSSMAlgorithms[algorithm]]},
    8.52 +                                    {(id)kSecAttrKeySizeInBits, keySizeInBits},
    8.53 +                                    {(id)kSecAttrEffectiveKeySize, keySizeInBits},
    8.54 +                                    {(id)kSecAttrIsPermanent, keychain ?$true :$false},
    8.55 +                                    {(id)kSecAttrCanEncrypt, $true},
    8.56 +                                    {(id)kSecAttrCanDecrypt, $true},
    8.57 +                                    {(id)kSecAttrCanWrap, $false},
    8.58 +                                    {(id)kSecAttrCanUnwrap, $false},
    8.59 +                                    {(id)kSecAttrCanDerive, $false},
    8.60 +                                    {(id)kSecAttrCanSign, $false},
    8.61 +                                    {(id)kSecAttrCanVerify, $false},
    8.62 +                                    {(id)kSecValueData, keyData},
    8.63 +                                    {(id)kSecReturnPersistentRef, $true});
    8.64 +    SecKeyRef keyRef = NULL;
    8.65 +    if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) {
    8.66 +        [self release];
    8.67 +        return nil;
    8.68 +    }
    8.69 +    Assert(keyRef, @"SecItemAdd didn't return anything");
    8.70 +    self = [self initWithKeyRef: keyRef];
    8.71 +    CFRelease(keyRef);
    8.72 +    return self;
    8.73 +}
    8.74 +
    8.75 +- (id) initWithKeyData: (NSData*)keyData
    8.76 +             algorithm: (CCAlgorithm)algorithm
    8.77 +{
    8.78 +    return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
    8.79 +}
    8.80 +
    8.81 ++ (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
    8.82 +                                      algorithm: (CCAlgorithm)algorithm
    8.83 +                                     inKeychain: (MYKeychain*)keychain
    8.84 +{
    8.85 +    return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
    8.86 +                                 algorithm: algorithm
    8.87 +                                inKeychain: keychain]
    8.88 +                    autorelease];
    8.89 +}
    8.90 +
    8.91 ++ (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
    8.92 +                                     algorithm: (CCAlgorithm)algorithm {
    8.93 +    return [self _generateSymmetricKeyOfSize: keySizeInBits
    8.94 +                                   algorithm: algorithm
    8.95 +                                  inKeychain: nil];
    8.96 +}
    8.97 +
    8.98 +
    8.99 +- (SecExternalItemType) keyType {
   8.100 +    return kSecAttrKeyClassSymmetric;
   8.101 +}
   8.102 +
   8.103 +- (CCAlgorithm) algorithm {
   8.104 +    CSSM_ALGORITHMS cssmAlg;
   8.105 +    id keyType = [self _attribute: kSecAttrKeyType];
   8.106 +    Assert(keyType!=nil, @"Key has no kSecAttrKeyType");
   8.107 +    cssmAlg = [keyType unsignedIntValue];
   8.108 +    switch(cssmAlg) {
   8.109 +        case CSSM_ALGID_AES:
   8.110 +            return kCCAlgorithmAES128;
   8.111 +        case CSSM_ALGID_DES:
   8.112 +            return kCCAlgorithmDES;	
   8.113 +        case CSSM_ALGID_3DES_3KEY:
   8.114 +            return kCCAlgorithm3DES;
   8.115 +        case CSSM_ALGID_CAST:
   8.116 +            return kCCAlgorithmCAST;
   8.117 +        case CSSM_ALGID_RC4:
   8.118 +            return kCCAlgorithmRC4;	
   8.119 +        default:
   8.120 +            Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
   8.121 +            return (CCAlgorithm)-1;
   8.122 +    }
   8.123 +}
   8.124 +
   8.125 +- (const char*) algorithmName {
   8.126 +    CCAlgorithm a = self.algorithm;
   8.127 +    if (a >= 0 && a <= kCCAlgorithmRC4)
   8.128 +        return kCCAlgorithmNames[a];
   8.129 +    else
   8.130 +        return "???";
   8.131 +}
   8.132 +
   8.133 +- (unsigned) keySizeInBits {
   8.134 +    id keySize = [self _attribute: kSecAttrKeySizeInBits];
   8.135 +    Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits");
   8.136 +    return [keySize unsignedIntValue];
   8.137 +}
   8.138 +
   8.139 +
   8.140 +- (NSString*) description {
   8.141 +    return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
   8.142 +}
   8.143 +
   8.144 +
   8.145 +- (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
   8.146 +{
   8.147 +    NSData *keyData = self.keyData;
   8.148 +    Assert(keyData, @"Couldn't get key data");
   8.149 +    NSMutableData *output = [NSMutableData dataWithLength: data.length + 256];
   8.150 +    size_t bytesWritten = 0;
   8.151 +    CCCryptorStatus status = CCCrypt(op, self.algorithm, options, 
   8.152 +                                     keyData.bytes, keyData.length, NULL,
   8.153 +                                     data.bytes, data.length, output.mutableBytes, output.length,
   8.154 +                                     &bytesWritten);
   8.155 +    if (status) {
   8.156 +        Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
   8.157 +        return nil;
   8.158 +    }
   8.159 +    output.length = bytesWritten;
   8.160 +    return output;
   8.161 +}
   8.162 +
   8.163 +- (NSData*) encryptData: (NSData*)data {
   8.164 +    return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
   8.165 +}
   8.166 +
   8.167 +
   8.168 +- (NSData*) decryptData: (NSData*)data {
   8.169 +    return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
   8.170 +}
   8.171 +
   8.172 +
   8.173 +@end
   8.174 +
   8.175 +
   8.176 +#endif MYCRYPTO_USE_IPHONE_API
     9.1 --- a/MYSymmetricKey.h	Tue Apr 14 18:34:52 2009 -0700
     9.2 +++ b/MYSymmetricKey.h	Sat Apr 18 18:12:06 2009 -0700
     9.3 @@ -11,6 +11,11 @@
     9.4  
     9.5  
     9.6  @interface MYSymmetricKey : MYKey <MYEncryption, MYDecryption>
     9.7 +{
     9.8 +#if !MYCRYPTO_USE_IPHONE_API
     9.9 +    CSSM_KEY *_ownedCSSMKey;
    9.10 +#endif
    9.11 +}
    9.12  
    9.13  /** Initializes a symmetric key from the given key data and algorithm. */
    9.14  - (id) initWithKeyData: (NSData*)keyData
    9.15 @@ -22,10 +27,41 @@
    9.16  + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
    9.17                                       algorithm: (CCAlgorithm)algorithm;
    9.18  
    9.19 +/** Converts a passphrase into a symmetric key.
    9.20 +    The same passphrase (and salt) will always return the same key, so you can use this method
    9.21 +    to encrypt and decrypt data using a user-entered passphrase, without having to store the key
    9.22 +    itself in the keychain.
    9.23 +    @param alertTitle  A title for the alert (this seems to be ignored by the OS).
    9.24 +    @param prompt  A prompt string displayed in the alert.
    9.25 +    @param creating  Is a new passphrase being created? If YES, the user will have to enter the
    9.26 +        passphrase twice, to check for errors, and the nifty passphrase-strength meter will be
    9.27 +        displayed. If NO, there's only one text-field, and an option to display its contents in
    9.28 +        the clear.
    9.29 +    @param salt  An arbitrary value whose data will be mixed in with the passphrase before
    9.30 +        hashing, to perturb the resulting bits. The purpose of this is to make it harder for
    9.31 +        an attacker to brute-force the key using a precompiled list of digests of common
    9.32 +        passwords. Changing the salt changes the key, so you need to pass the same value when
    9.33 +        re-deriving the key as you did when first generating it. */
    9.34 + + (MYSymmetricKey*) generateFromUserPassphraseWithAlertTitle: (NSString*)alertTitle
    9.35 +                                                 alertPrompt: (NSString*)prompt
    9.36 +                                                    creating: (BOOL)creating
    9.37 +                                                        salt: (id)saltObj;
    9.38 +
    9.39  /** The key's algorithm. */
    9.40  @property (readonly) CCAlgorithm algorithm;
    9.41  
    9.42  /** The key's size/length, in bits. */
    9.43  @property (readonly) unsigned keySizeInBits;
    9.44  
    9.45 +
    9.46 +/** A utility that prompts for a passphrase, using the Security agent's nice modal panel,
    9.47 +    and returns the raw passphrase as a string.
    9.48 +    @param alertTitle  A title for the alert (this seems to be ignored by the OS).
    9.49 +    @param prompt  A prompt string displayed in the alert.
    9.50 +    @param creating  Is a new passphrase being created? 
    9.51 +        (See description in +generateFromUserPassphrase... method.) */
    9.52 ++ (NSString*) promptForPassphraseWithAlertTitle: (NSString*)alertTitle
    9.53 +                                    alertPrompt: (NSString*)prompt
    9.54 +                                       creating: (BOOL)creating;
    9.55 +
    9.56  @end
    10.1 --- a/MYSymmetricKey.m	Tue Apr 14 18:34:52 2009 -0700
    10.2 +++ b/MYSymmetricKey.m	Sat Apr 18 18:12:06 2009 -0700
    10.3 @@ -10,23 +10,10 @@
    10.4  #import "MYCryptor.h"
    10.5  #import "MYCrypto_Private.h"
    10.6  
    10.7 +#if !MYCRYPTO_USE_IPHONE_API
    10.8  
    10.9 -#if MYCRYPTO_USE_IPHONE_API
   10.10 -typedef uint32_t CSSM_ALGORITHMS;
   10.11 -enum {
   10.12 -// Taken from cssmtype.h in OS X 10.5 SDK:
   10.13 -	CSSM_ALGID_NONE =					0x00000000L,
   10.14 -	CSSM_ALGID_DES =					CSSM_ALGID_NONE + 14,
   10.15 -	CSSM_ALGID_3DES_3KEY_EDE =			CSSM_ALGID_NONE + 17,
   10.16 -	CSSM_ALGID_3DES_3KEY =           	CSSM_ALGID_3DES_3KEY_EDE,
   10.17 -	CSSM_ALGID_RC4 =					CSSM_ALGID_NONE + 25,
   10.18 -	CSSM_ALGID_CAST =					CSSM_ALGID_NONE + 27,
   10.19 -	CSSM_ALGID_VENDOR_DEFINED =			CSSM_ALGID_NONE + 0x80000000L,
   10.20 -	CSSM_ALGID_AES
   10.21 -};
   10.22 -#else
   10.23  #import <Security/cssmtype.h>
   10.24 -#endif
   10.25 +
   10.26  
   10.27  static const CSSM_ALGORITHMS kCSSMAlgorithms[] = {
   10.28      CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4
   10.29 @@ -35,54 +22,56 @@
   10.30  static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"};
   10.31  
   10.32  
   10.33 +/** Undocumented Security function. Unfortunately this is the only way I can find to create
   10.34 +    a SecKeyRef from a CSSM_KEY. */
   10.35 +extern OSStatus SecKeyCreate(const CSSM_KEY *key, SecKeyRef* keyRef) WEAK_IMPORT_ATTRIBUTE;
   10.36 +
   10.37 +static CSSM_KEY* cssmKeyFromData( NSData *keyData, CSSM_ALGORITHMS algorithm,
   10.38 +                                 MYKeychain *keychain);
   10.39 +static CSSM_DATA makeSalt( id salty, size_t length );
   10.40 +static CSSM_RETURN impExpCreatePassKey(
   10.41 +	const SecKeyImportExportParameters *keyParams,  // required
   10.42 +	CSSM_CSP_HANDLE		cspHand,		// MUST be CSPDL
   10.43 +	BOOL                verifyPhrase,   // use 2nd passphrase textfield for verification?
   10.44 +	CSSM_KEY_PTR		*passKey);	// mallocd and RETURNED
   10.45 +
   10.46 +
   10.47  #pragma mark -
   10.48  @implementation MYSymmetricKey
   10.49  
   10.50  
   10.51 +- (id) _initWithCSSMKey: (CSSM_KEY*)cssmKey {
   10.52 +    SecKeyRef keyRef = NULL;
   10.53 +    if (SecKeyCreate == NULL) {
   10.54 +        // If weak-linked SPI fn no longer exists
   10.55 +        Warn(@"Unable to call SecKeyCreate SPI -- not available");
   10.56 +        [self release];
   10.57 +        return nil;
   10.58 +    }
   10.59 +    if (!check(SecKeyCreate(cssmKey,&keyRef), @"SecKeyCreate")) {
   10.60 +        [self release];
   10.61 +        return nil;
   10.62 +    }
   10.63 +    self = [self initWithKeyRef: keyRef];
   10.64 +    if (self) {
   10.65 +        _ownedCSSMKey = cssmKey;            // (so I'll remember to free it)
   10.66 +    }
   10.67 +    return self;
   10.68 +}
   10.69 +
   10.70 +
   10.71  - (id) _initWithKeyData: (NSData*)keyData
   10.72                algorithm: (CCAlgorithm)algorithm
   10.73               inKeychain: (MYKeychain*)keychain
   10.74  {
   10.75      Assert(algorithm <= kCCAlgorithmRC4);
   10.76      Assert(keyData);
   10.77 -    SecKeyRef keyRef = NULL;
   10.78 -#if MYCRYPTO_USE_IPHONE_API
   10.79 -    NSNumber *keySizeInBits = [NSNumber numberWithUnsignedInt: keyData.length * 8];
   10.80 -    NSDictionary *keyAttrs = $dict( {(id)kSecClass, (id)kSecClassKey},
   10.81 -                                    //{(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
   10.82 -                                    {(id)kSecAttrKeyType, [NSNumber numberWithUnsignedInt: kCSSMAlgorithms[algorithm]]},
   10.83 -                                    {(id)kSecAttrKeySizeInBits, keySizeInBits},
   10.84 -                                    {(id)kSecAttrEffectiveKeySize, keySizeInBits},
   10.85 -                                    {(id)kSecAttrIsPermanent, keychain ?$true :$false},
   10.86 -                                    {(id)kSecAttrCanEncrypt, $true},
   10.87 -                                    {(id)kSecAttrCanDecrypt, $true},
   10.88 -                                    {(id)kSecAttrCanWrap, $false},
   10.89 -                                    {(id)kSecAttrCanUnwrap, $false},
   10.90 -                                    {(id)kSecAttrCanDerive, $false},
   10.91 -                                    {(id)kSecAttrCanSign, $false},
   10.92 -                                    {(id)kSecAttrCanVerify, $false},
   10.93 -                                    {(id)kSecValueData, keyData},
   10.94 -                                    {(id)kSecReturnPersistentRef, $true});
   10.95 -    if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) {
   10.96 +    CSSM_KEY *key = cssmKeyFromData(keyData, kCSSMAlgorithms[algorithm], keychain);
   10.97 +    if (!key) {
   10.98          [self release];
   10.99          return nil;
  10.100      }
  10.101 -    Assert(keyRef, @"SecItemAdd didn't return anything");
  10.102 -#else
  10.103 -    Assert(NO,@"Unimplemented"); //FIX
  10.104 -    /* The technique below doesn't work, because there's no way to tell SecKeychainItemImport
  10.105 -       what algorithm to use when importing a raw key. Still looking for a solution... --jpa 4/2009
  10.106 -    SecKeyImportExportParameters params = {};
  10.107 -    keyRef = importKey(keyData, kSecItemTypeSessionKey, keychain.keychainRefOrDefault, &params);
  10.108 -    if (!keyRef) {
  10.109 -        [self release];
  10.110 -        return nil;
  10.111 -    }
  10.112 -     */
  10.113 -#endif
  10.114 -    self = [self initWithKeyRef: keyRef];
  10.115 -    CFRelease(keyRef);
  10.116 -    return self;
  10.117 +    return [self _initWithCSSMKey: key];
  10.118  }
  10.119  
  10.120  - (id) initWithKeyData: (NSData*)keyData
  10.121 @@ -95,12 +84,6 @@
  10.122                                        algorithm: (CCAlgorithm)algorithm
  10.123                                       inKeychain: (MYKeychain*)keychain
  10.124  {
  10.125 -#if MYCRYPTO_USE_IPHONE_API
  10.126 -    return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
  10.127 -                                 algorithm: algorithm
  10.128 -                                inKeychain: keychain]
  10.129 -                    autorelease];
  10.130 -#else
  10.131      Assert(algorithm <= kCCAlgorithmRC4);
  10.132      CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
  10.133      if (keychain)
  10.134 @@ -115,7 +98,6 @@
  10.135          return nil;
  10.136      }
  10.137      return [[[self alloc] initWithKeyRef: keyRef] autorelease];
  10.138 -#endif
  10.139  }
  10.140  
  10.141  + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
  10.142 @@ -125,44 +107,173 @@
  10.143                                    inKeychain: nil];
  10.144  }
  10.145  
  10.146 ++ (NSString*) promptForPassphraseWithAlertTitle: (NSString*)alertTitle
  10.147 +                                    alertPrompt: (NSString*)prompt
  10.148 +                                        creating: (BOOL)creating
  10.149 +{
  10.150 +    // Ask the user for a passphrase:
  10.151 +    SecKeyImportExportParameters params = {
  10.152 +        .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
  10.153 +        .flags = kSecKeySecurePassphrase,
  10.154 +        .alertTitle = (CFStringRef)alertTitle,
  10.155 +        .alertPrompt = (CFStringRef)prompt,
  10.156 +        .keyUsage = CSSM_KEYUSE_ANY,
  10.157 +        .keyAttributes = CSSM_KEYATTR_EXTRACTABLE
  10.158 +    };
  10.159 +    CSSM_CSP_HANDLE cspHandle = [[MYKeychain defaultKeychain] CSPHandle];
  10.160 +    CSSM_KEY *passphraseKey = NULL;
  10.161 +    if (impExpCreatePassKey(&params, 
  10.162 +                            cspHandle, 
  10.163 +                            creating,
  10.164 +                            &passphraseKey) != CSSM_OK)
  10.165 +        return nil;
  10.166 +    
  10.167 +    MYSymmetricKey *key = [[self alloc] _initWithCSSMKey: passphraseKey];
  10.168 +    NSData *keyData = key.keyData;
  10.169 +    Assert(keyData);
  10.170 +    NSString *passphrase = [[NSString alloc] initWithData: keyData
  10.171 +                                                 encoding: NSUTF8StringEncoding];
  10.172 +    [key release];
  10.173 +    return [passphrase autorelease];
  10.174 +}
  10.175 +
  10.176 +
  10.177 +#define PKCS5_V2_SALT_LEN		8
  10.178 +#define PKCS5_V2_ITERATIONS		2048
  10.179 +#define PKCS5_V2_DES_IV_SIZE	8
  10.180 +
  10.181 ++ (MYSymmetricKey*) generateFromUserPassphraseWithAlertTitle: (NSString*)alertTitle
  10.182 +                                                 alertPrompt: (NSString*)prompt
  10.183 +                                                     creating: (BOOL)creating
  10.184 +                                                        salt: (id)saltObj
  10.185 +{
  10.186 +    MYSymmetricKey *generatedKey = nil;
  10.187 +
  10.188 +    // Ask the user for a passphrase:
  10.189 +    SecKeyImportExportParameters params = {
  10.190 +        .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
  10.191 +        .flags = kSecKeySecurePassphrase,
  10.192 +        .alertTitle = (CFStringRef)alertTitle,
  10.193 +        .alertPrompt = (CFStringRef)prompt,
  10.194 +        .keyUsage = CSSM_KEYUSE_ANY,
  10.195 +        .keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_SENSITIVE
  10.196 +    };
  10.197 +    CSSM_CSP_HANDLE cspHandle = [[MYKeychain defaultKeychain] CSPHandle];
  10.198 +    CSSM_KEY *passphraseKey = NULL;
  10.199 +    if (impExpCreatePassKey(&params, 
  10.200 +                            cspHandle, 
  10.201 +                            creating,
  10.202 +                            &passphraseKey) != CSSM_OK)
  10.203 +        return nil;
  10.204 +
  10.205 +    CSSM_DATA saltData = makeSalt(saltObj,PKCS5_V2_SALT_LEN);
  10.206 +    CSSM_CRYPTO_DATA seed = {};
  10.207 +    
  10.208 +    // Now use the secure passphrase to generate a symmetric key:
  10.209 +    CSSM_CC_HANDLE ctx = 0;
  10.210 +    CSSM_ACCESS_CREDENTIALS credentials = {};
  10.211 +    if (checkcssm(CSSM_CSP_CreateDeriveKeyContext(cspHandle,
  10.212 +                                                  CSSM_ALGID_PKCS5_PBKDF2,
  10.213 +                                                  CSSM_ALGID_AES, 128,
  10.214 +                                                  &credentials,
  10.215 +                                                  passphraseKey, 
  10.216 +                                                  PKCS5_V2_ITERATIONS,
  10.217 +                                                  &saltData,
  10.218 +                                                  &seed,
  10.219 +                                                  &ctx),
  10.220 +                  @"CSSM_CSP_CreateDeriveKeyContext")) {
  10.221 +        CSSM_PKCS5_PBKDF2_PARAMS params = {.PseudoRandomFunction=CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1};
  10.222 +        CSSM_DATA paramData = {.Data=(void*)&params, .Length=sizeof(params)};
  10.223 +        CSSM_KEY *cssmKey = calloc(1,sizeof(CSSM_KEY));
  10.224 +        if (checkcssm(CSSM_DeriveKey(ctx,
  10.225 +                                     &paramData,
  10.226 +                                     CSSM_KEYUSE_ANY,
  10.227 +                                     CSSM_KEYATTR_EXTRACTABLE, //| CSSM_KEYATTR_SENSITIVE, 
  10.228 +                                     NULL, 
  10.229 +                                     NULL, 
  10.230 +                                     cssmKey),
  10.231 +                      @"CSSM_DeriveKey")) {
  10.232 +            generatedKey = [[[self alloc] _initWithCSSMKey: cssmKey] autorelease];
  10.233 +        }
  10.234 +    }
  10.235 +    CSSM_DeleteContext(ctx);
  10.236 +    CSSM_FreeKey(cspHandle, &credentials, passphraseKey, YES);
  10.237 +    return generatedKey;        
  10.238 +}
  10.239 +
  10.240 +
  10.241 +- (void) dealloc
  10.242 +{
  10.243 +    if(_ownedCSSMKey) 
  10.244 +        CSSM_FreeKey(self.cssmCSPHandle, NULL, _ownedCSSMKey, YES);
  10.245 +    [super dealloc];
  10.246 +}
  10.247 +
  10.248  
  10.249  #if !TARGET_OS_IPHONE
  10.250  - (NSData*) exportKeyInFormat: (SecExternalFormat)format
  10.251                        withPEM: (BOOL)withPEM
  10.252  {
  10.253 -    SecKeyImportExportParameters params = {
  10.254 -        .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
  10.255 -        .flags = kSecKeySecurePassphrase,
  10.256 -    };
  10.257 -    CFDataRef data = NULL;
  10.258 -    if (check(SecKeychainItemExport(self.keyRef,
  10.259 -                                    format, (withPEM ?kSecItemPemArmour :0), 
  10.260 -                                    &params, &data),
  10.261 -              @"SecKeychainItemExport"))
  10.262 -        return [(id)CFMakeCollectable(data) autorelease];
  10.263 -    else
  10.264 +    if (format==kSecFormatRawKey || format==kSecFormatUnknown)
  10.265 +        return [super exportKeyInFormat: format withPEM: withPEM];
  10.266 +    
  10.267 +    // Get access credentials:
  10.268 +    const CSSM_ACCESS_CREDENTIALS *credentials;
  10.269 +    credentials = [self cssmCredentialsForOperation: CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED 
  10.270 +                                               type: kSecCredentialTypeDefault
  10.271 +                                              error: nil];
  10.272 +    if (!credentials)
  10.273          return nil;
  10.274 +    
  10.275 +    // Prompt use for a passphrase to use for the wrapping key:
  10.276 +    MYSymmetricKey *wrappingKey = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Export Key" alertPrompt: @"Enter a passphrase to encrypt the key you're exporting from the Keychain:" creating: YES
  10.277 +            salt: [MYCryptor randomKeyOfLength: PKCS5_V2_SALT_LEN*8]];
  10.278 +    if (!wrappingKey)
  10.279 +        return nil;
  10.280 +    
  10.281 +    // Create the context:
  10.282 +    CSSM_CSP_HANDLE cspHandle = self.cssmCSPHandle;
  10.283 +    CSSM_CC_HANDLE ctx;
  10.284 +    if (!checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle,
  10.285 +                                                   CSSM_ALGID_NONE, 
  10.286 +                                                   CSSM_ALGMODE_WRAP,
  10.287 +                                                   NULL, 
  10.288 +                                                   wrappingKey.cssmKey,
  10.289 +                                                   NULL,
  10.290 +                                                   CSSM_PADDING_NONE,
  10.291 +                                                   NULL,
  10.292 +                                                   &ctx), 
  10.293 +                   @"CSSM_CSP_CreateSymmetricContext"))
  10.294 +        return nil;
  10.295 +    
  10.296 +    // Set the wrapped key format:
  10.297 +    NSData *result = nil;
  10.298 +    CSSM_CONTEXT_ATTRIBUTE attr = { CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, sizeof(uint32) };
  10.299 +    attr.Attribute.Uint32 = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7;
  10.300 +    if (checkcssm(CSSM_UpdateContextAttributes(ctx, 1, &attr), @"CSSM_UpdateContextAttributes")) {
  10.301 +        // Now wrap the key:
  10.302 +        CSSM_WRAP_KEY wrappedKey = {};
  10.303 +        if (checkcssm(CSSM_WrapKey(ctx, credentials, self.cssmKey, NULL, &wrappedKey),
  10.304 +                      @"CSSM_WrapKey")) {
  10.305 +            // ...and copy the wrapped key data to the result NSData:
  10.306 +            result = [NSData dataWithBytes: wrappedKey.KeyData.Data length: wrappedKey.KeyData.Length];
  10.307 +            CSSM_FreeKey(cspHandle, credentials, &wrappedKey, NO);
  10.308 +        }
  10.309 +    }
  10.310 +    // Finally, delete the context
  10.311 +    CSSM_DeleteContext(ctx);
  10.312 +    return result;
  10.313  }
  10.314  #endif
  10.315  
  10.316  
  10.317  - (SecExternalItemType) keyType {
  10.318 -#if MYCRYPTO_USE_IPHONE_API
  10.319 -    return kSecAttrKeyClassSymmetric;
  10.320 -#else
  10.321      return kSecItemTypeSessionKey;
  10.322 -#endif
  10.323  }
  10.324  
  10.325  - (CCAlgorithm) algorithm {
  10.326      CSSM_ALGORITHMS cssmAlg;
  10.327 -#if MYCRYPTO_USE_IPHONE_API
  10.328 -    id keyType = [self _attribute: kSecAttrKeyType];
  10.329 -    Assert(keyType!=nil, @"Key has no kSecAttrKeyType");
  10.330 -    cssmAlg = [keyType unsignedIntValue];
  10.331 -#else
  10.332      cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
  10.333 -#endif
  10.334      switch(cssmAlg) {
  10.335          case CSSM_ALGID_AES:
  10.336              return kCCAlgorithmAES128;
  10.337 @@ -189,15 +300,9 @@
  10.338  }
  10.339  
  10.340  - (unsigned) keySizeInBits {
  10.341 -#if MYCRYPTO_USE_IPHONE_API
  10.342 -    id keySize = [self _attribute: kSecAttrKeySizeInBits];
  10.343 -    Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits");
  10.344 -    return [keySize unsignedIntValue];
  10.345 -#else
  10.346      const CSSM_KEY *key = self.cssmKey;
  10.347      Assert(key);
  10.348      return key->KeyHeader.LogicalKeySizeInBits;
  10.349 -#endif
  10.350  }
  10.351  
  10.352  
  10.353 @@ -237,35 +342,214 @@
  10.354  @end
  10.355  
  10.356  
  10.357 -/* (Turned out I could just use SecKeyExport for this.)
  10.358 - 
  10.359 -static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) {
  10.360 -    CAssert(key);
  10.361 -    const CSSM_KEY* cssmKey;
  10.362 -    const CSSM_ACCESS_CREDENTIALS *credentials;
  10.363 -    CSSM_CSP_HANDLE cspHandle;
  10.364 +#pragma mark -
  10.365 +
  10.366 +
  10.367 +static CSSM_KEY* cssmKeyFromData( NSData *keyData, 
  10.368 +                                 CSSM_ALGORITHMS algorithm,
  10.369 +                                 MYKeychain *keychain ) {
  10.370 +    // Thanks to Jim Murphy for showing the way!
  10.371 +    if (!keychain) keychain = [MYKeychain defaultKeychain];
  10.372      CSSM_CC_HANDLE ccHandle;
  10.373 -    if (!check(SecKeyGetCSSMKey(key, &cssmKey), @"GetCSSMKey")
  10.374 -        || !check(SecKeyGetCredentials(key, 
  10.375 -                                       CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, 
  10.376 -                                       kSecCredentialTypeDefault,
  10.377 -                                       &credentials), @"GetCredentials")
  10.378 -        || !check(SecKeyGetCSPHandle(key, &cspHandle), @"GetCSPHandle")
  10.379 -        
  10.380 -        || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP,
  10.381 -                                                      NULL, NULL, NULL,
  10.382 -                                                      CSSM_PADDING_NONE, NULL, &ccHandle),
  10.383 -                      @"CSSM_CSP_CreateSymmetricContext"))
  10.384 -        return nil;
  10.385 +    if (!checkcssm(CSSM_CSP_CreateSymmetricContext([keychain CSPHandle],
  10.386 +                                                   CSSM_ALGID_NONE, CSSM_ALGMODE_WRAP,
  10.387 +                                                   NULL, NULL, NULL,
  10.388 +                                                   CSSM_PADDING_NONE, NULL,
  10.389 +                                                   &ccHandle), 
  10.390 +                   @"CSSM_CSP_CreateSymmetricContext"))
  10.391 +        return NO;
  10.392      
  10.393 -    CSSM_WRAP_KEY wrapped;
  10.394 -    NSData *result = nil;
  10.395 -    if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped),
  10.396 -                 @"CSSM_WrapKey")) {
  10.397 -        result = [NSData dataWithBytes: wrapped.KeyData.Data 
  10.398 -                                length: wrapped.KeyData.Length];
  10.399 +    CSSM_KEY wrappedKey = {
  10.400 +        .KeyHeader = {
  10.401 +            .BlobType = CSSM_KEYBLOB_RAW,
  10.402 +            .Format = CSSM_KEYBLOB_RAW_FORMAT_NONE,
  10.403 +            .AlgorithmId = algorithm,
  10.404 +            .KeyClass = CSSM_KEYCLASS_SESSION_KEY,
  10.405 +            .LogicalKeySizeInBits = keyData.length*8,
  10.406 +            .KeyAttr = CSSM_KEYATTR_EXTRACTABLE,
  10.407 +            .KeyUsage = CSSM_KEYUSE_ANY
  10.408 +        },
  10.409 +        .KeyData = {
  10.410 +            .Data = (void*)keyData.bytes,
  10.411 +            .Length = keyData.length
  10.412 +        }
  10.413 +    };
  10.414 +    
  10.415 +    CSSM_KEY *outKey = calloc(sizeof(CSSM_KEY),1);
  10.416 +    CSSM_DATA desc = {};
  10.417 +    if (!checkcssm(CSSM_UnwrapKey(ccHandle,
  10.418 +                                  NULL,
  10.419 +                                  &wrappedKey,
  10.420 +                                  CSSM_KEYUSE_ANY,
  10.421 +                                  CSSM_KEYATTR_EXTRACTABLE,
  10.422 +                                  NULL,
  10.423 +                                  NULL,
  10.424 +                                  outKey,
  10.425 +                                  &desc),
  10.426 +                   @"CSSM_UnwrapKey")) {
  10.427 +        free(outKey);
  10.428 +        outKey = NULL;
  10.429      }
  10.430      CSSM_DeleteContext(ccHandle);
  10.431 -    return result;
  10.432 +    return outKey;
  10.433  }
  10.434 +
  10.435 +
  10.436 +// Create salt data of a specific length from an arbitrary NSObject. */
  10.437 +static CSSM_DATA makeSalt( id salty, size_t length ) {
  10.438 +    // Convert to NSData if necessary:
  10.439 +    CAssert(salty!=nil);
  10.440 +    if (![salty isKindOfClass: [NSData class]])
  10.441 +        salty = [[salty description] dataUsingEncoding: NSUTF8StringEncoding];
  10.442 +    // Repeat enough times to fill the desired length:
  10.443 +    NSMutableData *salt = [[salty mutableCopy] autorelease];
  10.444 +    CAssert(salt.length>0);
  10.445 +    while (salt.length < length) {
  10.446 +        [salt appendData: salt];
  10.447 +    }
  10.448 +    // Truncate to length and return it:
  10.449 +    salt.length = length;
  10.450 +    return (CSSM_DATA){.Data=(void*)salt.bytes, .Length=salt.length};
  10.451 +}
  10.452 +
  10.453 +
  10.454 +// Copied from SecImportExportUtils.cpp in Apple's libsecurity_keychain project
  10.455 +/*
  10.456 + * Given a context specified via a CSSM_CC_HANDLE, add a new
  10.457 + * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
  10.458 + * AttributeLength, and an untyped pointer.
  10.459 + */
  10.460 +static CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
  10.461 +	uint32 AttributeType,
  10.462 +	uint32 AttributeLength,
  10.463 +	const void *AttributePtr)
  10.464 +{
  10.465 +	CSSM_CONTEXT_ATTRIBUTE		newAttr;	
  10.466 +	
  10.467 +	newAttr.AttributeType     = AttributeType;
  10.468 +	newAttr.AttributeLength   = AttributeLength;
  10.469 +	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
  10.470 +	return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
  10.471 +}
  10.472 +
  10.473 +/* 
  10.474 +* Add a CFString to a crypto context handle. 
  10.475  */
  10.476 +static CSSM_RETURN impExpAddStringAttr(
  10.477 +	CSSM_CC_HANDLE ccHand, 
  10.478 +	CFStringRef str,
  10.479 +	CSSM_ATTRIBUTE_TYPE attrType)
  10.480 +{
  10.481 +	/* CFStrings are passed as external rep in UTF8 encoding by convention */
  10.482 +	CFDataRef outData;
  10.483 +	outData = CFStringCreateExternalRepresentation(NULL,
  10.484 +		str, kCFStringEncodingUTF8,	0);		// lossByte 0 ==> no loss allowed 
  10.485 +	if(outData == NULL) {
  10.486 +		Warn(@"impExpAddStringAttr: bad string format");
  10.487 +		return paramErr;
  10.488 +	}
  10.489 +	
  10.490 +	CSSM_DATA attrData;
  10.491 +	attrData.Data = (uint8 *)CFDataGetBytePtr(outData);
  10.492 +	attrData.Length = CFDataGetLength(outData);
  10.493 +	CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA),
  10.494 +		&attrData);
  10.495 +	CFRelease(outData);
  10.496 +	if(crtn) {
  10.497 +		Warn(@"impExpAddStringAttr: CSSM_UpdateContextAttributes error");
  10.498 +	}
  10.499 +	return crtn;
  10.500 +}
  10.501 +
  10.502 +/*
  10.503 + * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result. 
  10.504 + */
  10.505 +static CSSM_RETURN impExpCreatePassKey(
  10.506 +	const SecKeyImportExportParameters *keyParams,  // required
  10.507 +	CSSM_CSP_HANDLE		cspHand,		// MUST be CSPDL
  10.508 +	BOOL                verifyPhrase,   // use 2nd passphrase textfield for verification?
  10.509 +	CSSM_KEY_PTR		*passKey)		// mallocd and RETURNED
  10.510 +{
  10.511 +	CSSM_RETURN crtn;
  10.512 +	CSSM_CC_HANDLE ccHand;
  10.513 +	uint32 verifyAttr;
  10.514 +	CSSM_DATA dummyLabel;
  10.515 +	CSSM_KEY_PTR ourKey = NULL;
  10.516 +	
  10.517 +	Log(@"Generating secure passphrase key");
  10.518 +	ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
  10.519 +	if(ourKey == NULL) {
  10.520 +		return memFullErr;
  10.521 +	}
  10.522 +	memset(ourKey, 0, sizeof(CSSM_KEY));
  10.523 +	
  10.524 +	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
  10.525 +		CSSM_ALGID_SECURE_PASSPHRASE,
  10.526 +		4,				// keySizeInBits must be non zero
  10.527 +		NULL,			// Seed
  10.528 +		NULL,			// Salt
  10.529 +		NULL,			// StartDate
  10.530 +		NULL,			// EndDate
  10.531 +		NULL,			// Params
  10.532 +		&ccHand);
  10.533 +	if(crtn) {
  10.534 +		checkcssm(crtn,@"CSSM_CSP_CreateKeyGenContext");
  10.535 +		return crtn;
  10.536 +	}
  10.537 +	/* subsequent errors to errOut: */
  10.538 +	
  10.539 +	/* additional context attributes specific to this type of key gen */
  10.540 +	CAssert(keyParams != NULL);			// or we wouldn't be here
  10.541 +	if(keyParams->alertTitle != NULL) {
  10.542 +		crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle, 
  10.543 +			CSSM_ATTRIBUTE_ALERT_TITLE);
  10.544 +		if(crtn) {
  10.545 +			goto errOut;
  10.546 +		}
  10.547 +	}
  10.548 +	if(keyParams->alertPrompt != NULL) {
  10.549 +		crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt, 
  10.550 +			CSSM_ATTRIBUTE_PROMPT);
  10.551 +		if(crtn) {
  10.552 +			goto errOut;
  10.553 +		}
  10.554 +	}
  10.555 +	verifyAttr = verifyPhrase ? 1 : 0;
  10.556 +	crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE,
  10.557 +		sizeof(uint32), (const void *)verifyAttr);
  10.558 +	if(crtn) {
  10.559 +		checkcssm(crtn,@"impExpAddContextAttribute");
  10.560 +		goto errOut;
  10.561 +	}
  10.562 +
  10.563 +	dummyLabel.Data = (uint8 *)"Secure Passphrase";
  10.564 +	dummyLabel.Length = strlen((char *)dummyLabel.Data);
  10.565 +    
  10.566 +    uint32 keyAttr = keyParams->keyAttributes;
  10.567 +    if (keyAttr & CSSM_KEYATTR_SENSITIVE)
  10.568 +        keyAttr |= CSSM_KEYATTR_RETURN_REF;
  10.569 +    else
  10.570 +        keyAttr |= CSSM_KEYATTR_EXTRACTABLE;
  10.571 +
  10.572 +	crtn = CSSM_GenerateKey(ccHand,
  10.573 +        keyParams->keyUsage ?: CSSM_KEYUSE_ANY,
  10.574 +		keyAttr,
  10.575 +		&dummyLabel,
  10.576 +		NULL,			// ACL
  10.577 +		ourKey);
  10.578 +	if(crtn) {
  10.579 +		checkcssm(crtn,@"CSSM_GenerateKey");
  10.580 +	}
  10.581 +errOut:
  10.582 +	CSSM_DeleteContext(ccHand);
  10.583 +	if(crtn == CSSM_OK) {
  10.584 +		*passKey = ourKey;
  10.585 +	}
  10.586 +	else if(ourKey != NULL) {
  10.587 +		free(ourKey);
  10.588 +	}
  10.589 +	return crtn;
  10.590 +}
  10.591 +	
  10.592 +
  10.593 +#endif !MYCRYPTO_USE_IPHONE_API