MYKeychain-iPhone.m
author Jens Alfke <jens@mooseyard.com>
Fri Aug 07 11:24:53 2009 -0700 (2009-08-07)
changeset 28 54b373aa65ab
parent 24 6856e071d25a
permissions -rw-r--r--
Fixed iPhone OS build. (issue 3)
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
jens@26
    17
// from cssmtype.h:
jens@26
    18
enum {
jens@26
    19
    CSSM_CERT_UNKNOWN =					0x00,
jens@26
    20
    CSSM_CERT_X_509v1 =					0x01,
jens@26
    21
    CSSM_CERT_X_509v2 =					0x02,
jens@26
    22
    CSSM_CERT_X_509v3 =					0x03,
jens@26
    23
jens@26
    24
    CSSM_CERT_ENCODING_UNKNOWN =		0x00,
jens@26
    25
    CSSM_CERT_ENCODING_CUSTOM =			0x01,
jens@26
    26
    CSSM_CERT_ENCODING_BER =			0x02,
jens@26
    27
    CSSM_CERT_ENCODING_DER =			0x03,
jens@26
    28
};
jens@26
    29
jens@26
    30
snej@0
    31
@interface MYKeyEnumerator : NSEnumerator
snej@0
    32
{
snej@0
    33
    CFArrayRef _results;
snej@0
    34
    CFTypeRef _itemClass;
snej@0
    35
    CFIndex _index;
jens@23
    36
    MYKeychainItem *_currentObject;
snej@0
    37
}
snej@0
    38
snej@0
    39
- (id) initWithQuery: (NSMutableDictionary*)query;
snej@0
    40
+ (id) firstItemWithQuery: (NSMutableDictionary*)query;
snej@0
    41
@end
snej@0
    42
snej@0
    43
snej@0
    44
snej@0
    45
@implementation MYKeychain
snej@0
    46
snej@0
    47
snej@0
    48
+ (MYKeychain*) allKeychains
snej@0
    49
{
snej@0
    50
    // iPhone only has a single keychain.
snej@0
    51
    return [self defaultKeychain];
snej@0
    52
}
snej@0
    53
snej@0
    54
+ (MYKeychain*) defaultKeychain
snej@0
    55
{
snej@0
    56
    static MYKeychain *sDefaultKeychain;
snej@0
    57
    @synchronized(self) {
snej@0
    58
        if (!sDefaultKeychain) {
snej@0
    59
            sDefaultKeychain = [[self alloc] init];
snej@0
    60
        }
snej@0
    61
    }
snej@0
    62
    return sDefaultKeychain;
snej@0
    63
}
snej@0
    64
snej@0
    65
snej@0
    66
- (id) copyWithZone: (NSZone*)zone {
snej@0
    67
    // It's not necessary to make copies of Keychain objects. This makes it more efficient
snej@0
    68
    // to use instances as NSDictionary keys or store them in NSSets.
snej@0
    69
    return [self retain];
snej@0
    70
}
snej@0
    71
snej@0
    72
snej@0
    73
snej@0
    74
#pragma mark -
snej@0
    75
#pragma mark SEARCHING:
snej@0
    76
snej@0
    77
snej@0
    78
- (MYPublicKey*) publicKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0
    79
    return [MYKeyEnumerator firstItemWithQuery:
jens@23
    80
                $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPublic},
jens@23
    81
                       {(id)kSecAttrApplicationLabel, pubKeyDigest.asData})];
snej@0
    82
}   
snej@0
    83
snej@0
    84
- (NSEnumerator*) enumeratePublicKeys {
jens@23
    85
    NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPublic});
snej@0
    86
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
    87
}
snej@0
    88
snej@0
    89
snej@3
    90
- (MYPrivateKey*) privateKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0
    91
    return [MYKeyEnumerator firstItemWithQuery:
jens@23
    92
                $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate},
jens@23
    93
                       {(id)kSecAttrApplicationLabel, pubKeyDigest.asData})];
