* Replaced MYKeyPair with MYPrivateKey.
* Changed config files.
5 // Created by Jens Alfke on 4/2/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYSymmetricKey.h"
11 #import "MYCrypto_Private.h"
14 #if MYCRYPTO_USE_IPHONE_API
15 typedef uint32_t CSSM_ALGORITHMS;
17 // Taken from cssmtype.h in OS X 10.5 SDK:
18 CSSM_ALGID_NONE = 0x00000000L,
19 CSSM_ALGID_DES = CSSM_ALGID_NONE + 14,
20 CSSM_ALGID_3DES_3KEY_EDE = CSSM_ALGID_NONE + 17,
21 CSSM_ALGID_3DES_3KEY = CSSM_ALGID_3DES_3KEY_EDE,
22 CSSM_ALGID_RC4 = CSSM_ALGID_NONE + 25,
23 CSSM_ALGID_CAST = CSSM_ALGID_NONE + 27,
24 CSSM_ALGID_VENDOR_DEFINED = CSSM_ALGID_NONE + 0x80000000L,
28 #import <Security/cssmtype.h>
31 static const CSSM_ALGORITHMS kCSSMAlgorithms[] = {
32 CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4
35 static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"};
39 @implementation MYSymmetricKey
42 - (id) _initWithKeyData: (NSData*)keyData
43 algorithm: (CCAlgorithm)algorithm
44 inKeychain: (MYKeychain*)keychain
46 Assert(algorithm <= kCCAlgorithmRC4);
48 SecKeyRef keyRef = NULL;
49 #if MYCRYPTO_USE_IPHONE_API
50 NSNumber *keySizeInBits = [NSNumber numberWithUnsignedInt: keyData.length * 8];
51 NSDictionary *keyAttrs = $dict( {(id)kSecClass, (id)kSecClassKey},
52 //{(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
53 {(id)kSecAttrKeyType, [NSNumber numberWithUnsignedInt: kCSSMAlgorithms[algorithm]]},
54 {(id)kSecAttrKeySizeInBits, keySizeInBits},
55 {(id)kSecAttrEffectiveKeySize, keySizeInBits},
56 {(id)kSecAttrIsPermanent, keychain ?$true :$false},
57 {(id)kSecAttrCanEncrypt, $true},
58 {(id)kSecAttrCanDecrypt, $true},
59 {(id)kSecAttrCanWrap, $false},
60 {(id)kSecAttrCanUnwrap, $false},
61 {(id)kSecAttrCanDerive, $false},
62 {(id)kSecAttrCanSign, $false},
63 {(id)kSecAttrCanVerify, $false},
64 {(id)kSecValueData, keyData},
65 //{(id)kSecAttrApplicationTag, [@"foo" dataUsingEncoding: NSUTF8StringEncoding]}, //TEMP
66 {(id)kSecReturnPersistentRef, $true});
67 if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) {
71 Log(@"SecItemAdd returned %@", keyRef);//TEMP
72 Assert(keyRef, @"SecItemAdd didn't return anything");
74 Assert(NO,@"Unimplemented"); //FIX
75 /* The technique below doesn't work, because there's no way to tell SecKeychainItemImport
76 what algorithm to use when importing a raw key. Still looking for a solution... --jpa 4/2009
77 SecKeyImportExportParameters params = {};
78 keyRef = importKey(keyData, kSecItemTypeSessionKey, keychain.keychainRefOrDefault, ¶ms);
85 self = [self initWithKeyRef: keyRef];
90 - (id) initWithKeyData: (NSData*)keyData
91 algorithm: (CCAlgorithm)algorithm
93 return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
96 + (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
97 algorithm: (CCAlgorithm)algorithm
98 inKeychain: (MYKeychain*)keychain
100 #if MYCRYPTO_USE_IPHONE_API
101 return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
103 inKeychain: keychain]
106 Assert(algorithm <= kCCAlgorithmRC4);
107 CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
109 flags |= CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE;
110 CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
111 SecKeyRef keyRef = NULL;
112 if (!check(SecKeyGenerate(keychain.keychainRefOrDefault, // nil kc generates a transient key
113 kCSSMAlgorithms[algorithm],
115 0, usage, flags, NULL, &keyRef),
116 @"SecKeyGenerate")) {
119 return [[[self alloc] initWithKeyRef: keyRef] autorelease];
123 + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
124 algorithm: (CCAlgorithm)algorithm {
125 return [self _generateSymmetricKeyOfSize: keySizeInBits
131 #if !TARGET_OS_IPHONE
132 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
133 withPEM: (BOOL)withPEM
135 SecKeyImportExportParameters params = {
136 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
137 .flags = kSecKeySecurePassphrase,
139 CFDataRef data = NULL;
140 if (check(SecKeychainItemExport(self.keyRef,
141 format, (withPEM ?kSecItemPemArmour :0),
143 @"SecKeychainItemExport"))
144 return [(id)CFMakeCollectable(data) autorelease];
151 - (SecExternalItemType) keyType {
152 #if MYCRYPTO_USE_IPHONE_API
153 return kSecAttrKeyClassSymmetric;
155 return kSecItemTypeSessionKey;
159 - (CCAlgorithm) algorithm {
160 CSSM_ALGORITHMS cssmAlg;
161 #if MYCRYPTO_USE_IPHONE_API
162 id keyType = [self _attribute: kSecAttrKeyType];
163 Assert(keyType!=nil, @"Key has no kSecAttrKeyType");
164 cssmAlg = [keyType unsignedIntValue];
166 cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
170 return kCCAlgorithmAES128;
172 return kCCAlgorithmDES;
173 case CSSM_ALGID_3DES_3KEY:
174 return kCCAlgorithm3DES;
175 case CSSM_ALGID_CAST:
176 return kCCAlgorithmCAST;
178 return kCCAlgorithmRC4;
180 Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
181 return (CCAlgorithm)-1;
185 - (const char*) algorithmName {
186 CCAlgorithm a = self.algorithm;
187 if (a >= 0 && a <= kCCAlgorithmRC4)
188 return kCCAlgorithmNames[a];
193 - (unsigned) keySizeInBits {
194 #if MYCRYPTO_USE_IPHONE_API
195 id keySize = [self _attribute: kSecAttrKeySizeInBits];
196 Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits");
197 return [keySize unsignedIntValue];
199 const CSSM_KEY *key = self.cssmKey;
201 return key->KeyHeader.LogicalKeySizeInBits;
206 - (NSString*) description {
207 return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
211 - (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
213 NSData *keyData = self.keyData;
214 Assert(keyData, @"Couldn't get key data");
215 NSMutableData *output = [NSMutableData dataWithLength: data.length + 256];
216 size_t bytesWritten = 0;
217 CCCryptorStatus status = CCCrypt(op, self.algorithm, options,
218 keyData.bytes, keyData.length, NULL,
219 data.bytes, data.length, output.mutableBytes, output.length,
222 Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
225 output.length = bytesWritten;
229 - (NSData*) encryptData: (NSData*)data {
230 return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
234 - (NSData*) decryptData: (NSData*)data {
235 return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
242 /* (Turned out I could just use SecKeyExport for this.)
244 static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) {
246 const CSSM_KEY* cssmKey;
247 const CSSM_ACCESS_CREDENTIALS *credentials;
248 CSSM_CSP_HANDLE cspHandle;
249 CSSM_CC_HANDLE ccHandle;
250 if (!check(SecKeyGetCSSMKey(key, &cssmKey), @"GetCSSMKey")
251 || !check(SecKeyGetCredentials(key,
252 CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED,
253 kSecCredentialTypeDefault,
254 &credentials), @"GetCredentials")
255 || !check(SecKeyGetCSPHandle(key, &cspHandle), @"GetCSPHandle")
257 || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP,
259 CSSM_PADDING_NONE, NULL, &ccHandle),
260 @"CSSM_CSP_CreateSymmetricContext"))
263 CSSM_WRAP_KEY wrapped;
264 NSData *result = nil;
265 if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped),
267 result = [NSData dataWithBytes: wrapped.KeyData.Data
268 length: wrapped.KeyData.Length];
270 CSSM_DeleteContext(ccHandle);