MYKey-iPhone.m
author Jens Alfke <jens@mooseyard.com>
Wed Jun 10 09:02:18 2009 -0700 (2009-06-10)
changeset 25 38c3c3923e1f
parent 23 39fec79de6e8
child 26 d9c2a06d4e4e
permissions -rw-r--r--
Changed the X.509 version number in generated certs from 1 to 3, so that SecCertificateCreateFromData on iPhone will accept them. :-/
     1 //
     2 //  MYKey-iPhone.m
     3 //  MYCrypto-iPhone
     4 //
     5 //  Created by Jens Alfke on 4/4/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 
    10 #import "MYCrypto_Private.h"
    11 
    12 #if MYCRYPTO_USE_IPHONE_API
    13 
    14 #import "MYDigest.h"
    15 #import "MYErrorUtils.h"
    16 
    17 
    18 #pragma mark -
    19 @implementation MYKey
    20 
    21 
    22 + (SecKeyRef) _addKeyWithInfo: (NSMutableDictionary*)info {
    23     if (![info objectForKey: (id)kSecAttrApplicationTag]) {
    24         // Every keychain item has to have a unique tag, apparently, or you'll get spurious
    25         // duplicate-item errors. If none was given, make up a random one:
    26         UInt8 tag[16];
    27         Assert(check(SecRandomCopyBytes(kSecRandomDefault, sizeof(tag), tag), @"SecRandomCopyBytes"));
    28         [info setObject: [NSData dataWithBytes: tag length: sizeof(tag)] 
    29                  forKey: (id)kSecAttrApplicationTag];
    30     }
    31     CFDataRef keyPersistentRef;
    32     SecKeyRef key;
    33     OSStatus err = SecItemAdd((CFDictionaryRef)info, (CFTypeRef*)&keyPersistentRef);
    34     if (err==errSecDuplicateItem) {
    35         // it's already in the keychain -- get a reference to it:
    36 		[info removeObjectForKey: (id)kSecReturnPersistentRef];
    37 		[info setObject: $true forKey: (id)kSecReturnRef];
    38 		if (check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef *)&key), 
    39                    @"SecItemCopyMatching"))
    40             return key;
    41     } else if (check(err, @"SecItemAdd")) {
    42         // It was added
    43         if ([[info objectForKey: (id)kSecReturnPersistentRef] boolValue]) {
    44             // now get its SecKeyRef:
    45             info = $mdict({(id)kSecValuePersistentRef, (id)keyPersistentRef},
    46                           {(id)kSecReturnRef, $true});
    47             err = SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef *)&key);
    48             CFRelease(keyPersistentRef);
    49             if (check(err,@"SecItemCopyMatching")) {
    50                 Assert(key!=nil);
    51                 return key;
    52             }
    53         } else {
    54             return (SecKeyRef)keyPersistentRef;
    55         }
    56     }
    57     return NULL;
    58 }
    59 
    60 
    61 - (id) initWithKeyRef: (SecKeyRef)key {
    62     return [self initWithKeychainItemRef: (SecKeychainItemRef)key];
    63 }
    64 
    65 
    66 - (id) _initWithKeyData: (NSData*)data
    67             forKeychain: (SecKeychainRef)keychain
    68 {
    69     NSMutableDictionary *info = $mdict({(id)kSecClass, (id)kSecClassKey},
    70                                         {(id)kSecAttrKeyType, (id)self.keyType},
    71                                         {(id)kSecValueData, data},
    72                                         {(id)kSecAttrIsPermanent, (keychain ?$true :$false)},
    73                                         {(id)kSecReturnPersistentRef, $true} );
    74     SecKeyRef key = [[self class] _addKeyWithInfo: info];
    75     if (!key) {
    76         [self release];
    77         return nil;
    78     }
    79     self = [self initWithKeyRef: (SecKeyRef)key];
    80     if (self) {
    81         if (!keychain)
    82             _keyData = [data copy];
    83     }
    84     return self;
    85 }
    86 
    87 - (id) initWithKeyData: (NSData*)data {
    88     return [self _initWithKeyData: data forKeychain: nil];
    89 }
    90 
    91 - (void) dealloc
    92 {
    93     [_keyData release];
    94     [super dealloc];
    95 }
    96 
    97 
    98 - (SecExternalItemType) keyClass {
    99     AssertAbstractMethod();
   100 }
   101 
   102 - (SecExternalItemType) keyType {
   103     return NULL;
   104 }
   105 
   106 - (NSData*) keyData {
   107     if (_keyData)
   108         return _keyData;
   109     
   110     NSDictionary *info = $dict( {(id)kSecValueRef, (id)self.keyRef},
   111                                 {(id)kSecReturnData, $true} );
   112     CFDataRef data;
   113     if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&data), @"SecItemCopyMatching"))
   114         return nil;
   115     else {
   116         Assert(data!=NULL);
   117         _keyData = NSMakeCollectable(data);
   118         return _keyData;
   119     }
   120     // The format of this data is not documented. There's been some reverse-engineering:
   121     // https://devforums.apple.com/message/32089#32089
   122     // Apparently it is a DER-formatted sequence of a modulus followed by an exponent.
   123     // This can be converted to OpenSSL format by wrapping it in some additional DER goop.
   124 }
   125 
   126 - (MYSHA1Digest*) _keyDigest {
   127     return [self.keyData my_SHA1Digest];
   128 }
   129 
   130 - (SecKeyRef) keyRef {
   131     return (SecKeyRef) self.keychainItemRef;
   132 }
   133 
   134 
   135 - (id) _attribute: (CFTypeRef)attribute {
   136     NSDictionary *info = $dict({(id)kSecValueRef, (id)self.keyRef},
   137                                {(id)kSecReturnAttributes, $true});
   138     CFDictionaryRef attrs = NULL;
   139     if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
   140         return nil;
   141     Log(@"_attribute: %@ of %@ %p", attribute, [self class], self.keyRef);//TEMP
   142     CFTypeRef rawValue = CFDictionaryGetValue(attrs,attribute);
   143     id value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
   144     CFRelease(attrs);
   145     return value;
   146 }
   147 
   148 - (BOOL) setValue: (NSString*)value ofAttribute: (SecKeychainAttrType)attribute {
   149     if (!value)
   150         value = (id)[NSNull null];
   151     NSDictionary *query = $dict( {(id)kSecValueRef, (id)self.keyRef} );
   152     NSDictionary *attrs = $dict( {(id)attribute, value} );
   153     return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
   154 }
   155 
   156 
   157 - (NSString*) name {
   158     return [self _attribute: kSecAttrLabel];
   159 }
   160 
   161 - (void) setName: (NSString*)name {
   162     [self setValue: name ofAttribute: kSecAttrLabel];
   163 }
   164 
   165 - (NSString*) alias {
   166     return [self _attribute: kSecAttrApplicationTag];
   167 }
   168 
   169 - (void) setAlias: (NSString*)alias {
   170     [self setValue: alias ofAttribute: kSecAttrApplicationTag];
   171 }
   172 
   173 
   174 
   175 
   176 /** Asymmetric encryption/decryption; used by MYPublicKey and MYPrivateKey. */
   177 - (NSData*) _crypt: (NSData*)data operation: (BOOL)operation {
   178     CAssert(data);
   179     size_t dataLength = data.length;
   180     SecKeyRef key = self.keyRef;
   181     size_t outputLength = MAX(dataLength, SecKeyGetBlockSize(key));
   182     void *outputBuf = malloc(outputLength);
   183     if (!outputBuf) return nil;
   184     OSStatus err;
   185     if (operation)
   186         err = SecKeyEncrypt(key, kSecPaddingNone,//PKCS1, 
   187                             data.bytes, dataLength,
   188                             outputBuf, &outputLength);
   189     else
   190         err = SecKeyDecrypt(key, kSecPaddingNone,//PKCS1, 
   191                             data.bytes, dataLength,
   192                             outputBuf, &outputLength);
   193     if (err) {
   194         free(outputBuf);
   195         Warn(@"%scrypting failed (%i)", (operation ?"En" :"De"), err);
   196         // Note: One of the errors I've seen is -9809, which is errSSLCrypto (SecureTransport.h)
   197         return nil;
   198     } else
   199         return [NSData dataWithBytesNoCopy: outputBuf length: outputLength freeWhenDone: YES];
   200 }
   201 
   202 
   203 @end
   204 
   205 
   206 #endif MYCRYPTO_USE_IPHONE_API
   207 
   208 
   209 
   210 /*
   211  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   212  
   213  Redistribution and use in source and binary forms, with or without modification, are permitted
   214  provided that the following conditions are met:
   215  
   216  * Redistributions of source code must retain the above copyright notice, this list of conditions
   217  and the following disclaimer.
   218  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   219  and the following disclaimer in the documentation and/or other materials provided with the
   220  distribution.
   221  
   222  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   223  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   224  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   225  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   226  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   227   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   228  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   229  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   230  */