MYSymmetricKey.m
author snej@snej.local
Tue Apr 14 18:34:52 2009 -0700 (2009-04-14)
changeset 11 3568d5fd4b6a
parent 4 f4709533c816
child 12 e4c971be4079
permissions -rw-r--r--
* The build process runs Doxygen only if it's installed (i.e. on the shell search path).
* Added instructions to the README on setting up a named Source Tree for MYUtilities.
* Changed the RSA key size in MYCryptoTest to 2048 and made it a named constant.
snej@0
     1
//
snej@0
     2
//  MYSymmetricKey.m
snej@0
     3
//  MYCrypto
snej@0
     4
//
snej@0
     5
//  Created by Jens Alfke on 4/2/09.
snej@0
     6
//  Copyright 2009 Jens Alfke. All rights reserved.
snej@0
     7
//
snej@0
     8
snej@0
     9
#import "MYSymmetricKey.h"
snej@0
    10
#import "MYCryptor.h"
snej@0
    11
#import "MYCrypto_Private.h"
snej@0
    12
snej@0
    13
snej@2
    14
#if MYCRYPTO_USE_IPHONE_API
snej@0
    15
typedef uint32_t CSSM_ALGORITHMS;
snej@0
    16
enum {
snej@0
    17
// Taken from cssmtype.h in OS X 10.5 SDK:
snej@0
    18
	CSSM_ALGID_NONE =					0x00000000L,
snej@0
    19
	CSSM_ALGID_DES =					CSSM_ALGID_NONE + 14,
snej@0
    20
	CSSM_ALGID_3DES_3KEY_EDE =			CSSM_ALGID_NONE + 17,
snej@0
    21
	CSSM_ALGID_3DES_3KEY =           	CSSM_ALGID_3DES_3KEY_EDE,
snej@0
    22
	CSSM_ALGID_RC4 =					CSSM_ALGID_NONE + 25,
snej@0
    23
	CSSM_ALGID_CAST =					CSSM_ALGID_NONE + 27,
snej@0
    24
	CSSM_ALGID_VENDOR_DEFINED =			CSSM_ALGID_NONE + 0x80000000L,
snej@0
    25
	CSSM_ALGID_AES
snej@0
    26
};
snej@0
    27
#else
snej@0
    28
#import <Security/cssmtype.h>
snej@0
    29
#endif
snej@0
    30
snej@0
    31
static const CSSM_ALGORITHMS kCSSMAlgorithms[] = {
snej@0
    32
    CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4
snej@0
    33
};
snej@0
    34
snej@2
    35
static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"};
snej@2
    36
snej@0
    37
snej@0
    38
#pragma mark -
snej@0
    39
@implementation MYSymmetricKey
snej@0
    40
snej@0
    41
snej@1
    42
- (id) _initWithKeyData: (NSData*)keyData
snej@1
    43
              algorithm: (CCAlgorithm)algorithm
snej@1
    44
             inKeychain: (MYKeychain*)keychain
snej@1
    45
{
snej@1
    46
    Assert(algorithm <= kCCAlgorithmRC4);
snej@1
    47
    Assert(keyData);
snej@1
    48
    SecKeyRef keyRef = NULL;
snej@2
    49
#if MYCRYPTO_USE_IPHONE_API
snej@2
    50
    NSNumber *keySizeInBits = [NSNumber numberWithUnsignedInt: keyData.length * 8];
snej@1
    51
    NSDictionary *keyAttrs = $dict( {(id)kSecClass, (id)kSecClassKey},
snej@2
    52
                                    //{(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
snej@2
    53
                                    {(id)kSecAttrKeyType, [NSNumber numberWithUnsignedInt: kCSSMAlgorithms[algorithm]]},
snej@2
    54
                                    {(id)kSecAttrKeySizeInBits, keySizeInBits},
snej@2
    55
                                    {(id)kSecAttrEffectiveKeySize, keySizeInBits},
snej@1
    56
                                    {(id)kSecAttrIsPermanent, keychain ?$true :$false},
snej@2
    57
                                    {(id)kSecAttrCanEncrypt, $true},
snej@2
    58
                                    {(id)kSecAttrCanDecrypt, $true},
snej@2
    59
                                    {(id)kSecAttrCanWrap, $false},
snej@2
    60
                                    {(id)kSecAttrCanUnwrap, $false},
snej@2
    61
                                    {(id)kSecAttrCanDerive, $false},
snej@2
    62
                                    {(id)kSecAttrCanSign, $false},
snej@2
    63
                                    {(id)kSecAttrCanVerify, $false},
snej@2
    64
                                    {(id)kSecValueData, keyData},
snej@2
    65
                                    {(id)kSecReturnPersistentRef, $true});
snej@1
    66
    if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) {
snej@1
    67
        [self release];
snej@1
    68
        return nil;
snej@1
    69
    }
snej@2
    70
    Assert(keyRef, @"SecItemAdd didn't return anything");
snej@1
    71
#else
snej@1
    72
    Assert(NO,@"Unimplemented"); //FIX
