MYKeychain-iPhone.m
author snej@snej-mbp.mtv.corp.google.com
Tue Apr 07 10:56:58 2009 -0700 (2009-04-07)
changeset 2 8982b8fada63
parent 0 0a6527af039b
child 3 1dfe820d7ebe
permissions -rw-r--r--
More work, mostly on documentation.
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@0
    11
snej@2
    12
#if MYCRYPTO_USE_IPHONE_API
snej@0
    13
snej@0
    14
snej@0
    15
@interface MYKeyEnumerator : NSEnumerator
snej@0
    16
{
snej@0
    17
    CFArrayRef _results;
snej@0
    18
    CFTypeRef _itemClass;
snej@0
    19
    CFIndex _index;
snej@0
    20
}
snej@0
    21
snej@0
    22
- (id) initWithQuery: (NSMutableDictionary*)query;
snej@0
    23
+ (id) firstItemWithQuery: (NSMutableDictionary*)query;
snej@0
    24
@end
snej@0
    25
snej@0
    26
snej@0
    27
snej@0
    28
@implementation MYKeychain
snej@0
    29
snej@0
    30
snej@0
    31
+ (MYKeychain*) allKeychains
snej@0
    32
{
snej@0
    33
    // iPhone only has a single keychain.
snej@0
    34
    return [self defaultKeychain];
snej@0
    35
}
snej@0
    36
snej@0
    37
+ (MYKeychain*) defaultKeychain
snej@0
    38
{
snej@0
    39
    static MYKeychain *sDefaultKeychain;
snej@0
    40
    @synchronized(self) {
snej@0
    41
        if (!sDefaultKeychain) {
snej@0
    42
            sDefaultKeychain = [[self alloc] init];
snej@0
    43
        }
snej@0
    44
    }
snej@0
    45
    return sDefaultKeychain;
snej@0
    46
}
snej@0
    47
snej@0
    48
snej@0
    49
- (id) copyWithZone: (NSZone*)zone {
snej@0
    50
    // It's not necessary to make copies of Keychain objects. This makes it more efficient
snej@0
    51
    // to use instances as NSDictionary keys or store them in NSSets.
snej@0
    52
    return [self retain];
snej@0
    53
}
snej@0
    54
snej@0
    55
snej@0
    56
snej@0
    57
#pragma mark -
snej@0
    58
#pragma mark SEARCHING:
snej@0
    59
snej@0
    60
snej@0
    61
- (MYPublicKey*) publicKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0
    62
    return [MYKeyEnumerator firstItemWithQuery:
snej@0
    63
                $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0
    64
                      {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData},
snej@0
    65
                      {(id)kSecReturnRef, $true})];
snej@0
    66
}   
snej@0
    67
snej@0
    68
- (NSEnumerator*) enumeratePublicKeys {
snej@0
    69
    NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0
    70
                                {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPublic},
snej@0
    71
                                {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0
    72
                                {(id)kSecReturnRef, $true});
snej@0
    73
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
    74
}
snej@0
    75
snej@0
    76
snej@0
    77
- (MYKeyPair*) keyPairWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0
    78
    return [MYKeyEnumerator firstItemWithQuery:
snej@0
    79
                $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0
    80
                      {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate},
snej@0
    81
                      {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData},
snej@0
    82
                      {(id)kSecReturnRef, $true})];
snej@0
    83
}
snej@0
    84
snej@0
    85
- (NSEnumerator*) enumerateKeyPairs {
snej@0
    86
    NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0
    87
                                {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate},
snej@0
    88
                                {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0
    89
                                {(id)kSecReturnRef, $true});
snej@0
    90
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
    91
}
snej@0
    92
snej@0
    93
- (MYCertificate*) certificateWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0
    94
    return [MYKeyEnumerator firstItemWithQuery:
snej@0
    95
                $mdict({(id)kSecClass, (id)kSecClassCertificate},
snej@0
    96
                      {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData},
snej@0
    97
                      {(id)kSecReturnRef, $true})];
snej@0
    98
}
snej@0
    99
snej@0
   100
