Updated the README for the 0.1 release.
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 Assert(keyRef, @"SecItemAdd didn't return anything");
73 Assert(NO,@"Unimplemented"); //FIX
74 /* The technique below doesn't work, because there's no way to tell SecKeychainItemImport
75 what algorithm to use when importing a raw key. Still looking for a solution... --jpa 4/2009
76 SecKeyImportExportParameters params = {};
77 keyRef = importKey(keyData, kSecItemTypeSessionKey, keychain.keychainRefOrDefault, ¶ms);
84 self = [self initWithKeyRef: keyRef];
89 - (id) initWithKeyData: (NSData*)keyData
90 algorithm: (CCAlgorithm)algorithm
92 return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
95 + (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
96 algorithm: (CCAlgorithm)algorithm
97 inKeychain: (MYKeychain*)keychain
99 #if MYCRYPTO_USE_IPHONE_API
100 return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
102 inKeychain: keychain]
105 Assert(algorithm <= kCCAlgorithmRC4);
106 CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
108 flags |= CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE;
109 CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
110 SecKeyRef keyRef = NULL;
111 if (!check(SecKeyGenerate(keychain.keychainRefOrDefault, // nil kc generates a transient key
112 kCSSMAlgorithms[algorithm],
114 0, usage, flags, NULL, &keyRef),
115 @"SecKeyGenerate")) {
118 return [[[self alloc] initWithKeyRef: keyRef] autorelease];
122 + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
123 algorithm: (CCAlgorithm)algorithm {
124 return [self _generateSymmetricKeyOfSize: keySizeInBits
130 #if !TARGET_OS_IPHONE
131 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
132 withPEM: (BOOL)withPEM
134 SecKeyImportExportParameters params = {
135 .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
136 .flags = kSecKeySecurePassphrase,
138 CFDataRef data = NULL;
139 if (check(SecKeychainItemExport(self.keyRef,
140 format, (withPEM ?kSecItemPemArmour :0),
142 @"SecKeychainItemExport"))
143 return [(id)CFMakeCollectable(data) autorelease];
150 - (SecExternalItemType) keyType {
151 #if MYCRYPTO_USE_IPHONE_API
152 return kSecAttrKeyClassSymmetric;
154 return kSecItemTypeSessionKey;
158 - (CCAlgorithm) algorithm {
159 CSSM_ALGORITHMS cssmAlg;
160 #if MYCRYPTO_USE_IPHONE_API
161 id keyType = [self _attribute: kSecAttrKeyType];
162 Assert(keyType!=nil, @"Key has no kSecAttrKeyType");
163 cssmAlg = [keyType unsignedIntValue];
165 cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
169 return kCCAlgorithmAES128;
171 return kCCAlgorithmDES;
172 case CSSM_ALGID_3DES_3KEY:
173 return kCCAlgorithm3DES;
174 case CSSM_ALGID_CAST:
175 return kCCAlgorithmCAST;
177 return kCCAlgorithmRC4;
179 Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
180 return (CCAlgorithm)-1;
184 - (const char*) algorithmName {
185 CCAlgorithm a = self.algorithm;
186 if (a >= 0 && a <= kCCAlgorithmRC4)
187 return kCCAlgorithmNames[a];
192 - (unsigned) keySizeInBits {
193 #if MYCRYPTO_USE_IPHONE_API
194 id keySize = [self _attribute: kSecAttrKeySizeInBits];
195 Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits");
196 return [keySize unsignedIntValue];
198 const CSSM_KEY *key = self.cssmKey;
200 return key->KeyHeader.LogicalKeySizeInBits;
205 - (NSString*) description {
206 return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
210 - (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
212 NSData *keyData = self.keyData;
213 Assert(keyData, @"Couldn't get key data");
214 NSMutableData *output = [NSMutableData dataWithLength: data.length + 256];
215 size_t bytesWritten = 0;
216 CCCryptorStatus status = CCCrypt(op, self.algorithm, options,
217 keyData.bytes, keyData.length, NULL,
218 data.bytes, data.length, output.mutableBytes, output.length,
221 Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
224 output.length = bytesWritten;
228 - (NSData*) encryptData: (NSData*)data {
229 return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
233 - (NSData*) decryptData: (NSData*)data {
234 return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
241 /* (Turned out I could just use SecKeyExport for this.)
243 static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) {
245 const CSSM_KEY* cssmKey;
246 const CSSM_ACCESS_CREDENTIALS *credentials;
247 CSSM_CSP_HANDLE cspHandle;
248 CSSM_CC_HANDLE ccHandle;
249 if (!check(SecKeyGetCSSMKey(key, &cssmKey), @"GetCSSMKey")
250 || !check(SecKeyGetCredentials(key,
251 CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED,
252 kSecCredentialTypeDefault,
253 &credentials), @"GetCredentials")
254 || !check(SecKeyGetCSPHandle(key, &cspHandle), @"GetCSPHandle")
256 || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP,
258 CSSM_PADDING_NONE, NULL, &ccHandle),
259 @"CSSM_CSP_CreateSymmetricContext"))
262 CSSM_WRAP_KEY wrapped;
263 NSData *result = nil;
264 if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped),
266 result = [NSData dataWithBytes: wrapped.KeyData.Data
267 length: wrapped.KeyData.Length];
269 CSSM_DeleteContext(ccHandle);