MYSymmetricKey.m
author snej@snej.local
Thu Apr 09 22:46:48 2009 -0700 (2009-04-09)
changeset 6 2d7692f9b6b4
parent 2 8982b8fada63
child 8 4c0eafa7b233
permissions -rw-r--r--
Updated the README for the 0.1 release.
     1 //
     2 //  MYSymmetricKey.m
     3 //  MYCrypto
     4 //
     5 //  Created by Jens Alfke on 4/2/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYSymmetricKey.h"
    10 #import "MYCryptor.h"
    11 #import "MYCrypto_Private.h"
    12 
    13 
    14 #if MYCRYPTO_USE_IPHONE_API
    15 typedef uint32_t CSSM_ALGORITHMS;
    16 enum {
    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,
    25 	CSSM_ALGID_AES
    26 };
    27 #else
    28 #import <Security/cssmtype.h>
    29 #endif
    30 
    31 static const CSSM_ALGORITHMS kCSSMAlgorithms[] = {
    32     CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4
    33 };
    34 
    35 static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"};
    36 
    37 
    38 #pragma mark -
    39 @implementation MYSymmetricKey
    40 
    41 
    42 - (id) _initWithKeyData: (NSData*)keyData
    43               algorithm: (CCAlgorithm)algorithm
    44              inKeychain: (MYKeychain*)keychain
    45 {
    46     Assert(algorithm <= kCCAlgorithmRC4);
    47     Assert(keyData);
    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")) {
    68         [self release];
    69         return nil;
    70     }
    71     Assert(keyRef, @"SecItemAdd didn't return anything");
    72 #else
    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, &params);
    78     if (!keyRef) {
    79         [self release];
    80         return nil;
    81     }
    82      */
    83 #endif
    84     self = [self initWithKeyRef: keyRef];
    85     CFRelease(keyRef);
    86     return self;
    87 }
    88 
    89 - (id) initWithKeyData: (NSData*)keyData
    90              algorithm: (CCAlgorithm)algorithm
    91 {
    92     return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
    93 }
    94 
    95 + (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
    96                                       algorithm: (CCAlgorithm)algorithm
    97                                      inKeychain: (MYKeychain*)keychain
    98 {
    99 #if MYCRYPTO_USE_IPHONE_API
   100     return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
   101                                  algorithm: algorithm
   102                                 inKeychain: keychain]
   103                     autorelease];
   104 #else
   105     Assert(algorithm <= kCCAlgorithmRC4);
   106     CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
   107     if (keychain)
   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],
   113                               keySizeInBits, 
   114                               0, usage, flags, NULL, &keyRef),
   115                @"SecKeyGenerate")) {
   116         return nil;
   117     }
   118     return [[[self alloc] initWithKeyRef: keyRef] autorelease];
   119 #endif
   120 }
   121 
   122 + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
   123                                      algorithm: (CCAlgorithm)algorithm {
   124     return [self _generateSymmetricKeyOfSize: keySizeInBits
   125                                    algorithm: algorithm
   126                                   inKeychain: nil];
   127 }
   128 
   129 
   130 #if !TARGET_OS_IPHONE
   131 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
   132                       withPEM: (BOOL)withPEM
   133 {
   134     SecKeyImportExportParameters params = {
   135         .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
   136         .flags = kSecKeySecurePassphrase,
   137     };
   138     CFDataRef data = NULL;
   139     if (check(SecKeychainItemExport(self.keyRef,
   140                                     format, (withPEM ?kSecItemPemArmour :0), 
   141                                     &params, &data),
   142               @"SecKeychainItemExport"))
   143         return [(id)CFMakeCollectable(data) autorelease];
   144     else
   145         return nil;
   146 }
   147 #endif
   148 
   149 
   150 - (SecExternalItemType) keyType {
   151 #if MYCRYPTO_USE_IPHONE_API
   152     return kSecAttrKeyClassSymmetric;
   153 #else
   154     return kSecItemTypeSessionKey;
   155 #endif
   156 }
   157 
   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];
   164 #else
   165     cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
   166 #endif
   167     switch(cssmAlg) {
   168         case CSSM_ALGID_AES:
   169             return kCCAlgorithmAES128;
   170         case CSSM_ALGID_DES:
   171             return kCCAlgorithmDES;	
   172         case CSSM_ALGID_3DES_3KEY:
   173             return kCCAlgorithm3DES;
   174         case CSSM_ALGID_CAST:
   175             return kCCAlgorithmCAST;
   176         case CSSM_ALGID_RC4:
   177             return kCCAlgorithmRC4;	
   178         default:
   179             Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
   180             return (CCAlgorithm)-1;
   181     }
   182 }
   183 
   184 - (const char*) algorithmName {
   185     CCAlgorithm a = self.algorithm;
   186     if (a >= 0 && a <= kCCAlgorithmRC4)
   187         return kCCAlgorithmNames[a];
   188     else
   189         return "???";
   190 }
   191 
   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];
   197 #else
   198     const CSSM_KEY *key = self.cssmKey;
   199     Assert(key);
   200     return key->KeyHeader.LogicalKeySizeInBits;
   201 #endif
   202 }
   203 
   204 
   205 - (NSString*) description {
   206     return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
   207 }
   208 
   209 
   210 - (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
   211 {
   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,
   219                                      &bytesWritten);
   220     if (status) {
   221         Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
   222         return nil;
   223     }
   224     output.length = bytesWritten;
   225     return output;
   226 }
   227 
   228 - (NSData*) encryptData: (NSData*)data {
   229     return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
   230 }
   231 
   232 
   233 - (NSData*) decryptData: (NSData*)data {
   234     return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
   235 }
   236 
   237 
   238 @end
   239 
   240 
   241 /* (Turned out I could just use SecKeyExport for this.)
   242  
   243 static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) {
   244     CAssert(key);
   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")
   255         
   256         || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP,
   257                                                       NULL, NULL, NULL,
   258                                                       CSSM_PADDING_NONE, NULL, &ccHandle),
   259                       @"CSSM_CSP_CreateSymmetricContext"))
   260         return nil;
   261     
   262     CSSM_WRAP_KEY wrapped;
   263     NSData *result = nil;
   264     if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped),
   265                  @"CSSM_WrapKey")) {
   266         result = [NSData dataWithBytes: wrapped.KeyData.Data 
   267                                 length: wrapped.KeyData.Length];
   268     }
   269     CSSM_DeleteContext(ccHandle);
   270     return result;
   271 }
   272 */