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