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.
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)kSecAttrApplicationTag, [@"foo" dataUsingEncoding: NSUTF8StringEncoding]}, //TEMP
snej@2
    66
                                    {(id)kSecReturnPersistentRef, $true});
snej@1
    67
    if (!check(SecItemAdd((CFDictionaryRef)keyAttrs, (CFTypeRef*)&keyRef), @"SecItemAdd")) {
snej@1
    68
        [self release];
snej@1
    69
        return nil;
snej@1
    70
    }
snej@2
    71
    Assert(keyRef, @"SecItemAdd didn't return anything");
snej@1
    72
#else
snej@1
    73
    Assert(NO,@"Unimplemented"); //FIX
snej@2
    74
    /* The technique below doesn't work, because there's no way to tell SecKeychainItemImport
snej@2
    75
       what algorithm to use when importing a raw key. Still looking for a solution... --jpa 4/2009
snej@2
    76
    SecKeyImportExportParameters params = {};
snej@2
    77
    keyRef = importKey(keyData, kSecItemTypeSessionKey, keychain.keychainRefOrDefault, &params);
snej@2
    78
    if (!keyRef) {
snej@2
    79
        [self release];
snej@2
    80
        return nil;
snej@2
    81
    }
snej@2
    82
     */
snej@1
    83
#endif
snej@1
    84
    self = [self initWithKeyRef: keyRef];
snej@1
    85
    CFRelease(keyRef);
snej@1
    86
    return self;
snej@1
    87
}
snej@1
    88
snej@1
    89
- (id) initWithKeyData: (NSData*)keyData
snej@1
    90
             algorithm: (CCAlgorithm)algorithm
snej@1
    91
{
snej@1
    92
    return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
snej@1
    93
}
snej@1
    94
snej@0
    95
