MYKey.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
//  MYKey.m
snej@0
     3
//  MYCrypto
snej@0
     4
//
snej@0
     5
//  Created by Jens Alfke on 3/21/09.
snej@0
     6
//  Copyright 2009 Jens Alfke. All rights reserved.
snej@0
     7
//
snej@0
     8
snej@0
     9
#import "MYKey.h"
snej@0
    10
#import "MYCrypto_Private.h"
snej@0
    11
#import "MYDigest.h"
snej@0
    12
#import "MYErrorUtils.h"
snej@0
    13
snej@2
    14
#if !MYCRYPTO_USE_IPHONE_API
snej@0
    15
snej@0
    16
snej@0
    17
#pragma mark -
snej@0
    18
@implementation MYKey
snej@0
    19
snej@0
    20
snej@0
    21
- (id) initWithKeyRef: (SecKeyRef)key {
snej@1
    22
    return [super initWithKeychainItemRef: (SecKeychainItemRef)key];
snej@0
    23
}
snej@0
    24
snej@0
    25
- (id) _initWithKeyData: (NSData*)data
snej@0
    26
            forKeychain: (SecKeychainRef)keychain {
snej@0
    27
    SecKeyImportExportParameters params = {};
snej@0
    28
    SecKeyRef key = importKey(data, self.keyType, keychain, &params);
snej@0
    29
    if (!key) {
snej@0
    30
        [self release];
snej@0
    31
        return nil;
snej@0
    32
    }
snej@1
    33
    self = [self initWithKeyRef: key];
snej@0
    34
    CFRelease(key);
snej@0
    35
    return self;
snej@0
    36
}
snej@0
    37
snej@0
    38
- (id) initWithKeyData: (NSData*)data {
snej@0
    39
    return [self _initWithKeyData: data forKeychain: nil];
snej@0
    40
}
snej@0
    41
snej@0
    42
snej@0
    43
- (SecExternalItemType) keyType {
snej@0
    44
    AssertAbstractMethod();
snej@0
    45
}
snej@0
    46
snej@0
    47
snej@1
    48
- (SecKeyRef) keyRef {
snej@1
    49
    return (SecKeyRef) self.keychainItemRef;
snej@0
    50
}
snej@0
    51
snej@0
    52
- (const CSSM_KEY*) cssmKey {
snej@0
    53
    const CSSM_KEY *cssmKey = NULL;
snej@2
    54
    Assert(check(SecKeyGetCSSMKey(self.keyRef, &cssmKey), @"SecKeyGetCSSMKey"), 
snej@2
    55
           @"Failed to get CSSM_KEY");
snej@0
    56
    return cssmKey;
snej@0
    57
}
snej@0
    58
snej@2
    59
- (const CSSM_CSP_HANDLE) cssmCSPHandle {
snej@2
    60
    CSSM_CSP_HANDLE cspHandle = 0;
snej@2
    61
    Assert(check(SecKeyGetCSPHandle(self.keyRef, &cspHandle), @"SecKeyGetCSPHandle"),
snej@2
    62
           @"Failed to get CSSM_CSP_HANDLE");
snej@2
    63
    return cspHandle;
snej@2
    64
}
snej@2
    65
snej@2
    66
- (const CSSM_ACCESS_CREDENTIALS*) cssmCredentialsForOperation: (CSSM_ACL_AUTHORIZATION_TAG)operation
snej@2
    67
                                                          type: (SecCredentialType)type
snej@2
    68
                                                         error: (NSError**)outError
snej@2
    69
{
snej@2
    70
    const CSSM_ACCESS_CREDENTIALS *credentials = NULL;
snej@2
    71
    OSStatus err = SecKeyGetCredentials(self.keyRef,
snej@2
    72
                                        operation,
snej@2
    73
                                        type,
snej@2
    74
                                        &credentials);
snej@2
    75
    if (!MYReturnError(outError, err,NSOSStatusErrorDomain, @"Couldn't get credentials for key"))
snej@2
    76
        return NULL;
snej@2
    77
    return credentials;
snej@2
    78
}
snej@2
    79