snej@0
    94
}
snej@0
    95
snej@3
    96
- (NSEnumerator*) enumeratePrivateKeys {
jens@23
    97
    NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate});
snej@0
    98
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
    99
}
snej@0
   100
snej@0
   101
- (MYCertificate*) certificateWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0
   102
    return [MYKeyEnumerator firstItemWithQuery:
snej@0
   103
                $mdict({(id)kSecClass, (id)kSecClassCertificate},
jens@23
   104
                       {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData})];
snej@0
   105
}
snej@0
   106
snej@0
   107
- (NSEnumerator*) enumerateCertificates {
jens@23
   108
    NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassCertificate});
snej@0
   109
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
   110
}
snej@0
   111
jens@21
   112
- (MYIdentity*) identityWithDigest: (MYSHA1Digest*)pubKeyDigest {
jens@21
   113
    return [MYKeyEnumerator firstItemWithQuery:
jens@21
   114
                $mdict({(id)kSecClass, (id)kSecClassIdentity},
jens@26
   115
                       {(id)kSecAttrApplicationLabel/*kSecAttrPublicKeyHash*/, pubKeyDigest.asData})];
jens@21
   116
}
jens@21
   117
snej@5
   118
- (NSEnumerator*) enumerateIdentities {
jens@23
   119
    NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassIdentity});
snej@5
   120
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@5
   121
}
snej@5
   122
snej@0
   123
- (NSEnumerator*) enumerateSymmetricKeys {
jens@23
   124
    NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric});
snej@0
   125
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
   126
}
snej@0
   127
snej@0
   128
- (NSEnumerator*) symmetricKeysWithAlias: (NSString*)alias {
jens@23
   129
    NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
jens@23
   130
                                        {(id)kSecAttrApplicationTag, alias});
snej@0
   131
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
   132
}
snej@0
   133
snej@0
   134
snej@0
   135
#pragma mark -
snej@0
   136
#pragma mark IMPORT:
snej@0
   137
snej@0
   138
jens@26
   139
+ (CFTypeRef) _addItemWithInfo: (NSMutableDictionary*)info {
jens@26
   140
    // Generally SecItemAdd will fail (return paramErr) if asked to return a regular ref.
jens@26
   141
    // As a workaround ask for a persistent ref instead, then convert that to regular ref.
jens@26
   142
    if (![[info objectForKey: (id)kSecReturnRef] boolValue])
jens@26
   143
        [info setObject: $true forKey: (id)kSecReturnPersistentRef];
jens@26
   144
    
jens@26
   145
    CFDataRef itemPersistentRef;
jens@26
   146
    CFTypeRef item;
jens@26
   147
    OSStatus err = SecItemAdd((CFDictionaryRef)info, (CFTypeRef*)&itemPersistentRef);
jens@26
   148
    if (err==errSecDuplicateItem) {
jens@26
   149
        Log(@"_addItemWithInfo: Keychain claims it's a dup, so look for existing item");
jens@26
   150
        // it's already in the keychain -- get a reference to it:
jens@26
   151
		[info removeObjectForKey: (id)kSecReturnPersistentRef];
jens@26
   152
		[info setObject: $true forKey: (id)kSecReturnRef];
jens@26
   153
		if (check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef *)&item), 
jens@26
   154
                  @"SecItemCopyMatching")) {
jens@26
   155
            if (!item)
jens@26
   156
                Warn(@"_addItemWithInfo: Couldn't find supposedly-duplicate item, info=%@",info);
jens@26
   157
            Log(@"_addItemWithInfo: SecItemAdd found item; ref=%@", item);//TEMP
jens@26
   158
            return item;
jens@26
   159
        }
