MYSymmetricKey.m
author Jens Alfke <jens@mooseyard.com>
Sun Apr 19 22:05:51 2009 -0700 (2009-04-19)
changeset 15 2ac5704e229f
parent 13 6fd9177eb6da
child 16 c409dbc4f068
permissions -rw-r--r--
Added tag v0.3 for changeset 3af1d1c0ceb5
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@12
    13
#if !MYCRYPTO_USE_IPHONE_API
snej@0
    14
snej@0
    15
#import <Security/cssmtype.h>
snej@12
    16
snej@0
    17
snej@13
    18
CSSM_ALGORITHMS CSSMFromCCAlgorithm( CCAlgorithm ccAlgorithm ) {
snej@13
    19
    static const CSSM_ALGORITHMS kCSSMAlgorithms[] = {
snej@13
    20
        CSSM_ALGID_AES, CSSM_ALGID_DES, CSSM_ALGID_3DES_3KEY, CSSM_ALGID_CAST, CSSM_ALGID_RC4
snej@13
    21
    };
snej@13
    22
    if (ccAlgorithm >=0 && ccAlgorithm <= kCCAlgorithmRC4)
snej@13
    23
        return kCSSMAlgorithms[ccAlgorithm];
snej@13
    24
    else
snej@13
    25
        return CSSM_ALGID_NONE;
snej@13
    26
}
snej@0
    27
snej@2
    28
static const char *kCCAlgorithmNames[] = {"AES", "DES", "DES^3", "CAST", "RC4"};
snej@2
    29
snej@0
    30
snej@12
    31
/** Undocumented Security function. Unfortunately this is the only way I can find to create
snej@12
    32
    a SecKeyRef from a CSSM_KEY. */
snej@12
    33
extern OSStatus SecKeyCreate(const CSSM_KEY *key, SecKeyRef* keyRef) WEAK_IMPORT_ATTRIBUTE;
snej@12
    34
snej@12
    35
static CSSM_KEY* cssmKeyFromData( NSData *keyData, CSSM_ALGORITHMS algorithm,
snej@12
    36
                                 MYKeychain *keychain);
snej@13
    37
//static CSSM_ENCRYPT_MODE defaultModeForAlgorithm(CSSM_ALGORITHMS algorithm);
snej@13
    38
//CSSM_PADDING defaultPaddingForAlgorithm(CSSM_ALGORITHMS algorithm);
snej@12
    39
static CSSM_DATA makeSalt( id salty, size_t length );
snej@12
    40
static CSSM_RETURN impExpCreatePassKey(
snej@12
    41
	const SecKeyImportExportParameters *keyParams,  // required
snej@12
    42
	CSSM_CSP_HANDLE		cspHand,		// MUST be CSPDL
snej@12
    43
	BOOL                verifyPhrase,   // use 2nd passphrase textfield for verification?
snej@12
    44
	CSSM_KEY_PTR		*passKey);	// mallocd and RETURNED
snej@12
    45
snej@12
    46
snej@0
    47
#pragma mark -
snej@0
    48
@implementation MYSymmetricKey
snej@0
    49
snej@0
    50
snej@12
    51
- (id) _initWithCSSMKey: (CSSM_KEY*)cssmKey {
snej@12
    52
    SecKeyRef keyRef = NULL;
snej@12
    53
    if (SecKeyCreate == NULL) {
snej@12
    54
        // If weak-linked SPI fn no longer exists
snej@12
    55
        Warn(@"Unable to call SecKeyCreate SPI -- not available");
snej@12
    56
        [self release];
snej@12
    57
        return nil;
snej@12
    58
    }
snej@12
    59
    if (!check(SecKeyCreate(cssmKey,&keyRef), @"SecKeyCreate")) {
snej@12
    60
        [self release];
snej@12
    61
        return nil;
snej@12
    62
    }
snej@12
    63
    self = [self initWithKeyRef: keyRef];
snej@12
    64
    if (self) {
snej@12
    65
        _ownedCSSMKey = cssmKey;            // (so I'll remember to free it)
snej@12
    66
    }
snej@12
    67
    return self;
snej@12
    68
}
snej@12
    69
snej@12
    70
snej@1
    71
- (id) _initWithKeyData: (NSData*)keyData
snej@1
    72
              algorithm: (CCAlgorithm)algorithm
snej@1
    73
             inKeychain: (MYKeychain*)keychain
snej@1
    74
{
snej@1
    75
    Assert(algorithm <= kCCAlgorithmRC4);
snej@1
    76
    Assert(keyData);
snej@13
    77
    CSSM_KEY *key = cssmKeyFromData(keyData, CSSMFromCCAlgorithm(algorithm), keychain);
snej@12
    78
    if (!key) {
snej@1
    79
        [self release];
snej@1
    80
        return nil;
snej@1
    81
    }
snej@12
    82
    return [self _initWithCSSMKey: key];
snej@1
    83
}
snej@1
    84
