MYSymmetricKey.m
author snej@snej-mbp.mtv.corp.google.com
Wed Apr 08 16:30:52 2009 -0700 (2009-04-08)
changeset 3 1dfe820d7ebe
parent 1 60e4cbbb5128
child 4 f4709533c816
permissions -rw-r--r--
* Replaced MYKeyPair with MYPrivateKey.
* Changed config files.
     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     Log(@"SecItemAdd returned %@", keyRef);//TEMP
    72     Assert(keyRef, @"SecItemAdd didn't return anything");
    73 #else
    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, &params);
    79     if (!keyRef) {
    80         [self release];
    81         return nil;
    82     }
    83      */
    84 #endif
    85     self = [self initWithKeyRef: keyRef];
    86     CFRelease(keyRef);
    87     return self;
    88 }
    89 
    90 - (id) initWithKeyData: (NSData*)keyData
    91              algorithm: (CCAlgorithm)algorithm
    92 {
    93     return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
    94 }
    95 
    96 + (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
    97                                       algorithm: (CCAlgorithm)algorithm
    98                                      inKeychain: (MYKeychain*)keychain
    99 {
   100 #if MYCRYPTO_USE_IPHONE_API
   101     return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
   102                                  algorithm: algorithm
   103                                 inKeychain: keychain]
   104                     autorelease];
   105 #else
   106     Assert(algorithm <= kCCAlgorithmRC4);
   107     CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
   108     if (keychain)
   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],
   114                               keySizeInBits, 
   115                               0, usage, flags, NULL, &keyRef),
   116                @"SecKeyGenerate")) {
   117         return nil;
   118     }
   119     return [[[self alloc] initWithKeyRef: keyRef] autorelease];
   120 #endif
   121 }
   122 
   123 + (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
   124                                      algorithm: (CCAlgorithm)algorithm {
   125     return [self _generateSymmetricKeyOfSize: keySizeInBits
   126                                    algorithm: algorithm
   127                                   inKeychain: nil];
   128 }
   129 
   130 
   131 #if !TARGET_OS_IPHONE
   132 - (NSData*) exportKeyInFormat: (SecExternalFormat)format
   133                       withPEM: (BOOL)withPEM
   134 {
   135     SecKeyImportExportParameters params = {
   136         .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
   137         .flags = kSecKeySecurePassphrase,
   138     };
   139     CFDataRef data = NULL;
   140     if (check(SecKeychainItemExport(self.keyRef,
   141                                     format, (withPEM ?kSecItemPemArmour :0), 
   142                                     &params, &data),
   143               @"SecKeychainItemExport"))
   144         return [(id)CFMakeCollectable(data) autorelease];
   145     else
   146         return nil;
   147 }
   148 #endif
   149 
   150 
   151 - (SecExternalItemType) keyType {
   152 #if MYCRYPTO_USE_IPHONE_API
   153     return kSecAttrKeyClassSymmetric;
   154 #else
   155     return kSecItemTypeSessionKey;
   156 #endif
   157 }
   158 
   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];
   165 #else
   166     cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
   167 #endif
   168     switch(cssmAlg) {
   169         case CSSM_ALGID_AES:
   170             return kCCAlgorithmAES128;
   171         case CSSM_ALGID_DES:
   172             return kCCAlgorithmDES;	
   173         case CSSM_ALGID_3DES_3KEY:
   174             return kCCAlgorithm3DES;
   175         case CSSM_ALGID_CAST:
   176             return kCCAlgorithmCAST;
   177         case CSSM_ALGID_RC4:
   178             return kCCAlgorithmRC4;	
   179         default:
   180             Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
   181             return (CCAlgorithm)-1;
   182     }
   183 }
   184 
   185 - (const char*) algorithmName {
   186     CCAlgorithm a = self.algorithm;
   187     if (a >= 0 && a <= kCCAlgorithmRC4)
   188         return kCCAlgorithmNames[a];
   189     else
   190         return "???";
   191 }
   192 
   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];
   198 #else
   199     const CSSM_KEY *key = self.cssmKey;
   200     Assert(key);
   201     return key->KeyHeader.LogicalKeySizeInBits;
   202 #endif
   203 }
   204 
   205 
   206 - (NSString*) description {
   207     return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
   208 }
   209 
   210 
   211 - (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
   212 {
   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,
   220                                      &bytesWritten);
   221     if (status) {
   222         Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
   223         return nil;
   224     }
   225     output.length = bytesWritten;
   226     return output;
   227 }
   228 
   229 - (NSData*) encryptData: (NSData*)data {
   230     return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
   231 }
   232 
   233 
   234 - (NSData*) decryptData: (NSData*)data {
   235     return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
   236 }
   237 
   238 
   239 @end
   240 
   241 
   242 /* (Turned out I could just use SecKeyExport for this.)
   243  
   244 static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) {
   245     CAssert(key);
   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")
   256         
   257         || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP,
   258                                                       NULL, NULL, NULL,
   259                                                       CSSM_PADDING_NONE, NULL, &ccHandle),
   260                       @"CSSM_CSP_CreateSymmetricContext"))
   261         return nil;
   262     
   263     CSSM_WRAP_KEY wrapped;
   264     NSData *result = nil;
   265     if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped),
   266                  @"CSSM_WrapKey")) {
   267         result = [NSData dataWithBytes: wrapped.KeyData.Data 
   268                                 length: wrapped.KeyData.Length];
   269     }
   270     CSSM_DeleteContext(ccHandle);
   271     return result;
   272 }
   273 */