jens@26
   160
    } else if (check(err, @"SecItemAdd")) {
jens@26
   161
        // It was added
jens@26
   162
        if ([[info objectForKey: (id)kSecReturnPersistentRef] boolValue]) {
jens@26
   163
            // now get its item ref:
jens@26
   164
            Log(@"SecItemAdd added item; persistenRef=%@", itemPersistentRef);//TEMP
jens@26
   165
            info = $mdict({(id)kSecValuePersistentRef, (id)itemPersistentRef},
jens@26
   166
            {(id)kSecReturnRef, $true});
jens@26
   167
            err = SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef *)&item);
jens@26
   168
            CFRelease(itemPersistentRef);
jens@26
   169
            if (check(err,@"SecItemCopyMatching")) {
jens@26
   170
                Assert(item!=nil);
jens@26
   171
                return item;
jens@26
   172
            }
jens@26
   173
        } else {
jens@26
   174
            Log(@"SecItemAdd added item; ref=%@", itemPersistentRef);//TEMP
jens@26
   175
            return (CFTypeRef)itemPersistentRef;
jens@26
   176
        }
jens@26
   177
    }
jens@26
   178
    Log(@"SecItemAdd failed: info = %@", info); // for help in debugging, dump the input dict
jens@26
   179
    return NULL;
jens@26
   180
}
jens@26
   181
jens@26
   182
snej@0
   183
- (MYPublicKey*) importPublicKey: (NSData*)keyData {
snej@0
   184
    return [[[MYPublicKey alloc] _initWithKeyData: keyData 
snej@0
   185
                                      forKeychain: self]
snej@0
   186
            autorelease];
snej@0
   187
}
snej@0
   188
snej@0
   189
- (MYCertificate*) importCertificate: (NSData*)data
snej@0
   190
{
snej@0
   191
    Assert(data);
jens@26
   192
    
jens@26
   193
#if 1
jens@26
   194
    SecCertificateRef cert0 = SecCertificateCreateWithData(NULL, (CFDataRef)data);
jens@26
   195
    if (!cert0)
snej@0
   196
        return nil;
jens@26
   197
    NSMutableDictionary *info = $mdict( {(id)kSecClass, (id)kSecClassCertificate},
jens@26
   198
                                        {(id)kSecValueRef, (id)cert0});
jens@26
   199
#else
jens@26
   200
    NSMutableDictionary *info = $mdict( {(id)kSecClass, (id)kSecClassCertificate},
jens@26
   201
                                        {(id)kSecAttrCertificateType, $object(CSSM_CERT_X_509v3)},
jens@26
   202
                                        {(id)kSecAttrCertificateEncoding, $object(CSSM_CERT_ENCODING_BER)},
jens@26
   203
                                        {(id)kSecValueData, data} );
jens@26
   204
#endif
jens@26
   205
    SecCertificateRef cert = (SecCertificateRef) [[self class] _addItemWithInfo: info];
jens@26
   206
    if (!cert)
jens@26
   207
        return nil;
jens@26
   208
    MYCertificate *myCert = [[[MYCertificate alloc] initWithCertificateRef: cert] autorelease];
jens@26
   209
    AssertEqual(data, myCert.certificateData);  //TEMP for debugging
jens@26
   210
    return myCert;
snej@0
   211
}
snej@0
   212
snej@0
   213
snej@0
   214
#pragma mark -
snej@0
   215
#pragma mark GENERATION:
snej@0
   216
snej@0
   217
snej@0
   218