snej@1
    85
- (id) initWithKeyData: (NSData*)keyData
snej@1
    86
             algorithm: (CCAlgorithm)algorithm
snej@1
    87
{
snej@1
    88
    return [self _initWithKeyData: keyData algorithm: algorithm inKeychain: nil];
snej@1
    89
}
snej@1
    90
snej@0
    91
+ (MYSymmetricKey*) _generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0
    92
                                      algorithm: (CCAlgorithm)algorithm
snej@0
    93
                                     inKeychain: (MYKeychain*)keychain
snej@0
    94
{
snej@0
    95
    Assert(algorithm <= kCCAlgorithmRC4);
snej@0
    96
    CSSM_KEYATTR_FLAGS flags = CSSM_KEYATTR_EXTRACTABLE;
snej@0
    97
    if (keychain)
snej@13
    98
        flags |= CSSM_KEYATTR_PERMANENT; // | CSSM_KEYATTR_SENSITIVE;   //FIX: Re-enable this bit
snej@13
    99
    else {
snej@13
   100
        flags |= CSSM_KEYATTR_RETURN_REF;
snej@13
   101
        keychain = [MYKeychain defaultKeychain]; // establish a context for the key
snej@13
   102
    }
snej@0
   103
    CSSM_KEYUSE usage = CSSM_KEYUSE_ANY;
snej@1
   104
    SecKeyRef keyRef = NULL;
snej@13
   105
    if (!check(SecKeyGenerate(keychain.keychainRefOrDefault,
snej@13
   106
                              CSSMFromCCAlgorithm(algorithm),
snej@0
   107
                              keySizeInBits, 
snej@0
   108
                              0, usage, flags, NULL, &keyRef),
snej@0
   109
               @"SecKeyGenerate")) {
snej@0
   110
        return nil;
snej@0
   111
    }
snej@1
   112
    return [[[self alloc] initWithKeyRef: keyRef] autorelease];
snej@0
   113
}
snej@0
   114
snej@0
   115
