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