snej@2
    73
    /* The technique below doesn't work, because there's no way to tell SecKeychainItemImport
snej@2
    74
       what algorithm to use when importing a raw key. Still looking for a solution... --jpa 4/2009
snej@2
    75
    SecKeyImportExportParameters params = {};
snej@2
    76
    keyRef = importKey(keyData, kSecItemTypeSessionKey, keychain.keychainRefOrDefault, &params);
snej@2
    77
    if (!keyRef) {
snej@2
    78
        [self release];
snej@2
    79
        return nil;
snej@2
    80
    }
snej@2
    81
     */
snej@1
    82
#endif
snej@1
    83
    self = [self initWithKeyRef: keyRef];
snej@1
    84
    CFRelease(keyRef);
snej@1
    85
    return self;
snej@1
    86
}
snej@1
    87
snej@1
    88
- (id) initWithKeyData: (NSData*)keyData
snej@1
    89
             algorithm: (CCAlgorithm)algorithm
snej@1
    90
{
snej@1
    91
    return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
snej@1
    92
}
snej@1
    93
snej@0
    94
+ (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0
    95
                                      algorithm: (CCAlgorithm)algorithm
snej@0
    96
                                     inKeychain: (MYKeychain*)keychain
snej@0
    97
{
snej@2
    98
#if MYCRYPTO_USE_IPHONE_API
snej@2
    99
    return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
snej@1
   100
                                 algorithm: algorithm
snej@1
   101
                                inKeychain: keychain]
snej@1
   102
                    autorelease];
snej@1
   103
#else
snej@0
   104
    Assert(algorithm <= kCCAlgorithmRC4);
snej@0
   105
    CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
snej@0
   106
    if (keychain)
snej@0
   107
        flags |= CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE;
snej@0
   108
    CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
snej@1
   109
    SecKeyRef keyRef = NULL;
snej@0
   110
    if (!check(SecKeyGenerate(keychain.keychainRefOrDefault,    // nil kc generates a transient key
snej@0
   111
                              kCSSMAlgorithms[algorithm],
snej@0
   112
                              keySizeInBits, 
snej@0
   113
                              0, usage, flags, NULL, &keyRef),
snej@0
   114
               @"SecKeyGenerate")) {
snej@0
   115
        return nil;
snej@0
   116
    }
snej@1
   117
    return [[[self alloc] initWithKeyRef: keyRef] autorelease];
snej@0
   118
#endif
snej@0
   119
}
snej@0
   120
snej@0
   121
