# HG changeset patch # User snej@snej.local # Date 1240103526 25200 # Node ID e4c971be4079f5f4f97030550ac889a3ae644265 # Parent 3568d5fd4b6a1dfe672c7518ac45915b7b8e2157 Working on export/import of symmetric keys, and passphrase entry. Not ready for release quite yet. diff -r 3568d5fd4b6a -r e4c971be4079 MYCertificate.m --- a/MYCertificate.m Tue Apr 14 18:34:52 2009 -0700 +++ b/MYCertificate.m Sat Apr 18 18:12:06 2009 -0700 @@ -236,6 +236,23 @@ } +// Taken from Keychain.framework +NSString* OIDAsString(const CSSM_OID oid) { + if ((NULL == oid.Data) || (0 >= oid.Length)) { + return nil; + } else { + NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)]; + unsigned int i; + + for (i = 0; i < oid.Length; ++i) { + [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]]; + } + + return result; + } +} + + TestCase(Trust) { Log(@"X.509 policy = %@", MYPolicyGetName([MYCertificate X509Policy])); diff -r 3568d5fd4b6a -r e4c971be4079 MYCrypto-iPhone.xcodeproj/project.pbxproj --- a/MYCrypto-iPhone.xcodeproj/project.pbxproj Tue Apr 14 18:34:52 2009 -0700 +++ b/MYCrypto-iPhone.xcodeproj/project.pbxproj Sat Apr 18 18:12:06 2009 -0700 @@ -14,6 +14,7 @@ 27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059CF20F8F0F9200A8422F /* MYIdentity.m */; }; 273391CD0F81EC70009414D9 /* MYCertificate-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */; }; 273392120F8283B8009414D9 /* MYKeychain-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 273392110F8283B8009414D9 /* MYKeychain-iPhone.m */; }; + 274110090F99234100AD413F /* MYSymmetricKey-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */; }; 2748607F0F8D5E0600FE617B /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 2748607E0F8D5E0600FE617B /* MYPrivateKey.m */; }; 276FB13F0F84090900CB326E /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 276FB13E0F84090900CB326E /* Security.framework */; }; 276FB16E0F84152B00CB326E /* MYCryptoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 276FB16D0F84152B00CB326E /* MYCryptoTest.m */; }; @@ -48,6 +49,7 @@ 27059CF20F8F0F9200A8422F /* MYIdentity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYIdentity.m; sourceTree = ""; }; 273391CC0F81EC70009414D9 /* MYCertificate-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYCertificate-iPhone.m"; sourceTree = ""; }; 273392110F8283B8009414D9 /* MYKeychain-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYKeychain-iPhone.m"; sourceTree = ""; }; + 274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYSymmetricKey-iPhone.m"; sourceTree = ""; }; 2748607D0F8D5DF200FE617B /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = ""; }; 2748607E0F8D5E0600FE617B /* MYPrivateKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPrivateKey.m; sourceTree = ""; }; 276FB13E0F84090900CB326E /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; }; @@ -157,6 +159,7 @@ 276FB3180F856AA700CB326E /* MYPublicKey.m */, 27A430150F87C6DB0063D362 /* MYSymmetricKey.h */, 27A430130F87C6D50063D362 /* MYSymmetricKey.m */, + 274110080F99234100AD413F /* MYSymmetricKey-iPhone.m */, 27E8231B0F81D56E0019BE60 /* MYDigest.h */, 27E8231C0F81D56E0019BE60 /* MYDigest.m */, 27E8231A0F81D56E0019BE60 /* MYCrypto_Private.h */, @@ -286,6 +289,7 @@ 2748607F0F8D5E0600FE617B /* MYPrivateKey.m in Sources */, 27059CF30F8F0F9200A8422F /* MYIdentity.m in Sources */, 27E3A6AA0F91B8B30079D4D9 /* MYCryptor.m in Sources */, + 274110090F99234100AD413F /* MYSymmetricKey-iPhone.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff -r 3568d5fd4b6a -r e4c971be4079 MYCrypto.xcodeproj/project.pbxproj --- a/MYCrypto.xcodeproj/project.pbxproj Tue Apr 14 18:34:52 2009 -0700 +++ b/MYCrypto.xcodeproj/project.pbxproj Sat Apr 18 18:12:06 2009 -0700 @@ -12,6 +12,7 @@ 27059D840F8FA82500A8422F /* SecurityInterface.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 27059D830F8FA82500A8422F /* SecurityInterface.framework */; }; 27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */ = {isa = PBXBuildFile; fileRef = 27059D520F8F9BB500A8422F /* MYDecoder.m */; }; 270B879F0F8C565000C56781 /* MYPrivateKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 270B879E0F8C565000C56781 /* MYPrivateKey.m */; }; + 27410FF00F99200A00AD413F /* MYSymmetricKey-iPhone.m in Sources */ = {isa = PBXBuildFile; fileRef = 27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */; }; 274861D50F8E4B5200FE617B /* MYCertGen.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42ECD0F8689D30063D362 /* MYCertGen.m */; }; 274863A20F8EF39400FE617B /* MYIdentity.m in Sources */ = {isa = PBXBuildFile; fileRef = 274863A10F8EF39400FE617B /* MYIdentity.m */; }; 27A42CBF0F8578B40063D362 /* MYCryptoTest.m in Sources */ = {isa = PBXBuildFile; fileRef = 27A42CBE0F8578B40063D362 /* MYCryptoTest.m */; }; @@ -57,6 +58,7 @@ 27059D830F8FA82500A8422F /* SecurityInterface.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SecurityInterface.framework; path = /System/Library/Frameworks/SecurityInterface.framework; sourceTree = ""; }; 270B879D0F8C565000C56781 /* MYPrivateKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYPrivateKey.h; sourceTree = ""; }; 270B879E0F8C565000C56781 /* MYPrivateKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYPrivateKey.m; sourceTree = ""; }; + 27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MYSymmetricKey-iPhone.m"; sourceTree = ""; }; 2748604D0F8D5C4C00FE617B /* MYCrypto_Release.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = MYCrypto_Release.xcconfig; sourceTree = ""; }; 274863A00F8EF39400FE617B /* MYIdentity.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MYIdentity.h; sourceTree = ""; }; 274863A10F8EF39400FE617B /* MYIdentity.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MYIdentity.m; sourceTree = ""; }; @@ -155,6 +157,7 @@ 270B879E0F8C565000C56781 /* MYPrivateKey.m */, 27A42D400F858ED80063D362 /* MYSymmetricKey.h */, 27A42D410F858ED80063D362 /* MYSymmetricKey.m */, + 27410FEF0F99200A00AD413F /* MYSymmetricKey-iPhone.m */, 27AAD97B0F892A0D0064DD7C /* MYCryptoConfig.h */, 27059D750F8FA23100A8422F /* MYCrypto+Cocoa.h */, 27059D760F8FA23100A8422F /* MYCrypto+Cocoa.m */, @@ -321,6 +324,7 @@ 27059D530F8F9BB500A8422F /* MYEncoder.m in Sources */, 27059D770F8FA23100A8422F /* MYCrypto+Cocoa.m in Sources */, 27059DE50F8FAF6500A8422F /* MYDecoder.m in Sources */, + 27410FF00F99200A00AD413F /* MYSymmetricKey-iPhone.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff -r 3568d5fd4b6a -r e4c971be4079 MYCryptoTest.m --- a/MYCryptoTest.m Tue Apr 14 18:34:52 2009 -0700 +++ b/MYCryptoTest.m Sat Apr 18 18:12:06 2009 -0700 @@ -11,7 +11,9 @@ #import "MYKeychain.h" #import "MYDigest.h" #import "MYIdentity.h" +#if !TARGET_OS_IPHONE #import "MYCrypto+Cocoa.h" +#endif #import "MYCrypto_Private.h" @@ -98,57 +100,85 @@ #pragma mark SYMMETRIC KEYS: -static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits ) { +static void testSymmetricKey( CCAlgorithm algorithm, unsigned sizeInBits, MYKeychain *inKeychain ) { NSAutoreleasePool *pool = [NSAutoreleasePool new]; - Log(@"--- Testing %3u-bit #%i", sizeInBits, (int)algorithm); - // Generate key: - MYSymmetricKey *key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits - algorithm: algorithm]; - Log(@"Created %@", key); + MYSymmetricKey *key = nil; + @try{ + Log(@"--- Testing %3u-bit #%i %s", sizeInBits, (int)algorithm, + (inKeychain ?", in keychain" :"")); + // Generate key: + if (inKeychain) + key = [inKeychain generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm]; + else + key = [MYSymmetricKey generateSymmetricKeyOfSize: sizeInBits algorithm: algorithm]; + Log(@"Created %@", key); CAssert(key); - CAssertEq(key.algorithm, algorithm); - CAssertEq(key.keySizeInBits, sizeInBits); -#if !TARGET_OS_IPHONE - CAssert(key.cssmKey != NULL); -#endif - - NSData *keyData = key.keyData; - Log(@"Key data = %@", keyData); - CAssertEq(keyData.length, sizeInBits/8); - - // Encrypt a small amount of text: - NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding]; - NSData *encrypted = [key encryptData: cleartext]; - Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted); - CAssert(encrypted.length >= cleartext.length); - NSData *decrypted = [key decryptData: encrypted]; - CAssertEqual(decrypted, cleartext); - - // Encrypt large binary data: - cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"]; - CAssert(cleartext); - encrypted = [key encryptData: cleartext]; - Log(@"Encrypted = %u bytes", encrypted.length); - CAssert(encrypted.length >= cleartext.length); - decrypted = [key decryptData: encrypted]; - CAssertEqual(decrypted, cleartext); - -#if !TARGET_OS_IPHONE - // Try reconstituting the key from its data: - NSData *exported = [key exportKeyInFormat: kSecFormatWrappedPKCS8 withPEM: NO]; - Log(@"Exported key: %@", exported); - // CAssert(exported); - //FIX: Exporting symmetric keys isn't working. Temporarily making this optional. - if (exported) { - CAssert(exported); - MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: exported algorithm: algorithm]; - Log(@"Reconstituted as %@", key2); - CAssertEqual(key2,key); + CAssertEq(key.algorithm, algorithm); + CAssertEq(key.keySizeInBits, sizeInBits); + #if !TARGET_OS_IPHONE + CAssert(key.cssmKey != NULL); + #endif + + NSData *keyData = key.keyData; + Log(@"Key data = %@", keyData); + CAssertEq(keyData.length, sizeInBits/8); + + // Encrypt a small amount of text: + Log(@"Testing encryption / decryption ..."); + NSData *cleartext = [@"This is a test. This is only a test." dataUsingEncoding: NSUTF8StringEncoding]; + NSData *encrypted = [key encryptData: cleartext]; + Log(@"Encrypted = %u bytes: %@", encrypted.length, encrypted); + CAssert(encrypted.length >= cleartext.length); + NSData *decrypted = [key decryptData: encrypted]; + CAssertEqual(decrypted, cleartext); + + // Encrypt large binary data: + cleartext = [NSData dataWithContentsOfFile: @"/Library/Desktop Pictures/Nature/Zen Garden.jpg"]; + CAssert(cleartext); + encrypted = [key encryptData: cleartext]; + Log(@"Encrypted = %u bytes", encrypted.length); + CAssert(encrypted.length >= cleartext.length); + decrypted = [key decryptData: encrypted]; + CAssertEqual(decrypted, cleartext); + + #if 1 + Log(@"Testing initWithKeyData:..."); + MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: keyData algorithm: algorithm]; + CAssert(key2); + Log(@"Key from data = %@",key2); + CAssertEqual(key2.keyData, keyData); + CAssertEq(key2.algorithm, algorithm); + CAssertEq(key2.keySizeInBits, sizeInBits); decrypted = [key2 decryptData: encrypted]; CAssertEqual(decrypted, cleartext); - } else - Warn(@"Unable to export key in PKCS8"); -#endif + [key2 release]; + #endif + + #if !TARGET_OS_IPHONE + // Try exporting and importing a wrapped key: + Log(@"Testing export/import..."); + NSData *exported = [key exportKeyInFormat: kSecFormatWrappedPKCS8 withPEM: NO]; + Log(@"Exported key: %@", exported); + #if 0 + CAssert(exported); + #else + //FIX: Exporting symmetric keys isn't working. Temporarily making this optional. + if (!exported) + Warn(@"Unable to export wrapped key"); + else + #endif + { + CAssert(exported); + MYSymmetricKey *key2 = [[MYSymmetricKey alloc] initWithKeyData: exported algorithm: algorithm]; + Log(@"Reconstituted as %@", key2); + CAssertEqual(key2.keyData,key.keyData); + decrypted = [key2 decryptData: encrypted]; + CAssertEqual(decrypted, cleartext); + } + #endif + }@finally{ + [key removeFromKeychain]; + } [pool drain]; } @@ -167,8 +197,49 @@ 40, 80, 128, 32, 200, 512*8}; - for (int i=0; i= cleartext.length); + NSData *decrypted = [key decryptData: encrypted]; + CAssertEqual(decrypted, cleartext); + + // Now test decryption by re-entered passphrase: + Log(@"Testing decryption using re-entered passphrase..."); + MYSymmetricKey *key2 = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Symmetric Key Passphrase Test Case" + alertPrompt: @"Please re-enter the same passphrase:" + creating: NO + salt: @"wahooma"]; + Log(@"Key from passphrase = %@", key2); + CAssert(key2); + decrypted = [key2 decryptData: encrypted]; + CAssertEqual(decrypted, cleartext); } @@ -261,6 +332,7 @@ } +#if !TARGET_OS_IPHONE TestCase(MYUseIdentity) { MYIdentity *me = nil;//[MYIdentity preferredIdentityForName: @"MYCryptoTest"]; if (!me) { @@ -278,6 +350,7 @@ CAssert(me,@"No default identity has been set up in the Keychain"); TestUseKeyPair(me.privateKey); } +#endif #pragma mark - @@ -362,7 +435,7 @@ TestCase(KeyPairExport) { RequireTestCase(MYKeychain); - RequireTestCase(MYPrivateKey); + RequireTestCase(MYGenerateKeyPair); testKeyPairExportWithPrompt(NO); } diff -r 3568d5fd4b6a -r e4c971be4079 MYCrypto_Private.h --- a/MYCrypto_Private.h Tue Apr 14 18:34:52 2009 -0700 +++ b/MYCrypto_Private.h Sat Apr 18 18:12:06 2009 -0700 @@ -57,6 +57,7 @@ - (NSData*) _crypt: (NSData *)data operation: (BOOL) op; // YES to encrypt, NO to decrypt #if !MYCRYPTO_USE_IPHONE_API @property (readonly) const CSSM_KEY* cssmKey; +@property (readonly) const CSSM_CSP_HANDLE cssmCSPHandle; - (NSData*) exportKeyInFormat: (SecExternalFormat)format withPEM: (BOOL)withPEM; - (CSSM_CC_HANDLE) _createSignatureContext: (CSSM_ALGORITHMS)algorithm; - (CSSM_CC_HANDLE) _createPassThroughContext; diff -r 3568d5fd4b6a -r e4c971be4079 MYDecoder.m --- a/MYDecoder.m Tue Apr 14 18:34:52 2009 -0700 +++ b/MYDecoder.m Sat Apr 18 18:12:06 2009 -0700 @@ -308,23 +308,6 @@ -// Taken from Keychain.framework -NSString* OIDAsString(const CSSM_OID oid) { - if ((NULL == oid.Data) || (0 >= oid.Length)) { - return nil; - } else { - NSMutableString *result = [NSMutableString stringWithCapacity:(4 * oid.Length)]; - unsigned int i; - - for (i = 0; i < oid.Length; ++i) { - [result appendFormat:@"%s%hhu", ((0 == i) ? "" : ", "), oid.Data[i]]; - } - - return result; - } -} - - #pragma mark - #pragma mark TEST CASE: diff -r 3568d5fd4b6a -r e4c971be4079 MYKeychainItem.m --- a/MYKeychainItem.m Tue Apr 14 18:34:52 2009 -0700 +++ b/MYKeychainItem.m Sat Apr 18 18:12:06 2009 -0700 @@ -92,11 +92,13 @@ } - (BOOL) removeFromKeychain { + OSStatus err; #if MYCRYPTO_USE_IPHONE_API - return check(SecItemDelete(self.asQuery), @"SecItemDelete"); + err = SecItemDelete(self.asQuery); #else - return check(SecKeychainItemDelete((SecKeychainItemRef)_itemRef), @"SecKeychainItemDelete"); + err = SecKeychainItemDelete((SecKeychainItemRef)_itemRef); #endif + return err==errSecItemNotFound || check(err, @"SecKeychainItemDelete"); } diff -r 3568d5fd4b6a -r e4c971be4079 MYSymmetricKey-iPhone.m --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MYSymmetricKey-iPhone.m Sat Apr 18 18:12:06 2009 -0700 @@ -0,0 +1,173 @@ +// +// MYSymmetricKey-iPhone.m +// MYCrypto +// +// Created by Jens Alfke on 4/17/09. +// Copyright 2009 Jens Alfke. All rights reserved. +// + +#import "MYSymmetricKey.h" +#import "MYCryptor.h" +#import "MYCrypto_Private.h" + +#if MYCRYPTO_USE_IPHONE_API + + +typedef uint32_t CSSM_ALGORITHMS; +enum { +// Taken from cssmtype.h in OS X 10.5 SDK: + CSSM_ALGID_NONE = 0x00000000L, + CSSM_ALGID_DES = CSSM_ALGID_NONE + 14, + CSSM_ALGID_3DES_3KEY_EDE = CSSM_ALGID_NONE + 17, + CSSM_ALGID_3DES_3KEY = CSSM_ALGID_3DES_3KEY_EDE, + CSSM_ALGID_RC4 = CSSM_ALGID_NONE + 25, + CSSM_ALGID_CAST = CSSM_ALGID_NONE + 27, + CSSM_ALGID_VENDOR_DEFINED = CSSM_ALGID_NONE + 0x80000000L, + CSSM_ALGID_AES +}; + +static const CSSM_ALGORITHMS kCSSMAlgorithms[] = { +CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4 +}; + +static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"}; + + +@implementation MYSymmetricKey + + +- (id) _initWithKeyData: (NSData*)keyData + algorithm: (CCAlgorithm)algorithm + inKeychain: (MYKeychain*)keychain +{ + Assert(algorithm <= kCCAlgorithmRC4); + Assert(keyData); + NSNumber *keySizeInBits = [NSNumber numberWithUnsignedInt: keyData.length * 8]; + NSDictionary *keyAttrs = $dict( {(id)kSecClass, (id)kSecClassKey}, + //{(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric}, + {(id)kSecAttrKeyType, [NSNumber numberWithUnsignedInt: kCSSMAlgorithms[algorithm]]}, + {(id)kSecAttrKeySizeInBits, keySizeInBits}, + {(id)kSecAttrEffectiveKeySize, keySizeInBits}, + {(id)kSecAttrIsPermanent, keychain ?$true :$false}, + {(id)kSecAttrCanEncrypt, $true}, + {(id)kSecAttrCanDecrypt, $true}, + {(id)kSecAttrCanWrap, $false}, + {(id)kSecAttrCanUnwrap, $false}, + {(id)kSecAttrCanDerive, $false}, + {(id)kSecAttrCanSign, $false}, + {(id)kSecAttrCanVerify, $false}, + {(id)kSecValueData, keyData}, + {(id)kSecReturnPersistentRef, $true}); + SecKeyRef keyRef = NULL; + if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) { + [self release]; + return nil; + } + Assert(keyRef, @"SecItemAdd didn't return anything"); + self = [self initWithKeyRef: keyRef]; + CFRelease(keyRef); + return self; +} + +- (id) initWithKeyData: (NSData*)keyData + algorithm: (CCAlgorithm)algorithm +{ + return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil]; +} + ++ (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits + algorithm: (CCAlgorithm)algorithm + inKeychain: (MYKeychain*)keychain +{ + return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits] + algorithm: algorithm + inKeychain: keychain] + autorelease]; +} + ++ (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits + algorithm: (CCAlgorithm)algorithm { + return [self _generateSymmetricKeyOfSize: keySizeInBits + algorithm: algorithm + inKeychain: nil]; +} + + +- (SecExternalItemType) keyType { + return kSecAttrKeyClassSymmetric; +} + +- (CCAlgorithm) algorithm { + CSSM_ALGORITHMS cssmAlg; + id keyType = [self _attribute: kSecAttrKeyType]; + Assert(keyType!=nil, @"Key has no kSecAttrKeyType"); + cssmAlg = [keyType unsignedIntValue]; + switch(cssmAlg) { + case CSSM_ALGID_AES: + return kCCAlgorithmAES128; + case CSSM_ALGID_DES: + return kCCAlgorithmDES; + case CSSM_ALGID_3DES_3KEY: + return kCCAlgorithm3DES; + case CSSM_ALGID_CAST: + return kCCAlgorithmCAST; + case CSSM_ALGID_RC4: + return kCCAlgorithmRC4; + default: + Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg); + return (CCAlgorithm)-1; + } +} + +- (const char*) algorithmName { + CCAlgorithm a = self.algorithm; + if (a >= 0 && a <= kCCAlgorithmRC4) + return kCCAlgorithmNames[a]; + else + return "???"; +} + +- (unsigned) keySizeInBits { + id keySize = [self _attribute: kSecAttrKeySizeInBits]; + Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits"); + return [keySize unsignedIntValue]; +} + + +- (NSString*) description { + return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName); +} + + +- (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options +{ + NSData *keyData = self.keyData; + Assert(keyData, @"Couldn't get key data"); + NSMutableData *output = [NSMutableData dataWithLength: data.length + 256]; + size_t bytesWritten = 0; + CCCryptorStatus status = CCCrypt(op, self.algorithm, options, + keyData.bytes, keyData.length, NULL, + data.bytes, data.length, output.mutableBytes, output.length, + &bytesWritten); + if (status) { + Warn(@"MYSymmetricKey: CCCrypt returned error %i",status); + return nil; + } + output.length = bytesWritten; + return output; +} + +- (NSData*) encryptData: (NSData*)data { + return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding]; +} + + +- (NSData*) decryptData: (NSData*)data { + return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding]; +} + + +@end + + +#endif MYCRYPTO_USE_IPHONE_API diff -r 3568d5fd4b6a -r e4c971be4079 MYSymmetricKey.h --- a/MYSymmetricKey.h Tue Apr 14 18:34:52 2009 -0700 +++ b/MYSymmetricKey.h Sat Apr 18 18:12:06 2009 -0700 @@ -11,6 +11,11 @@ @interface MYSymmetricKey : MYKey +{ +#if !MYCRYPTO_USE_IPHONE_API + CSSM_KEY *_ownedCSSMKey; +#endif +} /** Initializes a symmetric key from the given key data and algorithm. */ - (id) initWithKeyData: (NSData*)keyData @@ -22,10 +27,41 @@ + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits algorithm: (CCAlgorithm)algorithm; +/** Converts a passphrase into a symmetric key. + The same passphrase (and salt) will always return the same key, so you can use this method + to encrypt and decrypt data using a user-entered passphrase, without having to store the key + itself in the keychain. + @param alertTitle A title for the alert (this seems to be ignored by the OS). + @param prompt A prompt string displayed in the alert. + @param creating Is a new passphrase being created? If YES, the user will have to enter the + passphrase twice, to check for errors, and the nifty passphrase-strength meter will be + displayed. If NO, there's only one text-field, and an option to display its contents in + the clear. + @param salt An arbitrary value whose data will be mixed in with the passphrase before + hashing, to perturb the resulting bits. The purpose of this is to make it harder for + an attacker to brute-force the key using a precompiled list of digests of common + passwords. Changing the salt changes the key, so you need to pass the same value when + re-deriving the key as you did when first generating it. */ + + (MYSymmetricKey*) generateFromUserPassphraseWithAlertTitle: (NSString*)alertTitle + alertPrompt: (NSString*)prompt + creating: (BOOL)creating + salt: (id)saltObj; + /** The key's algorithm. */ @property (readonly) CCAlgorithm algorithm; /** The key's size/length, in bits. */ @property (readonly) unsigned keySizeInBits; + +/** A utility that prompts for a passphrase, using the Security agent's nice modal panel, + and returns the raw passphrase as a string. + @param alertTitle A title for the alert (this seems to be ignored by the OS). + @param prompt A prompt string displayed in the alert. + @param creating Is a new passphrase being created? + (See description in +generateFromUserPassphrase... method.) */ ++ (NSString*) promptForPassphraseWithAlertTitle: (NSString*)alertTitle + alertPrompt: (NSString*)prompt + creating: (BOOL)creating; + @end diff -r 3568d5fd4b6a -r e4c971be4079 MYSymmetricKey.m --- a/MYSymmetricKey.m Tue Apr 14 18:34:52 2009 -0700 +++ b/MYSymmetricKey.m Sat Apr 18 18:12:06 2009 -0700 @@ -10,23 +10,10 @@ #import "MYCryptor.h" #import "MYCrypto_Private.h" +#if !MYCRYPTO_USE_IPHONE_API -#if MYCRYPTO_USE_IPHONE_API -typedef uint32_t CSSM_ALGORITHMS; -enum { -// Taken from cssmtype.h in OS X 10.5 SDK: - CSSM_ALGID_NONE = 0x00000000L, - CSSM_ALGID_DES = CSSM_ALGID_NONE + 14, - CSSM_ALGID_3DES_3KEY_EDE = CSSM_ALGID_NONE + 17, - CSSM_ALGID_3DES_3KEY = CSSM_ALGID_3DES_3KEY_EDE, - CSSM_ALGID_RC4 = CSSM_ALGID_NONE + 25, - CSSM_ALGID_CAST = CSSM_ALGID_NONE + 27, - CSSM_ALGID_VENDOR_DEFINED = CSSM_ALGID_NONE + 0x80000000L, - CSSM_ALGID_AES -}; -#else #import -#endif + static const CSSM_ALGORITHMS kCSSMAlgorithms[] = { CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4 @@ -35,54 +22,56 @@ static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"}; +/** Undocumented Security function. Unfortunately this is the only way I can find to create + a SecKeyRef from a CSSM_KEY. */ +extern OSStatus SecKeyCreate(const CSSM_KEY *key, SecKeyRef* keyRef) WEAK_IMPORT_ATTRIBUTE; + +static CSSM_KEY* cssmKeyFromData( NSData *keyData, CSSM_ALGORITHMS algorithm, + MYKeychain *keychain); +static CSSM_DATA makeSalt( id salty, size_t length ); +static CSSM_RETURN impExpCreatePassKey( + const SecKeyImportExportParameters *keyParams, // required + CSSM_CSP_HANDLE cspHand, // MUST be CSPDL + BOOL verifyPhrase, // use 2nd passphrase textfield for verification? + CSSM_KEY_PTR *passKey); // mallocd and RETURNED + + #pragma mark - @implementation MYSymmetricKey +- (id) _initWithCSSMKey: (CSSM_KEY*)cssmKey { + SecKeyRef keyRef = NULL; + if (SecKeyCreate == NULL) { + // If weak-linked SPI fn no longer exists + Warn(@"Unable to call SecKeyCreate SPI -- not available"); + [self release]; + return nil; + } + if (!check(SecKeyCreate(cssmKey,&keyRef), @"SecKeyCreate")) { + [self release]; + return nil; + } + self = [self initWithKeyRef: keyRef]; + if (self) { + _ownedCSSMKey = cssmKey; // (so I'll remember to free it) + } + return self; +} + + - (id) _initWithKeyData: (NSData*)keyData algorithm: (CCAlgorithm)algorithm inKeychain: (MYKeychain*)keychain { Assert(algorithm <= kCCAlgorithmRC4); Assert(keyData); - SecKeyRef keyRef = NULL; -#if MYCRYPTO_USE_IPHONE_API - NSNumber *keySizeInBits = [NSNumber numberWithUnsignedInt: keyData.length * 8]; - NSDictionary *keyAttrs = $dict( {(id)kSecClass, (id)kSecClassKey}, - //{(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric}, - {(id)kSecAttrKeyType, [NSNumber numberWithUnsignedInt: kCSSMAlgorithms[algorithm]]}, - {(id)kSecAttrKeySizeInBits, keySizeInBits}, - {(id)kSecAttrEffectiveKeySize, keySizeInBits}, - {(id)kSecAttrIsPermanent, keychain ?$true :$false}, - {(id)kSecAttrCanEncrypt, $true}, - {(id)kSecAttrCanDecrypt, $true}, - {(id)kSecAttrCanWrap, $false}, - {(id)kSecAttrCanUnwrap, $false}, - {(id)kSecAttrCanDerive, $false}, - {(id)kSecAttrCanSign, $false}, - {(id)kSecAttrCanVerify, $false}, - {(id)kSecValueData, keyData}, - {(id)kSecReturnPersistentRef, $true}); - if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) { + CSSM_KEY *key = cssmKeyFromData(keyData, kCSSMAlgorithms[algorithm], keychain); + if (!key) { [self release]; return nil; } - Assert(keyRef, @"SecItemAdd didn't return anything"); -#else - Assert(NO,@"Unimplemented"); //FIX - /* The technique below doesn't work, because there's no way to tell SecKeychainItemImport - what algorithm to use when importing a raw key. Still looking for a solution... --jpa 4/2009 - SecKeyImportExportParameters params = {}; - keyRef = importKey(keyData, kSecItemTypeSessionKey, keychain.keychainRefOrDefault, ¶ms); - if (!keyRef) { - [self release]; - return nil; - } - */ -#endif - self = [self initWithKeyRef: keyRef]; - CFRelease(keyRef); - return self; + return [self _initWithCSSMKey: key]; } - (id) initWithKeyData: (NSData*)keyData @@ -95,12 +84,6 @@ algorithm: (CCAlgorithm)algorithm inKeychain: (MYKeychain*)keychain { -#if MYCRYPTO_USE_IPHONE_API - return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits] - algorithm: algorithm - inKeychain: keychain] - autorelease]; -#else Assert(algorithm <= kCCAlgorithmRC4); CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE; if (keychain) @@ -115,7 +98,6 @@ return nil; } return [[[self alloc] initWithKeyRef: keyRef] autorelease]; -#endif } + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits @@ -125,44 +107,173 @@ inKeychain: nil]; } ++ (NSString*) promptForPassphraseWithAlertTitle: (NSString*)alertTitle + alertPrompt: (NSString*)prompt + creating: (BOOL)creating +{ + // Ask the user for a passphrase: + SecKeyImportExportParameters params = { + .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION, + .flags = kSecKeySecurePassphrase, + .alertTitle = (CFStringRef)alertTitle, + .alertPrompt = (CFStringRef)prompt, + .keyUsage = CSSM_KEYUSE_ANY, + .keyAttributes = CSSM_KEYATTR_EXTRACTABLE + }; + CSSM_CSP_HANDLE cspHandle = [[MYKeychain defaultKeychain] CSPHandle]; + CSSM_KEY *passphraseKey = NULL; + if (impExpCreatePassKey(¶ms, + cspHandle, + creating, + &passphraseKey) != CSSM_OK) + return nil; + + MYSymmetricKey *key = [[self alloc] _initWithCSSMKey: passphraseKey]; + NSData *keyData = key.keyData; + Assert(keyData); + NSString *passphrase = [[NSString alloc] initWithData: keyData + encoding: NSUTF8StringEncoding]; + [key release]; + return [passphrase autorelease]; +} + + +#define PKCS5_V2_SALT_LEN 8 +#define PKCS5_V2_ITERATIONS 2048 +#define PKCS5_V2_DES_IV_SIZE 8 + ++ (MYSymmetricKey*) generateFromUserPassphraseWithAlertTitle: (NSString*)alertTitle + alertPrompt: (NSString*)prompt + creating: (BOOL)creating + salt: (id)saltObj +{ + MYSymmetricKey *generatedKey = nil; + + // Ask the user for a passphrase: + SecKeyImportExportParameters params = { + .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION, + .flags = kSecKeySecurePassphrase, + .alertTitle = (CFStringRef)alertTitle, + .alertPrompt = (CFStringRef)prompt, + .keyUsage = CSSM_KEYUSE_ANY, + .keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_SENSITIVE + }; + CSSM_CSP_HANDLE cspHandle = [[MYKeychain defaultKeychain] CSPHandle]; + CSSM_KEY *passphraseKey = NULL; + if (impExpCreatePassKey(¶ms, + cspHandle, + creating, + &passphraseKey) != CSSM_OK) + return nil; + + CSSM_DATA saltData = makeSalt(saltObj,PKCS5_V2_SALT_LEN); + CSSM_CRYPTO_DATA seed = {}; + + // Now use the secure passphrase to generate a symmetric key: + CSSM_CC_HANDLE ctx = 0; + CSSM_ACCESS_CREDENTIALS credentials = {}; + if (checkcssm(CSSM_CSP_CreateDeriveKeyContext(cspHandle, + CSSM_ALGID_PKCS5_PBKDF2, + CSSM_ALGID_AES, 128, + &credentials, + passphraseKey, + PKCS5_V2_ITERATIONS, + &saltData, + &seed, + &ctx), + @"CSSM_CSP_CreateDeriveKeyContext")) { + CSSM_PKCS5_PBKDF2_PARAMS params = {.PseudoRandomFunction=CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1}; + CSSM_DATA paramData = {.Data=(void*)¶ms, .Length=sizeof(params)}; + CSSM_KEY *cssmKey = calloc(1,sizeof(CSSM_KEY)); + if (checkcssm(CSSM_DeriveKey(ctx, + ¶mData, + CSSM_KEYUSE_ANY, + CSSM_KEYATTR_EXTRACTABLE, //| CSSM_KEYATTR_SENSITIVE, + NULL, + NULL, + cssmKey), + @"CSSM_DeriveKey")) { + generatedKey = [[[self alloc] _initWithCSSMKey: cssmKey] autorelease]; + } + } + CSSM_DeleteContext(ctx); + CSSM_FreeKey(cspHandle, &credentials, passphraseKey, YES); + return generatedKey; +} + + +- (void) dealloc +{ + if(_ownedCSSMKey) + CSSM_FreeKey(self.cssmCSPHandle, NULL, _ownedCSSMKey, YES); + [super dealloc]; +} + #if !TARGET_OS_IPHONE - (NSData*) exportKeyInFormat: (SecExternalFormat)format withPEM: (BOOL)withPEM { - SecKeyImportExportParameters params = { - .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION, - .flags = kSecKeySecurePassphrase, - }; - CFDataRef data = NULL; - if (check(SecKeychainItemExport(self.keyRef, - format, (withPEM ?kSecItemPemArmour :0), - ¶ms, &data), - @"SecKeychainItemExport")) - return [(id)CFMakeCollectable(data) autorelease]; - else + if (format==kSecFormatRawKey || format==kSecFormatUnknown) + return [super exportKeyInFormat: format withPEM: withPEM]; + + // Get access credentials: + const CSSM_ACCESS_CREDENTIALS *credentials; + credentials = [self cssmCredentialsForOperation: CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED + type: kSecCredentialTypeDefault + error: nil]; + if (!credentials) return nil; + + // Prompt use for a passphrase to use for the wrapping key: + MYSymmetricKey *wrappingKey = [MYSymmetricKey generateFromUserPassphraseWithAlertTitle: @"Export Key" alertPrompt: @"Enter a passphrase to encrypt the key you're exporting from the Keychain:" creating: YES + salt: [MYCryptor randomKeyOfLength: PKCS5_V2_SALT_LEN*8]]; + if (!wrappingKey) + return nil; + + // Create the context: + CSSM_CSP_HANDLE cspHandle = self.cssmCSPHandle; + CSSM_CC_HANDLE ctx; + if (!checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, + CSSM_ALGID_NONE, + CSSM_ALGMODE_WRAP, + NULL, + wrappingKey.cssmKey, + NULL, + CSSM_PADDING_NONE, + NULL, + &ctx), + @"CSSM_CSP_CreateSymmetricContext")) + return nil; + + // Set the wrapped key format: + NSData *result = nil; + CSSM_CONTEXT_ATTRIBUTE attr = { CSSM_ATTRIBUTE_WRAPPED_KEY_FORMAT, sizeof(uint32) }; + attr.Attribute.Uint32 = CSSM_KEYBLOB_WRAPPED_FORMAT_PKCS7; + if (checkcssm(CSSM_UpdateContextAttributes(ctx, 1, &attr), @"CSSM_UpdateContextAttributes")) { + // Now wrap the key: + CSSM_WRAP_KEY wrappedKey = {}; + if (checkcssm(CSSM_WrapKey(ctx, credentials, self.cssmKey, NULL, &wrappedKey), + @"CSSM_WrapKey")) { + // ...and copy the wrapped key data to the result NSData: + result = [NSData dataWithBytes: wrappedKey.KeyData.Data length: wrappedKey.KeyData.Length]; + CSSM_FreeKey(cspHandle, credentials, &wrappedKey, NO); + } + } + // Finally, delete the context + CSSM_DeleteContext(ctx); + return result; } #endif - (SecExternalItemType) keyType { -#if MYCRYPTO_USE_IPHONE_API - return kSecAttrKeyClassSymmetric; -#else return kSecItemTypeSessionKey; -#endif } - (CCAlgorithm) algorithm { CSSM_ALGORITHMS cssmAlg; -#if MYCRYPTO_USE_IPHONE_API - id keyType = [self _attribute: kSecAttrKeyType]; - Assert(keyType!=nil, @"Key has no kSecAttrKeyType"); - cssmAlg = [keyType unsignedIntValue]; -#else cssmAlg = self.cssmKey->KeyHeader.AlgorithmId; -#endif switch(cssmAlg) { case CSSM_ALGID_AES: return kCCAlgorithmAES128; @@ -189,15 +300,9 @@ } - (unsigned) keySizeInBits { -#if MYCRYPTO_USE_IPHONE_API - id keySize = [self _attribute: kSecAttrKeySizeInBits]; - Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits"); - return [keySize unsignedIntValue]; -#else const CSSM_KEY *key = self.cssmKey; Assert(key); return key->KeyHeader.LogicalKeySizeInBits; -#endif } @@ -237,35 +342,214 @@ @end -/* (Turned out I could just use SecKeyExport for this.) - -static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) { - CAssert(key); - const CSSM_KEY* cssmKey; - const CSSM_ACCESS_CREDENTIALS *credentials; - CSSM_CSP_HANDLE cspHandle; +#pragma mark - + + +static CSSM_KEY* cssmKeyFromData( NSData *keyData, + CSSM_ALGORITHMS algorithm, + MYKeychain *keychain ) { + // Thanks to Jim Murphy for showing the way! + if (!keychain) keychain = [MYKeychain defaultKeychain]; CSSM_CC_HANDLE ccHandle; - if (!check(SecKeyGetCSSMKey(key, &cssmKey), @"GetCSSMKey") - || !check(SecKeyGetCredentials(key, - CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, - kSecCredentialTypeDefault, - &credentials), @"GetCredentials") - || !check(SecKeyGetCSPHandle(key, &cspHandle), @"GetCSPHandle") - - || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP, - NULL, NULL, NULL, - CSSM_PADDING_NONE, NULL, &ccHandle), - @"CSSM_CSP_CreateSymmetricContext")) - return nil; + if (!checkcssm(CSSM_CSP_CreateSymmetricContext([keychain CSPHandle], + CSSM_ALGID_NONE, CSSM_ALGMODE_WRAP, + NULL, NULL, NULL, + CSSM_PADDING_NONE, NULL, + &ccHandle), + @"CSSM_CSP_CreateSymmetricContext")) + return NO; - CSSM_WRAP_KEY wrapped; - NSData *result = nil; - if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped), - @"CSSM_WrapKey")) { - result = [NSData dataWithBytes: wrapped.KeyData.Data - length: wrapped.KeyData.Length]; + CSSM_KEY wrappedKey = { + .KeyHeader = { + .BlobType = CSSM_KEYBLOB_RAW, + .Format = CSSM_KEYBLOB_RAW_FORMAT_NONE, + .AlgorithmId = algorithm, + .KeyClass = CSSM_KEYCLASS_SESSION_KEY, + .LogicalKeySizeInBits = keyData.length*8, + .KeyAttr = CSSM_KEYATTR_EXTRACTABLE, + .KeyUsage = CSSM_KEYUSE_ANY + }, + .KeyData = { + .Data = (void*)keyData.bytes, + .Length = keyData.length + } + }; + + CSSM_KEY *outKey = calloc(sizeof(CSSM_KEY),1); + CSSM_DATA desc = {}; + if (!checkcssm(CSSM_UnwrapKey(ccHandle, + NULL, + &wrappedKey, + CSSM_KEYUSE_ANY, + CSSM_KEYATTR_EXTRACTABLE, + NULL, + NULL, + outKey, + &desc), + @"CSSM_UnwrapKey")) { + free(outKey); + outKey = NULL; } CSSM_DeleteContext(ccHandle); - return result; + return outKey; } + + +// Create salt data of a specific length from an arbitrary NSObject. */ +static CSSM_DATA makeSalt( id salty, size_t length ) { + // Convert to NSData if necessary: + CAssert(salty!=nil); + if (![salty isKindOfClass: [NSData class]]) + salty = [[salty description] dataUsingEncoding: NSUTF8StringEncoding]; + // Repeat enough times to fill the desired length: + NSMutableData *salt = [[salty mutableCopy] autorelease]; + CAssert(salt.length>0); + while (salt.length < length) { + [salt appendData: salt]; + } + // Truncate to length and return it: + salt.length = length; + return (CSSM_DATA){.Data=(void*)salt.bytes, .Length=salt.length}; +} + + +// Copied from SecImportExportUtils.cpp in Apple's libsecurity_keychain project +/* + * Given a context specified via a CSSM_CC_HANDLE, add a new + * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType, + * AttributeLength, and an untyped pointer. + */ +static CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle, + uint32 AttributeType, + uint32 AttributeLength, + const void *AttributePtr) +{ + CSSM_CONTEXT_ATTRIBUTE newAttr; + + newAttr.AttributeType = AttributeType; + newAttr.AttributeLength = AttributeLength; + newAttr.Attribute.Data = (CSSM_DATA_PTR)AttributePtr; + return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr); +} + +/* +* Add a CFString to a crypto context handle. */ +static CSSM_RETURN impExpAddStringAttr( + CSSM_CC_HANDLE ccHand, + CFStringRef str, + CSSM_ATTRIBUTE_TYPE attrType) +{ + /* CFStrings are passed as external rep in UTF8 encoding by convention */ + CFDataRef outData; + outData = CFStringCreateExternalRepresentation(NULL, + str, kCFStringEncodingUTF8, 0); // lossByte 0 ==> no loss allowed + if(outData == NULL) { + Warn(@"impExpAddStringAttr: bad string format"); + return paramErr; + } + + CSSM_DATA attrData; + attrData.Data = (uint8 *)CFDataGetBytePtr(outData); + attrData.Length = CFDataGetLength(outData); + CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA), + &attrData); + CFRelease(outData); + if(crtn) { + Warn(@"impExpAddStringAttr: CSSM_UpdateContextAttributes error"); + } + return crtn; +} + +/* + * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result. + */ +static CSSM_RETURN impExpCreatePassKey( + const SecKeyImportExportParameters *keyParams, // required + CSSM_CSP_HANDLE cspHand, // MUST be CSPDL + BOOL verifyPhrase, // use 2nd passphrase textfield for verification? + CSSM_KEY_PTR *passKey) // mallocd and RETURNED +{ + CSSM_RETURN crtn; + CSSM_CC_HANDLE ccHand; + uint32 verifyAttr; + CSSM_DATA dummyLabel; + CSSM_KEY_PTR ourKey = NULL; + + Log(@"Generating secure passphrase key"); + ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY)); + if(ourKey == NULL) { + return memFullErr; + } + memset(ourKey, 0, sizeof(CSSM_KEY)); + + crtn = CSSM_CSP_CreateKeyGenContext(cspHand, + CSSM_ALGID_SECURE_PASSPHRASE, + 4, // keySizeInBits must be non zero + NULL, // Seed + NULL, // Salt + NULL, // StartDate + NULL, // EndDate + NULL, // Params + &ccHand); + if(crtn) { + checkcssm(crtn,@"CSSM_CSP_CreateKeyGenContext"); + return crtn; + } + /* subsequent errors to errOut: */ + + /* additional context attributes specific to this type of key gen */ + CAssert(keyParams != NULL); // or we wouldn't be here + if(keyParams->alertTitle != NULL) { + crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle, + CSSM_ATTRIBUTE_ALERT_TITLE); + if(crtn) { + goto errOut; + } + } + if(keyParams->alertPrompt != NULL) { + crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt, + CSSM_ATTRIBUTE_PROMPT); + if(crtn) { + goto errOut; + } + } + verifyAttr = verifyPhrase ? 1 : 0; + crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE, + sizeof(uint32), (const void *)verifyAttr); + if(crtn) { + checkcssm(crtn,@"impExpAddContextAttribute"); + goto errOut; + } + + dummyLabel.Data = (uint8 *)"Secure Passphrase"; + dummyLabel.Length = strlen((char *)dummyLabel.Data); + + uint32 keyAttr = keyParams->keyAttributes; + if (keyAttr & CSSM_KEYATTR_SENSITIVE) + keyAttr |= CSSM_KEYATTR_RETURN_REF; + else + keyAttr |= CSSM_KEYATTR_EXTRACTABLE; + + crtn = CSSM_GenerateKey(ccHand, + keyParams->keyUsage ?: CSSM_KEYUSE_ANY, + keyAttr, + &dummyLabel, + NULL, // ACL + ourKey); + if(crtn) { + checkcssm(crtn,@"CSSM_GenerateKey"); + } +errOut: + CSSM_DeleteContext(ccHand); + if(crtn == CSSM_OK) { + *passKey = ourKey; + } + else if(ourKey != NULL) { + free(ourKey); + } + return crtn; +} + + +#endif !MYCRYPTO_USE_IPHONE_API