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