+ (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0
   116
                                     algorithm: (CCAlgorithm)algorithm {
snej@0
   117
    return [self _generateSymmetricKeyOfSize: keySizeInBits
snej@0
   118
                                   algorithm: algorithm
snej@0
   119
                                  inKeychain: nil];
snej@0
   120
}
snej@0
   121
snej@12
   122
+ (NSString*) promptForPassphraseWithAlertTitle: (NSString*)alertTitle
snej@12
   123
                                    alertPrompt: (NSString*)prompt
snej@12
   124
                                        creating: (BOOL)creating
snej@12
   125
{
snej@12
   126
    // Ask the user for a passphrase:
snej@12
   127
    SecKeyImportExportParameters params = {
snej@12
   128
        .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
snej@12
   129
        .flags = kSecKeySecurePassphrase,
snej@12
   130
        .alertTitle = (CFStringRef)alertTitle,
snej@12
   131
        .alertPrompt = (CFStringRef)prompt,
snej@12
   132
        .keyUsage = CSSM_KEYUSE_ANY,
snej@12
   133
        .keyAttributes = CSSM_KEYATTR_EXTRACTABLE
snej@12
   134
    };
snej@12
   135
    CSSM_CSP_HANDLE cspHandle = [[MYKeychain defaultKeychain] CSPHandle];
snej@12
   136
    CSSM_KEY *passphraseKey = NULL;
snej@12
   137
    if (impExpCreatePassKey(&params, 
snej@12
   138
                            cspHandle, 
snej@12
   139
                            creating,
snej@12
   140
                            &passphraseKey) != CSSM_OK)
snej@12
   141
        return nil;
snej@12
   142
    
snej@12
   143
    MYSymmetricKey *key = [[self alloc] _initWithCSSMKey: passphraseKey];
snej@12
   144
    NSData *keyData = key.keyData;
snej@12
   145
    Assert(keyData);
snej@12
   146
    NSString *passphrase = [[NSString alloc] initWithData: keyData
snej@12
   147
                                                 encoding: NSUTF8StringEncoding];
snej@12
   148
    [key release];
snej@12
   149
    return [passphrase autorelease];
snej@12
   150
}
snej@12
   151
snej@12
   152
snej@12
   153
#define PKCS5_V2_SALT_LEN		8
snej@12
   154
#define PKCS5_V2_ITERATIONS		2048
snej@12
   155
#define PKCS5_V2_DES_IV_SIZE	8
snej@12
   156
snej@12
   157
+ (MYSymmetricKey*) generateFromUserPassphraseWithAlertTitle: (NSString*)alertTitle
snej@12
   158
                                                 alertPrompt: (NSString*)prompt
snej@12
   159
                                                     creating: (BOOL)creating
snej@12
   160
                                                        salt: (id)saltObj
snej@12
   161
{
snej@12
   162
    MYSymmetricKey *generatedKey = nil;
snej@12
   163
snej@12
   164
    // Ask the user for a passphrase:
snej@12
   165
    SecKeyImportExportParameters params = {
snej@12
   166
        .version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION,
snej@12
   167
        .flags = kSecKeySecurePassphrase,
snej@12
   168
        .alertTitle = (CFStringRef)alertTitle,
snej@12
   169
        .alertPrompt = (CFStringRef)prompt,
snej@12
   170
        .keyUsage = CSSM_KEYUSE_ANY,
snej@12
   171
        .keyAttributes = CSSM_KEYATTR_EXTRACTABLE | CSSM_KEYATTR_SENSITIVE
snej@12
   172
    };
snej@12
   173
    CSSM_CSP_HANDLE cspHandle = [[MYKeychain defaultKeychain] CSPHandle];
snej@12
   174
    CSSM_KEY *passphraseKey = NULL;
snej@12
   175
    if (impExpCreatePassKey(&params, 
snej@12
   176
                            cspHandle, 
snej@12
   177
                            creating,
snej@12
   178
                            &passphraseKey) != CSSM_OK)
snej@12
   179
        return nil;
snej@12
   180
snej@12
   181
    CSSM_DATA saltData = makeSalt(saltObj,PKCS5_V2_SALT_LEN);
snej@12
   182
    CSSM_CRYPTO_DATA seed = {};
snej@12
   183
    
snej@12
   184
    // Now use the secure passphrase to generate a symmetric key:
snej@12
   185
    CSSM_CC_HANDLE ctx = 0;
snej@12
   186
    CSSM_ACCESS_CREDENTIALS credentials = {};
snej@12
   187
    if (checkcssm(CSSM_CSP_CreateDeriveKeyContext(cspHandle,
snej@12
   188
                                                  CSSM_ALGID_PKCS5_PBKDF2,
snej@12
   189
                                                  CSSM_ALGID_AES, 128,
snej@12
   190
                                                  &credentials,
snej@12
   191
                                                  passphraseKey, 
snej@12
   192
                                                  PKCS5_V2_ITERATIONS,
snej@12
   193
                                                  &saltData,
snej@12
   194
                                                  &seed,
snej@12
   195
                                                  &ctx),
snej@12
   196
                  @"CSSM_CSP_CreateDeriveKeyContext")) {
snej@12
   197
        CSSM_PKCS5_PBKDF2_PARAMS params = {.PseudoRandomFunction=CSSM_PKCS5_PBKDF2_PRF_HMAC_SHA1};
snej@12
   198
        CSSM_DATA paramData = {.Data=(void*)&params, .Length=sizeof(params)};
snej@12
   199
        CSSM_KEY *cssmKey = calloc(1,sizeof(CSSM_KEY));
snej@12
   200
        if (checkcssm(CSSM_DeriveKey(ctx,
snej@12
   201
                                     &paramData,
snej@12
   202
                                     CSSM_KEYUSE_ANY,
snej@12
   203
                                     CSSM_KEYATTR_EXTRACTABLE, //| CSSM_KEYATTR_SENSITIVE, 
snej@12
   204
                                     NULL, 
snej@12
   205
                                     NULL, 
snej@12
   206
                                     cssmKey),
snej@12
   207
                      @"CSSM_DeriveKey")) {
snej@12
   208
            generatedKey = [[[self alloc] _initWithCSSMKey: cssmKey] autorelease];
snej@12
   209
        }
snej@12
   210
    }
snej@12
   211
    CSSM_DeleteContext(ctx);
snej@12
   212
    CSSM_FreeKey(cspHandle, &credentials, passphraseKey, YES);
snej@12
   213
    return generatedKey;        
snej@12
   214
}
snej@12
   215
snej@12
   216
snej@12
   217
- (void) dealloc
snej@12
   218
{
snej@12
   219
    if(_ownedCSSMKey) 
snej@12
   220
        CSSM_FreeKey(self.cssmCSPHandle, NULL, _ownedCSSMKey, YES);
snej@12
   221
    [super dealloc];
snej@12
   222
}
snej@12
   223
snej@0
   224
snej@2
   225
#if !TARGET_OS_IPHONE
snej@13
   226
- (NSData*) exportWrappedKeyWithPassphrasePrompt: (NSString*)prompt
snej@2
   227
{
snej@12
   228
    // Prompt use for a passphrase to use for the wrapping key:
snej@13
   229
    MYSymmetricKey *wrappingKey = [MYSymmetricKey 
snej@13
   230
                                   generateFromUserPassphraseWithAlertTitle: @"Export Key" 
snej@13
   231
                                   alertPrompt: prompt 
snej@13
   232
                                   creating: YES
snej@13
   233
                                   salt: [MYCryptor randomKeyOfLength: PKCS5_V2_SALT_LEN*8]];
snej@12
   234
    if (!wrappingKey)
snej@12
   235
        return nil;
snej@13
   236
    Log(@"Wrapping using %@",wrappingKey);
snej@12
   237
    
snej@12
   238
    // Create the context:
snej@13
   239
    CSSM_ACCESS_CREDENTIALS credentials = {};
snej@12
   240
    CSSM_CSP_HANDLE cspHandle = self.cssmCSPHandle;
snej@13
   241
    //CSSM_ALGORITHMS algorithm = wrappingKey.cssmAlgorithm;
snej@12
   242
    CSSM_CC_HANDLE ctx;
snej@12
   243
    if (!checkcssm(CSSM_CSP_CreateSymmetricContext(cspHandle,
snej@13
   244
                                                   wrappingKey.cssmAlgorithm, //CSSM_ALGID_3DES_3KEY_EDE, //algorithm, 
snej@13
   245
                                                   CSSM_ALGMODE_CBCPadIV8, //defaultModeForAlgorithm(algorithm),
snej@13
   246
                                                   &credentials, 
snej@12
   247
                                                   wrappingKey.cssmKey,
snej@12
   248
                                                   NULL,
snej@13
   249
                                                   CSSM_PADDING_PKCS7, //defaultPaddingForAlgorithm(algorithm),
snej@12
   250
                                                   NULL,
snej@12
   251
                                                   &ctx), 
snej@12
   252
                   @"CSSM_CSP_CreateSymmetricContext"))
snej@12
   253
        return nil;
snej@12
   254
    
snej@13
   255
    // Now wrap the key:
snej@12
   256
    NSData *result = nil;
snej@13
   257
    CSSM_WRAP_KEY wrappedKey = {};
snej@13
   258
    if (checkcssm(CSSM_WrapKey(ctx, &credentials, self.cssmKey, NULL, &wrappedKey),
snej@13
   259
                  @"CSSM_WrapKey")) {
snej@13
   260
        // ...and copy the wrapped key data to the result NSData:
snej@13
   261
        result = [NSData dataWithBytes: wrappedKey.KeyData.Data length: wrappedKey.KeyData.Length];
snej@13
   262
        CSSM_FreeKey(cspHandle, &credentials, &wrappedKey, NO);
snej@12
   263
    }
snej@12
   264
    // Finally, delete the context
snej@12
   265
    CSSM_DeleteContext(ctx);
snej@12
   266
    return result;
snej@2
   267
}
snej@2
   268
#endif
snej@2
   269
snej@0
   270
snej@0
   271
- (SecExternalItemType) keyType {
snej@0
   272
    return kSecItemTypeSessionKey;
snej@0
   273
}
snej@0
   274
snej@0
   275
- (CCAlgorithm) algorithm {
snej@0
   276
    CSSM_ALGORITHMS cssmAlg;
snej@0
   277
    cssmAlg = self.cssmKey->KeyHeader.AlgorithmId;
snej@0
   278
    switch(cssmAlg) {
snej@0
   279
        case CSSM_ALGID_AES:
snej@0
   280
            return kCCAlgorithmAES128;
snej@0
   281
        case CSSM_ALGID_DES:
snej@0
   282
            return kCCAlgorithmDES;	
snej@0
   283
        case CSSM_ALGID_3DES_3KEY:
snej@0
   284
            return kCCAlgorithm3DES;
snej@0
   285
        case CSSM_ALGID_CAST:
snej@0
   286
            return kCCAlgorithmCAST;
snej@0
   287
        case CSSM_ALGID_RC4:
snej@0
   288
            return kCCAlgorithmRC4;	
snej@0
   289
        default:
snej@0
   290
            Warn(@"CSSM_ALGORITHMS #%u doesn't map to any CCAlgorithm", cssmAlg);
snej@0
   291
            return (CCAlgorithm)-1;
snej@0
   292
    }
snej@0
   293
}
snej@0
   294
snej@2
   295
- (const char*) algorithmName {
snej@2
   296
    CCAlgorithm a = self.algorithm;
snej@2
   297
    if (a >= 0 && a <= kCCAlgorithmRC4)
snej@2
   298
        return kCCAlgorithmNames[a];
snej@2
   299
    else
snej@2
   300
        return "???";
snej@2
   301
}
snej@2
   302
snej@2
   303
- (unsigned) keySizeInBits {
snej@2
   304
    const CSSM_KEY *key = self.cssmKey;
snej@2
   305
    Assert(key);
snej@2
   306
    return key->KeyHeader.LogicalKeySizeInBits;
snej@2
   307
}
snej@2
   308
snej@2
   309
snej@2
   310
- (NSString*) description {
snej@2
   311
    return $sprintf(@"%@[%u-bit %s]", [self class], self.keySizeInBits, self.algorithmName);
snej@2
   312
}
snej@2
   313
snej@0
   314
snej@0
   315
- (NSData*) _cryptData: (NSData*)data operation: (CCOperation)op options: (CCOptions)options
snej@0
   316
{
snej@0
   317
    NSData *keyData = self.keyData;
snej@0
   318
    Assert(keyData, @"Couldn't get key data");
snej@0
   319
    NSMutableData *output = [NSMutableData dataWithLength: data.length + 256];
snej@0
   320
    size_t bytesWritten = 0;
snej@0
   321
    CCCryptorStatus status = CCCrypt(op, self.algorithm, options, 
snej@0
   322
                                     keyData.bytes, keyData.length, NULL,
snej@0
   323
                                     data.bytes, data.length, output.mutableBytes, output.length,
snej@0
   324
                                     &bytesWritten);
snej@0
   325
    if (status) {
snej@0
   326
        Warn(@"MYSymmetricKey: CCCrypt returned error %i",status);
snej@0
   327
        return nil;
snej@0
   328
    }
snej@0
   329
    output.length = bytesWritten;
snej@0
   330
    return output;
snej@0
   331
}
snej@0
   332
snej@0
   333
- (NSData*) encryptData: (NSData*)data {
snej@0
   334
    return [self _cryptData: data operation: kCCEncrypt options: kCCOptionPKCS7Padding];
snej@0
   335
}
snej@0
   336
snej@0
   337
snej@0
   338
- (NSData*) decryptData: (NSData*)data {
snej@0
   339
    return [self _cryptData: data operation: kCCDecrypt options: kCCOptionPKCS7Padding];
snej@0
   340
}
snej@0
   341
snej@0
   342
snej@0
   343
@end
snej@0
   344
snej@0
   345
snej@12
   346
#pragma mark -
snej@12
   347
snej@12
   348
snej@12
   349
static CSSM_KEY* cssmKeyFromData( NSData *keyData, 
snej@12
   350
                                 CSSM_ALGORITHMS algorithm,
snej@12
   351
                                 MYKeychain *keychain ) {
snej@12
   352
    // Thanks to Jim Murphy for showing the way!
snej@12
   353
    if (!keychain) keychain = [MYKeychain defaultKeychain];
snej@0
   354
    CSSM_CC_HANDLE ccHandle;
snej@12
   355
    if (!checkcssm(CSSM_CSP_CreateSymmetricContext([keychain CSPHandle],
snej@12
   356
                                                   CSSM_ALGID_NONE, CSSM_ALGMODE_WRAP,
snej@12
   357
                                                   NULL, NULL, NULL,
snej@12
   358
                                                   CSSM_PADDING_NONE, NULL,
snej@12
   359
                                                   &ccHandle), 
snej@12
   360
                   @"CSSM_CSP_CreateSymmetricContext"))
snej@12
   361
        return NO;
snej@0
   362
    
snej@12
   363
    CSSM_KEY wrappedKey = {
snej@12
   364
        .KeyHeader = {
snej@12
   365
            .BlobType = CSSM_KEYBLOB_RAW,
snej@12
   366
            .Format = CSSM_KEYBLOB_RAW_FORMAT_NONE,
snej@12
   367
            .AlgorithmId = algorithm,
snej@12
   368
            .KeyClass = CSSM_KEYCLASS_SESSION_KEY,
snej@12
   369
            .LogicalKeySizeInBits = keyData.length*8,
snej@12
   370
            .KeyAttr = CSSM_KEYATTR_EXTRACTABLE,
snej@12
   371
            .KeyUsage = CSSM_KEYUSE_ANY
snej@12
   372
        },
snej@12
   373
        .KeyData = {
snej@12
   374
            .Data = (void*)keyData.bytes,
snej@12
   375
            .Length = keyData.length
snej@12
   376
        }
snej@12
   377
    };
snej@12
   378
    
snej@12
   379
    CSSM_KEY *outKey = calloc(sizeof(CSSM_KEY),1);
snej@12
   380
    CSSM_DATA desc = {};
snej@12
   381
    if (!checkcssm(CSSM_UnwrapKey(ccHandle,
snej@12
   382
                                  NULL,
snej@12
   383
                                  &wrappedKey,
snej@12
   384
                                  CSSM_KEYUSE_ANY,
snej@12
   385
                                  CSSM_KEYATTR_EXTRACTABLE,
snej@12
   386
                                  NULL,
snej@12
   387
                                  NULL,
snej@12
   388
                                  outKey,
snej@12
   389
                                  &desc),
snej@12
   390
                   @"CSSM_UnwrapKey")) {
snej@12
   391
        free(outKey);
snej@12
   392
        outKey = NULL;
snej@0
   393
    }
snej@0
   394
    CSSM_DeleteContext(ccHandle);
snej@12
   395
    return outKey;
snej@0
   396
}
snej@12
   397
snej@12
   398
snej@12
   399
// Create salt data of a specific length from an arbitrary NSObject. */
snej@12
   400
static CSSM_DATA makeSalt( id salty, size_t length ) {
snej@12
   401
    // Convert to NSData if necessary:
snej@12
   402
    CAssert(salty!=nil);
snej@12
   403
    if (![salty isKindOfClass: [NSData class]])
snej@12
   404
        salty = [[salty description] dataUsingEncoding: NSUTF8StringEncoding];
snej@12
   405
    // Repeat enough times to fill the desired length:
snej@12
   406
    NSMutableData *salt = [[salty mutableCopy] autorelease];
snej@12
   407
    CAssert(salt.length>0);
snej@12
   408
    while (salt.length < length) {
snej@12
   409
        [salt appendData: salt];
snej@12
   410
    }
snej@12
   411
    // Truncate to length and return it:
snej@12
   412
    salt.length = length;
snej@12
   413
    return (CSSM_DATA){.Data=(void*)salt.bytes, .Length=salt.length};
snej@12
   414
}
snej@12
   415
snej@12
   416
snej@13
   417
#pragma mark -
snej@13
   418
// Code from Keychain.framework:
snej@13
   419
#if 0
snej@13
   420
static CSSM_ENCRYPT_MODE defaultModeForAlgorithm(CSSM_ALGORITHMS algorithm) {
snej@13
   421
    switch(algorithm) {
snej@13
   422
        // 8-byte block ciphers
snej@13
   423
        case CSSM_ALGID_DES:
snej@13
   424
        case CSSM_ALGID_3DES_3KEY_EDE:
snej@13
   425
        case CSSM_ALGID_RC5:
snej@13
   426
        case CSSM_ALGID_RC2:
snej@13
   427
            return CSSM_ALGMODE_CBCPadIV8; break;
snej@13
   428
        // 16-byte block ciphers
snej@13
   429
        case CSSM_ALGID_AES:
snej@13
   430
            return CSSM_ALGMODE_CBCPadIV8; break;
snej@13
   431
        // stream ciphers
snej@13
   432
        case CSSM_ALGID_ASC:
snej@13
   433
        case CSSM_ALGID_RC4:
snej@13
   434
            return CSSM_ALGMODE_NONE; break;
snej@13
   435
        // Unknown
snej@13
   436
        default:
snej@13
   437
        	Warn(@"Asked for the default mode for algorithm %d, but don't know that algorithm.\n", algorithm);
snej@13
   438
            return CSSM_ALGMODE_NONE;
snej@13
   439
    }
snej@13
   440
}
snej@13
   441
snej@13
   442
CSSM_PADDING defaultPaddingForAlgorithm(CSSM_ALGORITHMS algorithm) {
snej@13
   443
    switch(algorithm) {
snej@13
   444
        /* 8-byte block ciphers */
snej@13
   445
        case CSSM_ALGID_DES:
snej@13
   446
        case CSSM_ALGID_3DES_3KEY_EDE:
snej@13
   447
        case CSSM_ALGID_RC5:
snej@13
   448
        case CSSM_ALGID_RC2:
snej@13
   449
            return CSSM_PADDING_PKCS5; break;
snej@13
   450
            /* 16-byte block ciphers */
snej@13
   451
        case CSSM_ALGID_AES:
snej@13
   452
            return CSSM_PADDING_PKCS7; break;
snej@13
   453
            /* stream ciphers */
snej@13
   454
        case CSSM_ALGID_ASC:
snej@13
   455
        case CSSM_ALGID_RC4:
snej@13
   456
            return CSSM_PADDING_NONE; break;
snej@13
   457
            /* RSA/DSA asymmetric */
snej@13
   458
        case CSSM_ALGID_DSA:
snej@13
   459
        case CSSM_ALGID_RSA:
snej@13
   460
            return CSSM_PADDING_PKCS1; break;
snej@13
   461
            /* Unknown */
snej@13
   462
        default:
snej@13
   463
        	Warn(@"Asked for the default padding mode for %d, but don't know that algorithm.\n", algorithm);
snej@13
   464
            return CSSM_PADDING_NONE;
snej@13
   465
    }
snej@13
   466
}
snej@13
   467
#endif
snej@13
   468
snej@13
   469
#pragma mark -
snej@13
   470
// Code below was copied from SecImportExportUtils.cpp in Apple's libsecurity_keychain project
snej@13
   471
snej@13
   472
snej@12
   473
/*
snej@12
   474
 * Given a context specified via a CSSM_CC_HANDLE, add a new
snej@12
   475
 * CSSM_CONTEXT_ATTRIBUTE to the context as specified by AttributeType,
snej@12
   476
 * AttributeLength, and an untyped pointer.
snej@12
   477
 */
snej@12
   478
static CSSM_RETURN impExpAddContextAttribute(CSSM_CC_HANDLE CCHandle,
snej@12
   479
	uint32 AttributeType,
snej@12
   480
	uint32 AttributeLength,
snej@12
   481
	const void *AttributePtr)
snej@12
   482
{
snej@12
   483
	CSSM_CONTEXT_ATTRIBUTE		newAttr;	
snej@12
   484
	
snej@12
   485
	newAttr.AttributeType     = AttributeType;
snej@12
   486
	newAttr.AttributeLength   = AttributeLength;
snej@12
   487
	newAttr.Attribute.Data    = (CSSM_DATA_PTR)AttributePtr;
snej@12
   488
	return CSSM_UpdateContextAttributes(CCHandle, 1, &newAttr);
snej@12
   489
}
snej@12
   490
snej@12
   491
/* 
snej@12
   492
* Add a CFString to a crypto context handle. 
snej@0
   493
*/
snej@12
   494
static CSSM_RETURN impExpAddStringAttr(
snej@12
   495
	CSSM_CC_HANDLE ccHand, 
snej@12
   496
	CFStringRef str,
snej@12
   497
	CSSM_ATTRIBUTE_TYPE attrType)
snej@12
   498
{
snej@12
   499
	/* CFStrings are passed as external rep in UTF8 encoding by convention */
snej@12
   500
	CFDataRef outData;
snej@12
   501
	outData = CFStringCreateExternalRepresentation(NULL,
snej@12
   502
		str, kCFStringEncodingUTF8,	0);		// lossByte 0 ==> no loss allowed 
snej@12
   503
	if(outData == NULL) {
snej@12
   504
		Warn(@"impExpAddStringAttr: bad string format");
snej@12
   505
		return paramErr;
snej@12
   506
	}
snej@12
   507
	
snej@12
   508
	CSSM_DATA attrData;
snej@12
   509
	attrData.Data = (uint8 *)CFDataGetBytePtr(outData);
snej@12
   510
	attrData.Length = CFDataGetLength(outData);
snej@12
   511
	CSSM_RETURN crtn = impExpAddContextAttribute(ccHand, attrType, sizeof(CSSM_DATA),
snej@12
   512
		&attrData);
snej@12
   513
	CFRelease(outData);
snej@12
   514
	if(crtn) {
snej@12
   515
		Warn(@"impExpAddStringAttr: CSSM_UpdateContextAttributes error");
snej@12
   516
	}
snej@12
   517
	return crtn;
snej@12
   518
}
snej@12
   519
snej@12
   520
/*
snej@12
   521
 * Generate a secure passphrase key. Caller must eventually CSSM_FreeKey the result. 
snej@12
   522
 */
snej@12
   523
static CSSM_RETURN impExpCreatePassKey(
snej@12
   524
	const SecKeyImportExportParameters *keyParams,  // required
snej@12
   525
	CSSM_CSP_HANDLE		cspHand,		// MUST be CSPDL
snej@12
   526
	BOOL                verifyPhrase,   // use 2nd passphrase textfield for verification?
snej@12
   527
	CSSM_KEY_PTR		*passKey)		// mallocd and RETURNED
snej@12
   528
{
snej@12
   529
	CSSM_RETURN crtn;
snej@12
   530
	CSSM_CC_HANDLE ccHand;
snej@12
   531
	uint32 verifyAttr;
snej@12
   532
	CSSM_DATA dummyLabel;
snej@12
   533
	CSSM_KEY_PTR ourKey = NULL;
snej@12
   534
	
snej@12
   535
	Log(@"Generating secure passphrase key");
snej@12
   536
	ourKey = (CSSM_KEY_PTR)malloc(sizeof(CSSM_KEY));
snej@12
   537
	if(ourKey == NULL) {
snej@12
   538
		return memFullErr;
snej@12
   539
	}
snej@12
   540
	memset(ourKey, 0, sizeof(CSSM_KEY));
snej@12
   541
	
snej@12
   542
	crtn = CSSM_CSP_CreateKeyGenContext(cspHand,
snej@12
   543
		CSSM_ALGID_SECURE_PASSPHRASE,
snej@12
   544
		4,				// keySizeInBits must be non zero
snej@12
   545
		NULL,			// Seed
snej@12
   546
		NULL,			// Salt
snej@12
   547
		NULL,			// StartDate
snej@12
   548
		NULL,			// EndDate
snej@12
   549
		NULL,			// Params
snej@12
   550
		&ccHand);
snej@12
   551
	if(crtn) {
snej@12
   552
		checkcssm(crtn,@"CSSM_CSP_CreateKeyGenContext");
snej@12
   553
		return crtn;
snej@12
   554
	}
snej@12
   555
	/* subsequent errors to errOut: */
snej@12
   556
	
snej@12
   557
	/* additional context attributes specific to this type of key gen */
snej@12
   558
	CAssert(keyParams != NULL);			// or we wouldn't be here
snej@12
   559
	if(keyParams->alertTitle != NULL) {
snej@12
   560
		crtn = impExpAddStringAttr(ccHand, keyParams->alertTitle, 
snej@12
   561
			CSSM_ATTRIBUTE_ALERT_TITLE);
snej@12
   562
		if(crtn) {
snej@12
   563
			goto errOut;
snej@12
   564
		}
snej@12
   565
	}
snej@12
   566
	if(keyParams->alertPrompt != NULL) {
snej@12
   567
		crtn = impExpAddStringAttr(ccHand, keyParams->alertPrompt, 
snej@12
   568
			CSSM_ATTRIBUTE_PROMPT);
snej@12
   569
		if(crtn) {
snej@12
   570
			goto errOut;
snej@12
   571
		}
snej@12
   572
	}
snej@12
   573
	verifyAttr = verifyPhrase ? 1 : 0;
snej@12
   574
	crtn = impExpAddContextAttribute(ccHand, CSSM_ATTRIBUTE_VERIFY_PASSPHRASE,
snej@12
   575
		sizeof(uint32), (const void *)verifyAttr);
snej@12
   576
	if(crtn) {
snej@12
   577
		checkcssm(crtn,@"impExpAddContextAttribute");
snej@12
   578
		goto errOut;
snej@12
   579
	}
snej@12
   580
snej@12
   581
	dummyLabel.Data = (uint8 *)"Secure Passphrase";
snej@12
   582
	dummyLabel.Length = strlen((char *)dummyLabel.Data);
snej@12
   583
    
snej@12
   584
    uint32 keyAttr = keyParams->keyAttributes;
snej@12
   585
    if (keyAttr & CSSM_KEYATTR_SENSITIVE)
snej@12
   586
        keyAttr |= CSSM_KEYATTR_RETURN_REF;
snej@12
   587
    else
snej@12
   588
        keyAttr |= CSSM_KEYATTR_EXTRACTABLE;
snej@12
   589
snej@12
   590
	crtn = CSSM_GenerateKey(ccHand,
snej@12
   591
        keyParams->keyUsage ?: CSSM_KEYUSE_ANY,
snej@12
   592
		keyAttr,
snej@12
   593
		&dummyLabel,
snej@12
   594
		NULL,			// ACL
snej@12
   595
		ourKey);
snej@12
   596
	if(crtn) {
snej@12
   597
		checkcssm(crtn,@"CSSM_GenerateKey");
snej@12
   598
	}
snej@12
   599
errOut:
snej@12
   600
	CSSM_DeleteContext(ccHand);
snej@12
   601
	if(crtn == CSSM_OK) {
snej@12
   602
		*passKey = ourKey;
snej@12
   603
	}
snej@12
   604
	else if(ourKey != NULL) {
snej@12
   605
		free(ourKey);
snej@12
   606
	}
snej@12
   607
	return crtn;
snej@12
   608
}
snej@12
   609
	
snej@12
   610
snej@12
   611
#endif !MYCRYPTO_USE_IPHONE_API
snej@14
   612
snej@14
   613
snej@14
   614
snej@14
   615
/*
snej@14
   616
 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
snej@14
   617
 
snej@14
   618
 Redistribution and use in source and binary forms, with or without modification, are permitted
snej@14
   619
 provided that the following conditions are met:
snej@14
   620
 
snej@14
   621
 * Redistributions of source code must retain the above copyright notice, this list of conditions
snej@14
   622
 and the following disclaimer.
snej@14
   623
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
snej@14
   624
 and the following disclaimer in the documentation and/or other materials provided with the
snej@14
   625
 distribution.
snej@14
   626
 
snej@14
   627
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
snej@14
   628
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
snej@14
   629
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
snej@14
   630
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
snej@14
   631
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
snej@14
   632
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
snej@14
   633
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
snej@14
   634
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
snej@14
   635
 */