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