snej@0
    80
- (NSData*) exportKeyInFormat: (SecExternalFormat)format withPEM: (BOOL)withPEM {
snej@0
    81
    CFDataRef data = NULL;
snej@1
    82
    if (check(SecKeychainItemExport(self.keyRef, format, (withPEM ?kSecItemPemArmour :0), NULL, &data),
snej@0
    83
              @"SecKeychainItemExport"))
snej@0
    84
        return [(id)CFMakeCollectable(data) autorelease];
snej@0
    85
    else
snej@0
    86
        return nil;
snej@0
    87
}
snej@0
    88
snej@0
    89
- (NSData*) keyData {
snej@0
    90
    return [self exportKeyInFormat: kSecFormatRawKey withPEM: NO];
snej@0
    91
}
snej@0
    92
snej@0
    93
- (NSString*) name {
snej@0
    94
    return [self stringValueOfAttribute: kSecKeyPrintName];
snej@0
    95
}
snej@0
    96
snej@0
    97
- (void) setName: (NSString*)name {
snej@0
    98
    [self setValue: name ofAttribute: kSecKeyPrintName];
snej@0
    99
}
snej@0
   100
snej@0
   101
- (NSString*) comment {
snej@0
   102
    return [self stringValueOfAttribute: kSecKeyApplicationTag];
snej@0
   103
}
snej@0
   104
snej@0
   105
- (void) setComment: (NSString*)comment {
snej@0
   106
    [self setValue: comment ofAttribute: kSecKeyApplicationTag];
snej@0
   107
}
snej@0
   108
snej@0
   109
- (NSString*) alias {
snej@0
   110
    return [self stringValueOfAttribute: kSecKeyAlias];
snej@0
   111
}
snej@0
   112
snej@0
   113
- (void) setAlias: (NSString*)alias {
snej@0
   114
    [self setValue: alias ofAttribute: kSecKeyAlias];
snej@0
   115
}
snej@0
   116
snej@0
   117
snej@0
   118
#pragma mark -
snej@0
   119
#pragma mark UTILITY FUNCTIONS:
snej@0
   120
snej@0
   121
snej@0
   122
SecKeyRef importKey(NSData *data, 
snej@0
   123
                    SecExternalItemType type,
snej@0
   124
                    SecKeychainRef keychain,
snej@0
   125
                    SecKeyImportExportParameters *params) {
snej@0
   126
    SecExternalFormat inputFormat = (type==kSecItemTypeSessionKey) ?kSecFormatRawKey :kSecFormatOpenSSL;
snej@0
   127
    CFArrayRef items = NULL;
snej@0
   128
    
snej@0
   129
    params->version = SEC_KEY_IMPORT_EXPORT_PARAMS_VERSION;
snej@0
   130
    params->flags |= kSecKeyImportOnlyOne;
snej@2
   131
    params->keyAttributes |= CSSM_KEYATTR_EXTRACTABLE;
snej@0
   132
    if (keychain) {
snej@2
   133
        params->keyAttributes |= CSSM_KEYATTR_PERMANENT;
snej@0
   134
        if (type==kSecItemTypeSessionKey)
snej@0
   135
            params->keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_DECRYPT;
snej@0
   136
        else if (type==kSecItemTypePublicKey)
snej@0
   137
            params->keyUsage = CSSM_KEYUSE_ENCRYPT | CSSM_KEYUSE_VERIFY;
snej@0
   138
        else if (type==kSecItemTypePrivateKey)
snej@0
   139
            params->keyUsage = CSSM_KEYUSE_DECRYPT | CSSM_KEYUSE_SIGN;
snej@0
   140
    }
snej@0
   141
    if (!check(SecKeychainItemImport((CFDataRef)data, NULL, &inputFormat, &type,
snej@0
   142
                                     0, params, keychain, &items),
snej@0
   143
               @"SecKeychainItemImport"))
snej@0
   144
        return nil;
snej@0
   145
    if (!items || CFArrayGetCount(items) != 1)
snej@0
   146
        return nil;
snej@0
   147
    SecKeyRef key = (SecKeyRef)CFRetain(CFArrayGetValueAtIndex(items,0));
snej@0
   148
    CFRelease(items);
snej@0
   149
    return key; // caller must CFRelease
snej@3
   150
}
snej@0
   151