- (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0
   219
                                     algorithm: (CCAlgorithm)algorithm
snej@0
   220
{
snej@0
   221
    return [MYSymmetricKey _generateSymmetricKeyOfSize: keySizeInBits
snej@0
   222
                                             algorithm: algorithm inKeychain: self];
snej@0
   223
}
snej@0
   224
snej@3
   225
- (MYPrivateKey*) generateRSAKeyPairOfSize: (unsigned)keySize {
snej@3
   226
    return [MYPrivateKey _generateRSAKeyPairOfSize: keySize inKeychain: self];
snej@0
   227
}
snej@0
   228
snej@0
   229
jens@26
   230
#pragma mark -
jens@26
   231
#pragma mark REMOVING:
jens@26
   232
jens@26
   233
jens@26
   234
- (BOOL) removeAllCertificates {
jens@26
   235
    NSDictionary *query = $dict({(id)kSecClass, (id)kSecClassCertificate});
jens@26
   236
    return check(SecItemDelete((CFDictionaryRef)query),  @"SecItemDelete");
jens@26
   237
}
jens@26
   238
jens@26
   239
- (BOOL) removeAllKeys {
jens@26
   240
    NSDictionary *query = $dict({(id)kSecClass, (id)kSecClassKey});
jens@26
   241
    return check(SecItemDelete((CFDictionaryRef)query), @"SecItemDelete");
jens@26
   242
}
jens@26
   243
jens@26
   244
snej@0
   245
@end
snej@0
   246
snej@0
   247
snej@0
   248
snej@0
   249
#pragma mark -
snej@0
   250
@implementation MYKeyEnumerator
snej@0
   251
snej@0
   252
- (id) initWithQuery: (NSMutableDictionary*)query {
snej@0
   253
    self = [super init];
snej@0
   254
    if (self) {
jens@23
   255
        _itemClass = (CFTypeRef)[query objectForKey: (id)kSecAttrKeyClass];
jens@23
   256
        if (_itemClass)
jens@23
   257
            [query setObject: (id)kSecClassKey forKey: (id)kSecClass];
jens@23
   258
        else
jens@23
   259
            _itemClass = (CFTypeRef)[query objectForKey: (id)kSecClass];
jens@23
   260
        Assert(_itemClass);
jens@23
   261
        CFRetain(_itemClass);
jens@23
   262
jens@23
   263
        // Ask for all results unless caller specified fewer:
jens@23
   264
        CFTypeRef limit = [query objectForKey: (id)kSecMatchLimit];
jens@23
   265
        if (! limit) {
jens@23
   266
            limit = kSecMatchLimitAll;
jens@23
   267
            [query setObject: (id)limit forKey: (id)kSecMatchLimit];
jens@23
   268
        }
jens@23
   269
        
jens@23
   270
        [query setObject: $true forKey: (id)kSecReturnRef];
jens@23
   271
        
snej@0
   272
        OSStatus err = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef*)&_results);
snej@0
   273
        if (err && err != errSecItemNotFound) {
snej@0
   274
            check(err,@"SecItemCopyMatching");
snej@0
   275
            [self release];
snej@0
   276
            return nil;
snej@0
   277
        }
jens@26
   278
        //Log(@"Enumerator results = %@", _results);
jens@23
   279
        
jens@23
   280
        if (_results && CFEqual(limit,kSecMatchLimitOne)) {
jens@23
   281
            // If you ask for only one, it gives you the object back instead of an array:
jens@23
   282
            CFArrayRef resultsArray = CFArrayCreate(NULL, (const void**)&_results, 1, 
jens@23
   283
                                                    &kCFTypeArrayCallBacks);
jens@23
   284
            CFRelease(_results);
jens@23
   285
            _results = resultsArray;
jens@23
   286
        }
snej@0
   287
    }
snej@0
   288
    return self;
snej@0
   289
}
snej@0
   290
snej@0
   291
+ (id) firstItemWithQuery: (NSMutableDictionary*)query {
jens@23
   292
    [query setObject: (id)kSecMatchLimitOne forKey: (id)kSecMatchLimit];
snej@0
   293
    MYKeyEnumerator *e = [[self alloc] initWithQuery: query];
jens@23
   294
    MYKeychainItem *item = [e.nextObject retain];
snej@0
   295
    [e release];
jens@23
   296
    return [item autorelease];
snej@0
   297
}    
snej@0
   298
snej@0
   299
