Working on export/import of symmetric keys, and passphrase entry. Not ready for release quite yet.
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, ¶ms);
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(¶ms,
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(¶ms,
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*)¶ms, .Length=sizeof(params)};
10.223 + CSSM_KEY *cssmKey = calloc(1,sizeof(CSSM_KEY));
10.224 + if (checkcssm(CSSM_DeriveKey(ctx,
10.225 + ¶mData,
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 - ¶ms, &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