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