+ (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0
    96
                                      algorithm: (CCAlgorithm)algorithm
snej@0
    97
                                     inKeychain: (MYKeychain*)keychain
snej@0
    98
{
snej@2
    99
#if MYCRYPTO_USE_IPHONE_API
snej@2
   100
    return [[[self alloc] _initWithKeyData: [MYCryptor randomKeyOfLength: keySizeInBits]
snej@1
   101
                                 algorithm: algorithm
snej@1
   102
                                inKeychain: keychain]
snej@1
   103
                    autorelease];
snej@1
   104
#else
snej@0
   105
    Assert(algorithm <= kCCAlgorithmRC4);
snej@0
   106
    CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
snej@0
   107
    if (keychain)
snej@0
   108
        flags |= CSSM_KEYATTR_PERMANENT | CSSM_KEYATTR_SENSITIVE | CSSM_KEYATTR_EXTRACTABLE;
snej@0
   109
    CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
snej@1
   110
    SecKeyRef keyRef = NULL;
snej@0
   111
    if (!check(SecKeyGenerate(keychain.keychainRefOrDefault,    // nil kc generates a transient key
snej@0
   112
                              kCSSMAlgorithms[algorithm],
snej@0
   113
                              keySizeInBits, 
snej@0
   114
                              0, usage, flags, NULL, &keyRef),
snej@0
   115
               @"SecKeyGenerate")) {
snej@0
   116
        return nil;
snej@0
   117
    }
snej@1
   118
    return [[[self alloc] initWithKeyRef: keyRef] autorelease];
snej@0
   119
#endif
snej@0
   120
}
snej@0
   121
snej@0
   122
+ (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0
   123
                                     algorithm: (CCAlgorithm)algorithm {
snej@0
   124
    return [self _generateSymmetricKeyOfSize: keySizeInBits
snej@0
   125
                                   algorithm: algorithm
snej@0
   126
                                  inKeychain: nil];
snej@0
   127
}
snej@0
   128
snej@0
   129
snej@2
   130
#if !TARGET_OS_IPHONE
snej@2
   131
- (NSData*) exportKeyInFormat: (SecExternalFormat)format
snej@2
   132
                      withPEM: (BOOL)withPEM
snej@2
   133
{
snej@2
   134
    SecKeyImportExportParameters params = {
snej@2
   135
        .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
snej@2
   136
        .flags = kSecKeySecurePassphrase,
snej@2
   137
    };
snej@2
   138
    CFDataRef data = NULL;
snej@2
   139
    if (check(SecKeychainItemExport(self.keyRef,
snej@2
   140
                                    format, (withPEM ?kSecItemPemArmour :0), 
snej@2
   141
                                    &params, &data),
snej@2
   142
              @"SecKeychainItemExport"))
snej@2
   143
        return [(id)CFMakeCollectable(data) autorelease];
snej@2
   144
    else
snej@2
   145
        return nil;
snej@2
   146
}
snej@2
   147
#endif
snej@2
   148
snej@0
   149
snej@0
   150
- (SecExternalItemType) keyType {
snej@2
   151
#if MYCRYPTO_USE_IPHONE_API
snej@0
   152
    return kSecAttrKeyClassSymmetric;
snej@0
   153
#else
snej@0
   154
    return kSecItemTypeSessionKey;
snej@0
   155
#endif
snej@0
   156
}
snej@0
   157
snej@0
   158
- (CCAlgorithm) algorithm {
snej@0
   159
    CSSM_ALGORITHMS cssmAlg;
snej@2
   160
#if MYCRYPTO_USE_IPHONE_API
snej@0
   161
    id keyType = [self _attribute: kSecAttrKeyType];
snej@0
   162
    Assert(keyType!=nil, @"Key has no kSecAttrKeyType");
snej@0
   163
    cssmAlg = [keyType unsignedIntValue];
snej@0
   164
#else
snej@0
   165
    cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
snej@0
   166
#endif
snej@0
   167
    switch(cssmAlg) {
snej@0
   168
        case CSSM_ALGID_AES:
snej@0
   169
            return kCCAlgorithmAES128;
snej@0
   170
        case CSSM_ALGID_DES:
snej@0
   171
            return kCCAlgorithmDES;	
snej@0
   172
        case CSSM_ALGID_3DES_3KEY:
snej@0
   173
            return kCCAlgorithm3DES;
snej@0
   174
        case CSSM_ALGID_CAST:
snej@0
   175
            return kCCAlgorithmCAST;
snej@0
   176
        case CSSM_ALGID_RC4:
snej@0
   177
            return kCCAlgorithmRC4;	
snej@0
   178
        default:
snej@0
   179
            Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
snej@0
   180
            return (CCAlgorithm)-1;
snej@0
   181
    }
snej@0
   182
}
snej@0
   183
snej@2
   184
- (const char*) algorithmName {
snej@2
   185
    CCAlgorithm a = self.algorithm;
snej@2
   186
    if (a >= 0 && a <= kCCAlgorithmRC4)
snej@2
   187
        return kCCAlgorithmNames[a];
snej@2
   188
    else
snej@2
   189
        return "???";
snej@2
   190
}
snej@2
   191
snej@2
   192
- (unsigned) keySizeInBits {
snej@2
   193
#if MYCRYPTO_USE_IPHONE_API
snej@2
   194
    id keySize = [self _attribute: kSecAttrKeySizeInBits];
snej@2
   195
    Assert(keySize!=nil, @"Key has no kSecAttrKeySizeInBits");
snej@2
   196
    return [keySize unsignedIntValue];
snej@2
   197
#else
snej@2
   198
    const CSSM_KEY *key = self.cssmKey;
snej@2
   199
    Assert(key);
snej@2
   200
    return key->KeyHeader.LogicalKeySizeInBits;
snej@2
   201
#endif
snej@2
   202
}
snej@2
   203
snej@2
   204
snej@2
   205
- (NSString*) description {
snej@2
   206
    return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
snej@2
   207
}
snej@2
   208
snej@0
   209
snej@0
   210
- (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
snej@0
   211
{
snej@0
   212
    NSData *keyData = self.keyData;
snej@0
   213
    Assert(keyData, @"Couldn't get key data");
snej@0
   214
    NSMutableData *output = [NSMutableData dataWithLength: data.length + 256];
snej@0
   215
    size_t bytesWritten = 0;
snej@0
   216
    CCCryptorStatus status = CCCrypt(op, self.algorithm, options, 
snej@0
   217
                                     keyData.bytes, keyData.length, NULL,
snej@0
   218
                                     data.bytes, data.length, output.mutableBytes, output.length,
snej@0
   219
                                     &bytesWritten);
snej@0
   220
    if (status) {
snej@0
   221
        Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
snej@0
   222
        return nil;
snej@0
   223
    }
snej@0
   224
    output.length = bytesWritten;
snej@0
   225
    return output;
snej@0
   226
}
snej@0
   227
snej@0
   228
- (NSData*) encryptData: (NSData*)data {
snej@0
   229
    return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
snej@0
   230
}
snej@0
   231
snej@0
   232
snej@0
   233
- (NSData*) decryptData: (NSData*)data {
snej@0
   234
    return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
snej@0
   235
}
snej@0
   236
snej@0
   237
snej@0
   238
@end
snej@0
   239
snej@0
   240
snej@0
   241
/* (Turned out I could just use SecKeyExport for this.)
snej@0
   242
 
snej@0
   243
static NSData* wrap(SecKeyRef key, CSSM_ALGORITHMS algorithm) {
snej@0
   244
    CAssert(key);
snej@0
   245
    const CSSM_KEY* cssmKey;
snej@0
   246
    const CSSM_ACCESS_CREDENTIALS *credentials;
snej@0
   247
    CSSM_CSP_HANDLE cspHandle;
snej@0
   248
    CSSM_CC_HANDLE ccHandle;
snej@0
   249
    if (!check(SecKeyGetCSSMKey(key, &cssmKey), @"GetCSSMKey")
snej@0
   250
        || !check(SecKeyGetCredentials(key, 
snej@0
   251
                                       CSSM_ACL_AUTHORIZATION_EXPORT_WRAPPED, 
snej@0
   252
                                       kSecCredentialTypeDefault,
snej@0
   253
                                       &credentials), @"GetCredentials")
snej@0
   254
        || !check(SecKeyGetCSPHandle(key, &cspHandle), @"GetCSPHandle")
snej@0
   255
        
snej@0
   256
        || !checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle, algorithm, CSSM_ALGMODE_WRAP,
snej@0
   257
                                                      NULL, NULL, NULL,
snej@0
   258
                                                      CSSM_PADDING_NONE, NULL, &ccHandle),
snej@0
   259
                      @"CSSM_CSP_CreateSymmetricContext"))
snej@0
   260
        return nil;
snej@0
   261
    
snej@0
   262
    CSSM_WRAP_KEY wrapped;
snej@0
   263
    NSData *result = nil;
snej@0
   264
    if(checkcssm(CSSM_WrapKey(ccHandle, credentials, cssmKey, NULL, &wrapped),
snej@0
   265
                 @"CSSM_WrapKey")) {
snej@0
   266
        result = [NSData dataWithBytes: wrapped.KeyData.Data 
snej@0
   267
                                length: wrapped.KeyData.Length];
snej@0
   268
    }
snej@0
   269
    CSSM_DeleteContext(ccHandle);
snej@0
   270
    return result;
snej@0
   271
}
snej@0
   272
*/