MYKeychain-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 //  MYKeychain-iPhone.m
     3 //  MYCrypto-iPhone
     4 //
     5 //  Created by Jens Alfke on 3/31/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYCrypto_Private.h"
    10 #import "MYDigest.h"
    11 #import "MYIdentity.h"
    12 
    13 
    14 #if MYCRYPTO_USE_IPHONE_API
    15 
    16 
    17 @interface MYKeyEnumerator : NSEnumerator
    18 {
    19     CFArrayRef _results;
    20     CFTypeRef _itemClass;
    21     CFIndex _index;
    22     MYKeychainItem *_currentObject;
    23 }
    24 
    25 - (id) initWithQuery: (NSMutableDictionary*)query;
    26 + (id) firstItemWithQuery: (NSMutableDictionary*)query;
    27 @end
    28 
    29 
    30 
    31 @implementation MYKeychain
    32 
    33 
    34 + (MYKeychain*) allKeychains
    35 {
    36     // iPhone only has a single keychain.
    37     return [self defaultKeychain];
    38 }
    39 
    40 + (MYKeychain*) defaultKeychain
    41 {
    42     static MYKeychain *sDefaultKeychain;
    43     @synchronized(self) {
    44         if (!sDefaultKeychain) {
    45             sDefaultKeychain = [[self alloc] init];
    46         }
    47     }
    48     return sDefaultKeychain;
    49 }
    50 
    51 
    52 - (id) copyWithZone: (NSZone*)zone {
    53     // It's not necessary to make copies of Keychain objects. This makes it more efficient
    54     // to use instances as NSDictionary keys or store them in NSSets.
    55     return [self retain];
    56 }
    57 
    58 
    59 
    60 #pragma mark -
    61 #pragma mark SEARCHING:
    62 
    63 
    64 - (MYPublicKey*) publicKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
    65     return [MYKeyEnumerator firstItemWithQuery:
    66                 $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPublic},
    67                        {(id)kSecAttrApplicationLabel, pubKeyDigest.asData})];
    68 }   
    69 
    70 - (NSEnumerator*) enumeratePublicKeys {
    71     NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPublic});
    72     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
    73 }
    74 
    75 
    76 - (MYPrivateKey*) privateKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
    77     return [MYKeyEnumerator firstItemWithQuery:
    78                 $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate},
    79                        {(id)kSecAttrApplicationLabel, pubKeyDigest.asData})];
    80 }
    81 
    82 - (NSEnumerator*) enumeratePrivateKeys {
    83     NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate});
    84     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
    85 }
    86 
    87 - (MYCertificate*) certificateWithDigest: (MYSHA1Digest*)pubKeyDigest {
    88     return [MYKeyEnumerator firstItemWithQuery:
    89                 $mdict({(id)kSecClass, (id)kSecClassCertificate},
    90                        {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData})];
    91 }
    92 
    93 - (NSEnumerator*) enumerateCertificates {
    94     NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassCertificate});
    95     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
    96 }
    97 
    98 - (MYIdentity*) identityWithDigest: (MYSHA1Digest*)pubKeyDigest {
    99     return [MYKeyEnumerator firstItemWithQuery:
   100                 $mdict({(id)kSecClass, (id)kSecClassIdentity},
   101                         {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData})];
   102 }
   103 
   104 - (NSEnumerator*) enumerateIdentities {
   105     NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassIdentity});
   106     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
   107 }
   108 
   109 - (NSEnumerator*) enumerateSymmetricKeys {
   110     NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric});
   111     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
   112 }
   113 
   114 - (NSEnumerator*) symmetricKeysWithAlias: (NSString*)alias {
   115     NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
   116                                         {(id)kSecAttrApplicationTag, alias});
   117     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
   118 }
   119 
   120 
   121 #pragma mark -
   122 #pragma mark IMPORT:
   123 
   124 
   125 - (MYPublicKey*) importPublicKey: (NSData*)keyData {
   126     return [[[MYPublicKey alloc] _initWithKeyData: keyData 
   127                                       forKeychain: self]
   128             autorelease];
   129 }
   130 
   131 - (MYCertificate*) importCertificate: (NSData*)data
   132 {
   133     Assert(data);
   134     NSDictionary *info = $dict( {(id)kSecClass, (id)kSecClassCertificate},
   135                                 {(id)kSecValueData, data},
   136                                 {(id)kSecReturnRef, $true} );
   137     SecCertificateRef cert;
   138     if (!check(SecItemAdd((CFDictionaryRef)info, (CFTypeRef*)&cert), @"SecItemAdd"))
   139         return nil;
   140     return [[[MYCertificate alloc] initWithCertificateRef: cert] autorelease];
   141 }
   142 
   143 
   144 #pragma mark -
   145 #pragma mark GENERATION:
   146 
   147 
   148 - (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
   149                                      algorithm: (CCAlgorithm)algorithm
   150 {
   151     return [MYSymmetricKey _generateSymmetricKeyOfSize: keySizeInBits
   152                                              algorithm: algorithm inKeychain: self];
   153 }
   154 
   155 - (MYPrivateKey*) generateRSAKeyPairOfSize: (unsigned)keySize {
   156     return [MYPrivateKey _generateRSAKeyPairOfSize: keySize inKeychain: self];
   157 }
   158 
   159 
   160 @end
   161 
   162 
   163 
   164 #pragma mark -
   165 @implementation MYKeyEnumerator
   166 
   167 - (id) initWithQuery: (NSMutableDictionary*)query {
   168     self = [super init];
   169     if (self) {
   170         _itemClass = (CFTypeRef)[query objectForKey: (id)kSecAttrKeyClass];
   171         if (_itemClass)
   172             [query setObject: (id)kSecClassKey forKey: (id)kSecClass];
   173         else
   174             _itemClass = (CFTypeRef)[query objectForKey: (id)kSecClass];
   175         Assert(_itemClass);
   176         CFRetain(_itemClass);
   177 
   178         // Ask for all results unless caller specified fewer:
   179         CFTypeRef limit = [query objectForKey: (id)kSecMatchLimit];
   180         if (! limit) {
   181             limit = kSecMatchLimitAll;
   182             [query setObject: (id)limit forKey: (id)kSecMatchLimit];
   183         }
   184         
   185         [query setObject: $true forKey: (id)kSecReturnRef];
   186         
   187         OSStatus err = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef*)&_results);
   188         if (err && err != errSecItemNotFound) {
   189             check(err,@"SecItemCopyMatching");
   190             [self release];
   191             return nil;
   192         }
   193         Log(@"Enumerator results = %@", _results);//TEMP
   194         
   195         if (_results && CFEqual(limit,kSecMatchLimitOne)) {
   196             // If you ask for only one, it gives you the object back instead of an array:
   197             CFArrayRef resultsArray = CFArrayCreate(NULL, (const void**)&_results, 1, 
   198                                                     &kCFTypeArrayCallBacks);
   199             CFRelease(_results);
   200             _results = resultsArray;
   201         }
   202     }
   203     return self;
   204 }
   205 
   206 + (id) firstItemWithQuery: (NSMutableDictionary*)query {
   207     [query setObject: (id)kSecMatchLimitOne forKey: (id)kSecMatchLimit];
   208     MYKeyEnumerator *e = [[self alloc] initWithQuery: query];
   209     MYKeychainItem *item = [e.nextObject retain];
   210     [e release];
   211     return [item autorelease];
   212 }    
   213 
   214 - (void) dealloc
   215 {
   216     [_currentObject release];
   217     CFRelease(_itemClass);
   218     if (_results) CFRelease(_results);
   219     [super dealloc];
   220 }
   221 
   222 
   223 - (BOOL) _verifyPublicKeyRef: (MYKeychainItemRef)itemRef {
   224     // Enumerating the keychain sometimes returns public-key refs that give not-found errors
   225     // when you try to use them for anything. As a workaround, detect these early on before
   226     // even creating a MYPublicKey:
   227     NSDictionary *info = $dict({(id)kSecValueRef, (id)itemRef},
   228     {(id)kSecReturnAttributes, $true});
   229     CFDictionaryRef attrs = NULL;
   230     OSStatus err = SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs);
   231     if (attrs) CFRelease(attrs);
   232     if (err == errSecItemNotFound) {
   233         Log(@"MYKeyEnumerator: Ignoring bogus(?) key with ref %p", itemRef);
   234         return NO;
   235     } else
   236         return YES;
   237 }        
   238 
   239 - (id) nextObject {
   240     if (!_results)
   241         return nil;
   242     setObj(&_currentObject,nil);
   243     while (_currentObject==nil && _index < CFArrayGetCount(_results)) {
   244         CFTypeRef found = CFArrayGetValueAtIndex(_results, _index++); 
   245         if (_itemClass == kSecAttrKeyClassPrivate) {
   246             _currentObject = [[MYPrivateKey alloc] initWithKeyRef: (SecKeyRef)found];
   247         } else if (_itemClass == kSecAttrKeyClassPublic) {
   248             if ([self _verifyPublicKeyRef: found])
   249                 _currentObject = [[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found];
   250         } else if (_itemClass == kSecAttrKeyClassSymmetric) {
   251             _currentObject = [[MYSymmetricKey alloc] initWithKeyRef: (SecKeyRef)found];
   252         } else if (_itemClass == kSecClassCertificate) {
   253             _currentObject = [[MYCertificate alloc] initWithCertificateRef: (SecCertificateRef)found];
   254         } else if (_itemClass == kSecClassIdentity) {
   255             _currentObject = [[MYIdentity alloc] initWithIdentityRef: (SecIdentityRef)found];
   256         } else  {
   257             Assert(NO,@"Unknown _itemClass: %@",_itemClass);
   258         }
   259     }
   260     return _currentObject;
   261 }
   262 
   263 
   264 @end
   265 
   266 #endif MYCRYPTO_USE_IPHONE_API
   267 
   268 
   269 /*
   270  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   271  
   272  Redistribution and use in source and binary forms, with or without modification, are permitted
   273  provided that the following conditions are met:
   274  
   275  * Redistributions of source code must retain the above copyright notice, this list of conditions
   276  and the following disclaimer.
   277  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   278  and the following disclaimer in the documentation and/or other materials provided with the
   279  distribution.
   280  
   281  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   282  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   283  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   284  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   285  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   286   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   287  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   288  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   289  */