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)kSecReturnPersistentRef, $true});
66 if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) {
70 Assert(keyRef, @"SecItemAdd didn't return anything");
72 Assert(NO,@"Unimplemented"); //FIX
73 /* The technique below doesn't work, because there's no way to tell SecKeychainItemImport
74 what algorithm to use when importing a raw key. Still looking for a solution... --jpa 4/2009
75 SecKeyImportExportParameters params = {};
76 keyRef = importKey(keyData, kSecItemTypeSessionKey, keychain.keychainRefOrDefault, ¶ms);
83 self = [self initWithKeyRef: keyRef];
88 - (id) initWithKeyData: (NSData*)keyData
89 algorithm: (CCAlgorithm)algorithm
91 return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
94 + (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
95 algorithm: (CCAlgorithm)algorithm
96 inKeychain: (MYKeychain*)keychain
98 #if MYCRYPTO_USE_IPHONE_API
99 return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
101 inKeychain: keychain]
104 Assert(algorithm <= kCCAlgorithmRC4);
105 CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
107 flags |= CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE;
108 CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
109 SecKeyRef keyRef = NULL;
110 if (!check(SecKeyGenerate(keychain.keychainRefOrDefault, // nil kc generates a transient key
111 kCSSMAlgorithms[algorithm],
113 0, usage, flags, NULL, &keyRef),
114 @"SecKeyGenerate")) {
117 return [[[self alloc] initWithKeyRef: keyRef] autorelease];
121 + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
122 algorithm: (CCAlgorithm)algorithm {
123 return [self _generateSymmetricKeyOfSize: keySizeInBits
129 #if !TARGET_OS_IPHONE
130 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
131 withPEM: (BOOL)withPEM
133 SecKeyImportExportParameters params = {
134 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
135 .flags = kSecKeySecurePassphrase,
137 CFDataRef data = NULL;
138 if (check(SecKeychainItemExport(self.keyRef,
139 format, (withPEM ?kSecItemPemArmour :0),
141 @"SecKeychainItemExport"))
142 return [(id)CFMakeCollectable(data) autorelease];
149 - (SecExternalItemType) keyType {
150 #if MYCRYPTO_USE_IPHONE_API
151 return kSecAttrKeyClassSymmetric;
153 return kSecItemTypeSessionKey;
157 - (CCAlgorithm) algorithm {
158 CSSM_ALGORITHMS cssmAlg;
159 #if MYCRYPTO_USE_IPHONE_API
160 id keyType = [self _attribute: kSecAttrKeyType];
161 Assert(keyType!=nil, @"Key has no kSecAttrKeyType");
162 cssmAlg = [keyType unsignedIntValue];
164 cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
168 return kCCAlgorithmAES128;
170 return kCCAlgorithmDES;
171 case CSSM_ALGID_3DES_3KEY:
172 return kCCAlgorithm3DES;
173 case CSSM_ALGID_CAST:
174 return kCCAlgorithmCAST;
176 return kCCAlgorithmRC4;
178 Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
179 return (CCAlgorithm)-1;
183 - (const char*) algorithmName {
184 CCAlgorithm a = self.algorithm;
185 if (a >= 0 && a <= kCCAlgorithmRC4)
186 return kCCAlgorithmNames[a];
191 - (unsigned) keySizeInBits {
192 #if MYCRYPTO_USE_IPHONE_API
193 id keySize = [self _attribute: kSecAttrKeySizeInBits];
194 Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits");
195 return [keySize unsignedIntValue];
197 const CSSM_KEY *key = self.cssmKey;
199 return key->KeyHeader.LogicalKeySizeInBits;
204 - (NSString*) description {
205 return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
209 - (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
211 NSData *keyData = self.keyData;
212 Assert(keyData, @"Couldn't get key data");
213 NSMutableData *output = [NSMutableData dataWithLength: data.length + 256];
214 size_t bytesWritten = 0;
215 CCCryptorStatus status = CCCrypt(op, self.algorithm, options,
216 keyData.bytes, keyData.length, NULL,
217 data.bytes, data.length, output.mutableBytes, output.length,
220 Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
223 output.length = bytesWritten;
227 - (NSData*) encryptData: (NSData*)data {
228 return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
232 - (NSData*) decryptData: (NSData*)data {
233 return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
240 /* (Turned out I could just use SecKeyExport for this.)
242 static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) {
244 const CSSM_KEY* cssmKey;
245 const CSSM_ACCESS_CREDENTIALS *credentials;
246 CSSM_CSP_HANDLE cspHandle;
247 CSSM_CC_HANDLE ccHandle;
248 if (!check(SecKeyGetCSSMKey(key, &cssmKey), @"GetCSSMKey")
249 || !check(SecKeyGetCredentials(key,
250 CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED,
251 kSecCredentialTypeDefault,
252 &credentials), @"GetCredentials")
253 || !check(SecKeyGetCSPHandle(key, &cspHandle), @"GetCSPHandle")
255 || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP,
257 CSSM_PADDING_NONE, NULL, &ccHandle),
258 @"CSSM_CSP_CreateSymmetricContext"))
261 CSSM_WRAP_KEY wrapped;
262 NSData *result = nil;
263 if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped),
265 result = [NSData dataWithBytes: wrapped.KeyData.Data
266 length: wrapped.KeyData.Length];
268 CSSM_DeleteContext(ccHandle);