snej@0
   152
snej@3
   153
- (MYSHA1Digest*) _keyDigest {
snej@3
   154
    MYSHA1Digest *digest = nil;
snej@3
   155
    CSSM_DATA *keyDigest = NULL;
snej@3
   156
    CSSM_CC_HANDLE context = [self _createPassThroughContext];
snej@3
   157
    if (context) {
snej@3
   158
        if (checkcssm(CSSM_CSP_PassThrough(context, CSSM_APPLECSP_KEYDIGEST, NULL, (void**)&keyDigest),
snej@3
   159
                      @"CSSM_CSP_PassThrough")) {
snej@3
   160
            if (keyDigest && keyDigest->Data) {
snej@3
   161
                digest = [[[MYSHA1Digest alloc] initWithRawDigest: keyDigest->Data
snej@3
   162
                                                           length: keyDigest->Length] autorelease];
snej@3
   163
            }
snej@3
   164
        } else {
snej@3
   165
            SecKeyRef keyRef = self.keyRef;
snej@3
   166
            // Note - CSSM_CSP_PassThrough fails on a couple of private keys I've seen; it seems to
snej@3
   167
            // be ones that are either expired or don't have a matching public key at all (?)
snej@3
   168
            Warn(@"Failed to get digest of SecKeyRef %p (name='%@' appTag='%@')", 
snej@3
   169
                 keyRef,
snej@3
   170
                 self.name,
snej@3
   171
                 self.comment);
snej@3
   172
            NSData *digestData = [[self class] _getAttribute: kSecKeyLabel 
snej@3
   173
                                                      ofItem: (SecKeychainItemRef)keyRef];
snej@3
   174
            if (digestData) {
snej@3
   175
                digest = (MYSHA1Digest*) [MYSHA1Digest digestFromDigestData: digestData];
snej@3
   176
                if (!digest)
snej@3
   177
                    Warn(@"Digest property of key %p was invalid SHA-1: %@", keyRef,digestData);
snej@3
   178
            }
snej@3
   179
        }
snej@3
   180
        CSSM_DeleteContext(context);
snej@3
   181
    }
snej@3
   182
    return digest;
snej@3
   183
}
snej@3
   184
snej@3
   185
snej@3
   186
/** Asymmetric encryption/decryption; used by MYPublicKey and MYPrivateKey. */
snej@3
   187