- (void) dealloc
snej@0
   300
{
jens@23
   301
    [_currentObject release];
jens@23
   302
    CFRelease(_itemClass);
snej@0
   303
    if (_results) CFRelease(_results);
snej@0
   304
    [super dealloc];
snej@0
   305
}
snej@0
   306
snej@0
   307
jens@24
   308
- (BOOL) _verifyPublicKeyRef: (MYKeychainItemRef)itemRef {
jens@24
   309
    // Enumerating the keychain sometimes returns public-key refs that give not-found errors
jens@24
   310
    // when you try to use them for anything. As a workaround, detect these early on before
jens@24
   311
    // even creating a MYPublicKey:
jens@24
   312
    NSDictionary *info = $dict({(id)kSecValueRef, (id)itemRef},
jens@26
   313
                               {(id)kSecReturnAttributes, $true});
jens@24
   314
    CFDictionaryRef attrs = NULL;
jens@24
   315
    OSStatus err = SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs);
jens@24
   316
    if (attrs) CFRelease(attrs);
jens@24
   317
    if (err == errSecItemNotFound) {
jens@24
   318
        Log(@"MYKeyEnumerator: Ignoring bogus(?) key with ref %p", itemRef);
jens@24
   319
        return NO;
jens@24
   320
    } else
jens@24
   321
        return YES;
jens@24
   322
}        
jens@24
   323
snej@0
   324
- (id) nextObject {
snej@0
   325
    if (!_results)
snej@0
   326
        return nil;
jens@23
   327
    setObj(&_currentObject,nil);
jens@23
   328
    while (_currentObject==nil && _index < CFArrayGetCount(_results)) {
snej@2
   329
        CFTypeRef found = CFArrayGetValueAtIndex(_results, _index++); 
snej@0
   330
        if (_itemClass == kSecAttrKeyClassPrivate) {
jens@23
   331
            _currentObject = [[MYPrivateKey alloc] initWithKeyRef: (SecKeyRef)found];
snej@0
   332
        } else if (_itemClass == kSecAttrKeyClassPublic) {
jens@24
   333
            if ([self _verifyPublicKeyRef: found])
jens@24
   334
                _currentObject = [[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found];
snej@0
   335
        } else if (_itemClass == kSecAttrKeyClassSymmetric) {
jens@23
   336
            _currentObject = [[MYSymmetricKey alloc] initWithKeyRef: (SecKeyRef)found];
snej@0
   337
        } else if (_itemClass == kSecClassCertificate) {
jens@23
   338
            _currentObject = [[MYCertificate alloc] initWithCertificateRef: (SecCertificateRef)found];
snej@5
   339
        } else if (_itemClass == kSecClassIdentity) {
jens@23
   340
            _currentObject = [[MYIdentity alloc] initWithIdentityRef: (SecIdentityRef)found];
jens@23
   341
        } else  {
jens@23
   342
            Assert(NO,@"Unknown _itemClass: %@",_itemClass);
snej@0
   343
        }
snej@0
   344
    }
jens@23
   345
    return _currentObject;
snej@0
   346
}
snej@0
   347
snej@0
   348
snej@0
   349
@end
snej@0
   350
snej@2
   351
#endif MYCRYPTO_USE_IPHONE_API
snej@0
   352
snej@0
   353
snej@0
   354
/*
snej@0
   355
 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
snej@0
   356
 
snej@0
   357
 Redistribution and use in source and binary forms, with or without modification, are permitted
snej@0
   358
 provided that the following conditions are met:
snej@0
   359
 
snej@0
   360
 * Redistributions of source code must retain the above copyright notice, this list of conditions
snej@0
   361
 and the following disclaimer.
snej@0
   362
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
snej@0
   363
 and the following disclaimer in the documentation and/or other materials provided with the
snej@0
   364
 distribution.
snej@0
   365
 
snej@0
   366
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
snej@0
   367
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
snej@0
   368
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
snej@0
   369
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
snej@0
   370
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
snej@0
   371
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
snej@0
   372
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
snej@0
   373
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
snej@0
   374
 */