- (NSEnumerator*) enumerateCertificates {
snej@0
   101
    NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassCertificate},
snej@0
   102
                                {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0
   103
                                {(id)kSecReturnRef, $true});
snej@0
   104
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
   105
}
snej@0
   106
snej@0
   107
- (NSEnumerator*) enumerateSymmetricKeys {
snej@0
   108
    NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0
   109
                                {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
snej@0
   110
                                {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0
   111
                                {(id)kSecReturnRef, $true});
snej@0
   112
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
   113
}
snej@0
   114
snej@0
   115
- (NSEnumerator*) symmetricKeysWithAlias: (NSString*)alias {
snej@0
   116
    NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0
   117
                                {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
snej@0
   118
                                {(id)kSecAttrApplicationTag, alias},
snej@0
   119
                                {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0
   120
                                {(id)kSecReturnRef, $true});
snej@0
   121
    return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0
   122
}
snej@0
   123
snej@0
   124
snej@0
   125
#pragma mark -
snej@0
   126
#pragma mark IMPORT:
snej@0
   127
snej@0
   128
snej@0
   129
- (MYPublicKey*) importPublicKey: (NSData*)keyData {
snej@0
   130
    return [[[MYPublicKey alloc] _initWithKeyData: keyData 
snej@0
   131
                                      forKeychain: self]
snej@0
   132
            autorelease];
snej@0
   133
}
snej@0
   134
snej@0
   135
- (MYCertificate*) importCertificate: (NSData*)data
snej@0
   136
{
snej@0
   137
    Assert(data);
snej@0
   138
    NSDictionary *info = $dict( {(id)kSecClass, (id)kSecClassCertificate},
snej@0
   139
                                {(id)kSecValueData, data},
snej@0
   140
                                {(id)kSecReturnRef, $true} );
snej@0
   141
    SecCertificateRef cert;
snej@0
   142
    if (!check(SecItemAdd((CFDictionaryRef)info, (CFTypeRef*)&cert), @"SecItemAdd"))
snej@0
   143
        return nil;
snej@0
   144
    return [[[MYCertificate alloc] initWithCertificateRef: cert] autorelease];
snej@0
   145
}
snej@0
   146
snej@0
   147
snej@0
   148
#pragma mark -
snej@0
   149
#pragma mark GENERATION:
snej@0
   150
snej@0
   151
snej@0
   152
- (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0
   153
                                     algorithm: (CCAlgorithm)algorithm
snej@0
   154
{
snej@0
   155
    return [MYSymmetricKey _generateSymmetricKeyOfSize: keySizeInBits
snej@0
   156
                                             algorithm: algorithm inKeychain: self];
snej@0
   157
}
snej@0
   158
snej@0
   159
- (MYKeyPair*) generateRSAKeyPairOfSize: (unsigned)keySize {
snej@0
   160
    return [MYKeyPair _generateRSAKeyPairOfSize: keySize inKeychain: self];
snej@0
   161
}
snej@0
   162
snej@0
   163
snej@0
   164
snej@0
   165
@end
snej@0
   166
snej@0
   167
snej@0
   168
snej@0
   169
#pragma mark -
snej@0
   170
@implementation MYKeyEnumerator
snej@0
   171
snej@0
   172
- (id) initWithQuery: (NSMutableDictionary*)query {
snej@0
   173
    self = [super init];
snej@0
   174
    if (self) {
snej@0
   175
        if (![query objectForKey: (id)kSecMatchLimit])
snej@0
   176
            [query setObject: (id)kSecMatchLimitAll forKey: (id)kSecMatchLimit];
snej@0
   177
        OSStatus err = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef*)&_results);
snej@0
   178
        if (err && err != errSecItemNotFound) {
snej@0
   179
            check(err,@"SecItemCopyMatching");
snej@0
   180
            [self release];
snej@0
   181
            return nil;
snej@0
   182
        }
snej@0
   183
        if (_results) CFRetain(_results);
snej@0
   184
        _itemClass = (CFTypeRef)[query objectForKey: (id)kSecClass];
snej@0
   185
        if (_itemClass == kSecClassKey)
snej@0
   186
            _itemClass = (CFTypeRef)[query objectForKey: (id)kSecAttrKeyClass];
snej@0
   187
        if (_itemClass) CFRetain(_itemClass);
snej@0
   188
    }
snej@0
   189
    return self;
snej@0
   190
}
snej@0
   191
snej@0
   192
+ (id) firstItemWithQuery: (NSMutableDictionary*)query {
snej@0
   193
    MYKeyEnumerator *e = [[self alloc] initWithQuery: query];
snej@0
   194
    MYKeychainItem *item = e.nextObject;
snej@0
   195
    [e release];
snej@0
   196
    return item;
snej@0
   197
}    
snej@0
   198
snej@0
   199
- (void) dealloc
snej@0
   200
{
snej@0
   201
    if (_itemClass) CFRelease(_itemClass);
snej@0
   202
    if (_results) CFRelease(_results);
snej@0
   203
    [super dealloc];
snej@0
   204
}
snej@0
   205
snej@0
   206
snej@0
   207
- (id) nextObject {
snej@0
   208
    if (!_results)
snej@0
   209
        return nil;
snej@0
   210
    MYKeychainItem *next = nil;
snej@2
   211
    while (next==nil && _index < CFArrayGetCount(_results)) {
snej@2
   212
        CFTypeRef found = CFArrayGetValueAtIndex(_results, _index++); 
snej@0
   213
        if (_itemClass == kSecAttrKeyClassPrivate) {
snej@0
   214
            MYSHA1Digest *digest = [MYPublicKey _digestOfKey: (SecKeyRef)found];
snej@0
   215
            if (digest) {
snej@0
   216
                MYPublicKey *publicKey = [[MYKeychain defaultKeychain] publicKeyWithDigest: digest];
snej@0
   217
                if (publicKey)
snej@0
   218
                    next = [[[MYKeyPair alloc] initWithPublicKeyRef: publicKey.keyRef
snej@0
   219
                                                     privateKeyRef: (SecKeyRef)found]
snej@0
   220
                           autorelease];
snej@0
   221
                else {
snej@0
   222
                    // The matching public key won't turn up if it's embedded in a certificate;
snej@0
   223
                    // I'd have to search for certs if I wanted to look that up. Skip it for now.
snej@0
   224
                    //Warn(@"Couldn't find matching public key for private key!");
snej@0
   225
                }
snej@0
   226
            }
snej@0
   227
        } else if (_itemClass == kSecAttrKeyClassPublic) {
snej@0
   228
            next = [[[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found] autorelease];
snej@0
   229
        } else if (_itemClass == kSecAttrKeyClassSymmetric) {
snej@0
   230
            next = [[[MYSymmetricKey alloc] initWithKeyRef: (SecKeyRef)found] autorelease];
snej@0
   231
        } else if (_itemClass == kSecClassCertificate) {
snej@0
   232
            next = [[[MYCertificate alloc] initWithCertificateRef: (SecCertificateRef)found] autorelease];
snej@0
   233
        }
snej@0
   234
        CFRelease(found);
snej@0
   235
    }
snej@0
   236
    return next;
snej@0
   237
}
snej@0
   238
snej@0
   239
snej@0
   240
@end
snej@0
   241
snej@2
   242
#endif MYCRYPTO_USE_IPHONE_API
snej@0
   243
snej@0
   244
snej@0
   245
/*
snej@0
   246
 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
snej@0
   247
 
snej@0
   248
 Redistribution and use in source and binary forms, with or without modification, are permitted
snej@0
   249
 provided that the following conditions are met:
snej@0
   250
 
snej@0
   251
 * Redistributions of source code must retain the above copyright notice, this list of conditions
snej@0
   252
 and the following disclaimer.
snej@0
   253
 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
snej@0
   254
 and the following disclaimer in the documentation and/or other materials provided with the
snej@0
   255
 distribution.
snej@0
   256
 
snej@0
   257
 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
snej@0
   258
 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
snej@0
   259
 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
snej@0
   260
 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
snej@0
   261
 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
snej@0
   262
  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
snej@0
   263
 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
snej@0
   264
 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
snej@0
   265
 */