- (NSData*) _crypt: (NSData*)data operation: (BOOL)operation {
snej@3
   188
    CAssert(data);
snej@3
   189
    const CSSM_ACCESS_CREDENTIALS *credentials;
snej@3
   190
    credentials = [self cssmCredentialsForOperation: (operation ?CSSM_ACL_AUTHORIZATION_ENCRYPT 
snej@3
   191
                                                                :CSSM_ACL_AUTHORIZATION_DECRYPT) 
snej@3
   192
                                               type: kSecCredentialTypeDefault
snej@3
   193
                                              error: nil];
snej@3
   194
    if (!credentials)
snej@3
   195
        return nil;
snej@3
   196
    
snej@3
   197
    CSSM_CC_HANDLE ccHandle;
snej@3
   198
    if (!checkcssm(CSSM_CSP_CreateAsymmetricContext(self.cssmCSPHandle,
snej@3
   199
                                                    CSSM_ALGID_RSA,
snej@3
   200
                                                    credentials,
snej@3
   201
                                                    self.cssmKey,
snej@3
   202
                                                    CSSM_PADDING_PKCS1,
snej@3
   203
                                                    &ccHandle),
snej@3
   204
                   @"CSSM_CSP_CreateAsymmetricContext"))
snej@3
   205
        return nil;
snej@3
   206
    
snej@3
   207
    CSSM_DATA original = {data.length, (void*)data.bytes};
snej@3
   208
    CSSM_DATA result = {};
snej@3
   209
    size_t outputLength;
snej@3
   210
    BOOL ok;
snej@3
   211
    if (operation)
snej@3
   212
        ok = checkcssm(CSSM_EncryptData(ccHandle, &original, 1, &result, 1, &outputLength, &result),
snej@3
   213
                       @"CSSM_EncryptData");
snej@3
   214
    else
snej@3
   215
        ok = checkcssm(CSSM_DecryptData(ccHandle, &original, 1, &result, 1, &outputLength, &result),
snej@3
   216
                       @"CSSM_DecryptData");
snej@3
   217
    CSSM_DeleteContext(ccHandle);
snej@3
   218
    return ok ?[NSData dataWithBytesNoCopy: result.Data length: outputLength freeWhenDone: YES] :nil;
snej@3
   219
}
snej@3
   220
snej@3
   221
snej@3
   222
- (CSSM_CC_HANDLE) _createSignatureContext: (CSSM_ALGORITHMS)algorithm {
snej@3
   223
    const CSSM_ACCESS_CREDENTIALS *credentials;
snej@3
   224
    credentials = [self cssmCredentialsForOperation: CSSM_ACL_AUTHORIZATION_SIGN 
snej@3
   225
                                               type: kSecCredentialTypeDefault
snej@3
   226
                                              error: nil];
snej@3
   227
    if (credentials) {
snej@3
   228
        CSSM_CC_HANDLE ccHandle = 0;
snej@3
   229
        if (checkcssm(CSSM_CSP_CreateSignatureContext(self.cssmCSPHandle, 
snej@3
   230
                                                      algorithm, 
snej@3
   231
                                                      credentials,
snej@3
   232
                                                      self.cssmKey,
snej@3
   233
                                                      &ccHandle),
snej@3
   234
                             @"CSSM_CSP_CreateSignatureContext") )
snej@3
   235
            return ccHandle;
snej@3
   236
    }
snej@3
   237
    return 0;
snej@3
   238
}
snej@3
   239
snej@3
   240
- (CSSM_CC_HANDLE) _createPassThroughContext
snej@3
   241
{
snej@3
   242
    CSSM_CC_HANDLE ccHandle = 0;
snej@3
   243
    if (checkcssm(CSSM_CSP_CreatePassThroughContext(self.cssmCSPHandle, self.cssmKey, &ccHandle), 
snej@3
   244
                          @"CSSM_CSP_CreatePassThroughContext") )
snej@3
   245
        return ccHandle;
snej@3
   246
    else
snej@3
   247
        return 0;
snej@3
   248
}
snej@3
   249
snej@3
   250
snej@3
   251
@end
snej@3
   252
snej@2
   253
#endif MYCRYPTO_USE_IPHONE_API
snej@0
   254
snej@0
   255
snej@0
   256
snej@0
   257
/*
snej@0
   258
 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
snej@0
   259
 
snej@0
   260
 Redistribution and use in source and binary forms, with or without modification, are permitted
snej@0
   261
 provided that the following conditions are met:
snej@0
   262
 
snej@0
   263
 * Redistributions of source code must retain the above copyright notice, this list of conditions
snej@0
   264
 and the following disclaimer.
snej@0
   265
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
snej@0
   266
 and the following disclaimer in the documentation and/or other materials provided with the
snej@0
   267
 distribution.
snej@0
   268
 
snej@0
   269
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
snej@0
   270
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
snej@0
   271
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
snej@0
   272
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
snej@0
   273
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
snej@0
   274
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
snej@0
   275
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
snej@0
   276
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
snej@0
   277
 */