+ (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0
   122
                                     algorithm: (CCAlgorithm)algorithm {
snej@0
   123
    return [self _generateSymmetricKeyOfSize: keySizeInBits
snej@0
   124
                                   algorithm: algorithm
snej@0
   125
                                  inKeychain: nil];
snej@0
   126
}
snej@0
   127
snej@0
   128
snej@2
   129
#if !TARGET_OS_IPHONE
snej@2
   130
- (NSData*) exportKeyInFormat: (SecExternalFormat)format
snej@2
   131
                      withPEM: (BOOL)withPEM
snej@2
   132
{
snej@2
   133
    SecKeyImportExportParameters params = {
snej@2
   134
        .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
snej@2
   135
        .flags = kSecKeySecurePassphrase,
snej@2
   136
    };
snej@2
   137
    CFDataRef data = NULL;
snej@2
   138
    if (check(SecKeychainItemExport(self.keyRef,
snej@2
   139
                                    format, (withPEM ?kSecItemPemArmour :0), 
snej@2
   140
                                    &params, &data),
snej@2
   141
              @"SecKeychainItemExport"))
snej@2
   142
        return [(id)CFMakeCollectable(data) autorelease];
snej@2
   143
    else
snej@2
   144
        return nil;
snej@2
   145
}
snej@2
   146
#endif
snej@2
   147
snej@0
   148
snej@0
   149
- (SecExternalItemType) keyType {
snej@2
   150
#if MYCRYPTO_USE_IPHONE_API
snej@0
   151
    return kSecAttrKeyClassSymmetric;
snej@0
   152
#else
snej@0
   153
    return kSecItemTypeSessionKey;
snej@0
   154
#endif
snej@0
   155
}
snej@0
   156
snej@0
   157
- (CCAlgorithm) algorithm {
snej@0
   158
    CSSM_ALGORITHMS cssmAlg;
snej@2
   159
#if MYCRYPTO_USE_IPHONE_API
snej@0
   160
    id keyType = [self _attribute: kSecAttrKeyType];
snej@0
   161
    Assert(keyType!=nil, @"Key has no kSecAttrKeyType");
snej@0
   162
    cssmAlg = [keyType unsignedIntValue];
snej@0
   163
#else
snej@0
   164
    cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
snej@0
   165
#endif
snej@0
   166
    switch(cssmAlg) {
snej@0
   167
        case CSSM_ALGID_AES:
snej@0
   168
            return kCCAlgorithmAES128;
snej@0
   169
        case CSSM_ALGID_DES:
snej@0
   170
            return kCCAlgorithmDES;	
snej@0
   171
        case CSSM_ALGID_3DES_3KEY:
snej@0
   172
            return kCCAlgorithm3DES;
snej@0
   173
        case CSSM_ALGID_CAST:
snej@0
   174
            return kCCAlgorithmCAST;
snej@0
   175
        case CSSM_ALGID_RC4:
snej@0
   176
            return kCCAlgorithmRC4;	
snej@0
   177
        default:
snej@0
   178
            Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
snej@0
   179
            return (CCAlgorithm)-1;
snej@0
   180
    }
snej@0
   181
}
snej@0
   182
snej@2
   183
- (const char*) algorithmName {
snej@2
   184
    CCAlgorithm a = self.algorithm;
snej@2
   185
    if (a >= 0 && a <= kCCAlgorithmRC4)
snej@2
   186
        return kCCAlgorithmNames[a];
snej@2
   187
    else
snej@2
   188
        return "???";
snej@2
   189
}
snej@2
   190
snej@2
   191
- (unsigned) keySizeInBits {
snej@2
   192
#if MYCRYPTO_USE_IPHONE_API
snej@2
   193
    id keySize = [self _attribute: kSecAttrKeySizeInBits];
snej@2
   194
    Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits");
snej@2
   195
    return [keySize unsignedIntValue];
snej@2
   196
#else
snej@2
   197
    const CSSM_KEY *key = self.cssmKey;
snej@2
   198
    Assert(key);
snej@2
   199
    return key->KeyHeader.LogicalKeySizeInBits;
snej@2
   200
#endif
snej@2
   201
}
snej@2
   202
snej@2
   203
snej@2
   204
- (NSString*) description {
snej@2
   205
    return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
snej@2
   206
}
snej@2
   207
snej@0
   208
snej@0
   209
- (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
snej@0
   210
{
snej@0
   211
    NSData *keyData = self.keyData;
snej@0
   212
    Assert(keyData, @"Couldn't get key data");
snej@0
   213
    NSMutableData *output = [NSMutableData dataWithLength: data.length + 256];
snej@0
   214
    size_t bytesWritten = 0;
snej@0
   215
    CCCryptorStatus status = CCCrypt(op, self.algorithm, options, 
snej@0
   216
                                     keyData.bytes, keyData.length, NULL,
snej@0
   217
                                     data.bytes, data.length, output.mutableBytes, output.length,
snej@0
   218
                                     &bytesWritten);
snej@0
   219
    if (status) {
snej@0
   220
        Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
snej@0
   221
        return nil;
snej@0
   222
    }
snej@0
   223
    output.length = bytesWritten;
snej@0
   224
    return output;
snej@0
   225
}
snej@0
   226
snej@0
   227
- (NSData*) encryptData: (NSData*)data {
snej@0
   228
    return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
snej@0
   229
}
snej@0
   230
snej@0
   231
snej@0
   232
- (NSData*) decryptData: (NSData*)data {
snej@0
   233
    return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
snej@0
   234
}
snej@0
   235
snej@0
   236
snej@0
   237
@end
snej@0
   238
snej@0
   239
snej@0
   240
/* (Turned out I could just use SecKeyExport for this.)
snej@0
   241
 
snej@0
   242
static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) {
snej@0
   243
    CAssert(key);
snej@0
   244
    const CSSM_KEY* cssmKey;
snej@0
   245
    const CSSM_ACCESS_CREDENTIALS *credentials;
snej@0
   246
    CSSM_CSP_HANDLE cspHandle;
snej@0
   247
    CSSM_CC_HANDLE ccHandle;
snej@0
   248
    if (!check(SecKeyGetCSSMKey(key, &cssmKey), @"GetCSSMKey")
snej@0
   249
        || !check(SecKeyGetCredentials(key, 
snej@0
   250
                                       CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, 
snej@0
   251
                                       kSecCredentialTypeDefault,
snej@0
   252
                                       &credentials), @"GetCredentials")
snej@0
   253
        || !check(SecKeyGetCSPHandle(key, &cspHandle), @"GetCSPHandle")
snej@0
   254
        
snej@0
   255
        || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP,
snej@0
   256
                                                      NULL, NULL, NULL,
snej@0
   257
                                                      CSSM_PADDING_NONE, NULL, &ccHandle),
snej@0
   258
                      @"CSSM_CSP_CreateSymmetricContext"))
snej@0
   259
        return nil;
snej@0
   260
    
snej@0
   261
    CSSM_WRAP_KEY wrapped;
snej@0
   262
    NSData *result = nil;
snej@0
   263
    if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped),
snej@0
   264
                 @"CSSM_WrapKey")) {
snej@0
   265
        result = [NSData dataWithBytes: wrapped.KeyData.Data 
snej@0
   266
                                length: wrapped.KeyData.Length];
snej@0
   267
    }
snej@0
   268
    CSSM_DeleteContext(ccHandle);
snej@0
   269
    return result;
snej@0
   270
}
snej@0
   271
*/