snej@0: //
snej@0: //  MYKeychain-iPhone.m
snej@0: //  MYCrypto-iPhone
snej@0: //
snej@0: //  Created by Jens Alfke on 3/31/09.
snej@0: //  Copyright 2009 Jens Alfke. All rights reserved.
snej@0: //
snej@0: 
snej@0: #import "MYCrypto_Private.h"
snej@0: #import "MYDigest.h"
snej@5: #import "MYIdentity.h"
snej@5: 
snej@0: 
snej@2: #if MYCRYPTO_USE_IPHONE_API
snej@0: 
snej@0: 
snej@0: @interface MYKeyEnumerator : NSEnumerator
snej@0: {
snej@0:     CFArrayRef _results;
snej@0:     CFTypeRef _itemClass;
snej@0:     CFIndex _index;
snej@0: }
snej@0: 
snej@0: - (id) initWithQuery: (NSMutableDictionary*)query;
snej@0: + (id) firstItemWithQuery: (NSMutableDictionary*)query;
snej@0: @end
snej@0: 
snej@0: 
snej@0: 
snej@0: @implementation MYKeychain
snej@0: 
snej@0: 
snej@0: + (MYKeychain*) allKeychains
snej@0: {
snej@0:     // iPhone only has a single keychain.
snej@0:     return [self defaultKeychain];
snej@0: }
snej@0: 
snej@0: + (MYKeychain*) defaultKeychain
snej@0: {
snej@0:     static MYKeychain *sDefaultKeychain;
snej@0:     @synchronized(self) {
snej@0:         if (!sDefaultKeychain) {
snej@0:             sDefaultKeychain = [[self alloc] init];
snej@0:         }
snej@0:     }
snej@0:     return sDefaultKeychain;
snej@0: }
snej@0: 
snej@0: 
snej@0: - (id) copyWithZone: (NSZone*)zone {
snej@0:     // It's not necessary to make copies of Keychain objects. This makes it more efficient
snej@0:     // to use instances as NSDictionary keys or store them in NSSets.
snej@0:     return [self retain];
snej@0: }
snej@0: 
snej@0: 
snej@0: 
snej@0: #pragma mark -
snej@0: #pragma mark SEARCHING:
snej@0: 
snej@0: 
snej@0: - (MYPublicKey*) publicKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0:     return [MYKeyEnumerator firstItemWithQuery:
snej@0:                 $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0:                       {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData},
snej@0:                       {(id)kSecReturnRef, $true})];
snej@0: }   
snej@0: 
snej@0: - (NSEnumerator*) enumeratePublicKeys {
snej@0:     NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0:                                 {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPublic},
snej@0:                                 {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0:                                 {(id)kSecReturnRef, $true});
snej@0:     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0: }
snej@0: 
snej@0: 
snej@3: - (MYPrivateKey*) privateKeyWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0:     return [MYKeyEnumerator firstItemWithQuery:
snej@0:                 $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0:                       {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate},
snej@0:                       {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData},
snej@0:                       {(id)kSecReturnRef, $true})];
snej@0: }
snej@0: 
snej@3: - (NSEnumerator*) enumeratePrivateKeys {
snej@0:     NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0:                                 {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate},
snej@0:                                 {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0:                                 {(id)kSecReturnRef, $true});
snej@0:     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0: }
snej@0: 
snej@0: - (MYCertificate*) certificateWithDigest: (MYSHA1Digest*)pubKeyDigest {
snej@0:     return [MYKeyEnumerator firstItemWithQuery:
snej@0:                 $mdict({(id)kSecClass, (id)kSecClassCertificate},
snej@0:                       {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData},
snej@0:                       {(id)kSecReturnRef, $true})];
snej@0: }
snej@0: 
snej@0: - (NSEnumerator*) enumerateCertificates {
snej@0:     NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassCertificate},
snej@0:                                 {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0:                                 {(id)kSecReturnRef, $true});
snej@0:     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0: }
snej@0: 
snej@5: - (NSEnumerator*) enumerateIdentities {
snej@5:     NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassIdentity},
snej@5:                                         {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@5:                                         {(id)kSecReturnRef, $true});
snej@5:     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@5: }
snej@5: 
snej@0: - (NSEnumerator*) enumerateSymmetricKeys {
snej@0:     NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0:                                 {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
snej@0:                                 {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0:                                 {(id)kSecReturnRef, $true});
snej@0:     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0: }
snej@0: 
snej@0: - (NSEnumerator*) symmetricKeysWithAlias: (NSString*)alias {
snej@0:     NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassKey},
snej@0:                                 {(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric},
snej@0:                                 {(id)kSecAttrApplicationTag, alias},
snej@0:                                 {(id)kSecMatchLimit, (id)kSecMatchLimitAll},
snej@0:                                 {(id)kSecReturnRef, $true});
snej@0:     return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease];
snej@0: }
snej@0: 
snej@0: 
snej@0: #pragma mark -
snej@0: #pragma mark IMPORT:
snej@0: 
snej@0: 
snej@0: - (MYPublicKey*) importPublicKey: (NSData*)keyData {
snej@0:     return [[[MYPublicKey alloc] _initWithKeyData: keyData 
snej@0:                                       forKeychain: self]
snej@0:             autorelease];
snej@0: }
snej@0: 
snej@0: - (MYCertificate*) importCertificate: (NSData*)data
snej@0: {
snej@0:     Assert(data);
snej@0:     NSDictionary *info = $dict( {(id)kSecClass, (id)kSecClassCertificate},
snej@0:                                 {(id)kSecValueData, data},
snej@0:                                 {(id)kSecReturnRef, $true} );
snej@0:     SecCertificateRef cert;
snej@0:     if (!check(SecItemAdd((CFDictionaryRef)info, (CFTypeRef*)&cert), @"SecItemAdd"))
snej@0:         return nil;
snej@0:     return [[[MYCertificate alloc] initWithCertificateRef: cert] autorelease];
snej@0: }
snej@0: 
snej@0: 
snej@0: #pragma mark -
snej@0: #pragma mark GENERATION:
snej@0: 
snej@0: 
snej@0: - (MYSymmetricKey*) generateSymmetricKeyOfSize: (unsigned)keySizeInBits
snej@0:                                      algorithm: (CCAlgorithm)algorithm
snej@0: {
snej@0:     return [MYSymmetricKey _generateSymmetricKeyOfSize: keySizeInBits
snej@0:                                              algorithm: algorithm inKeychain: self];
snej@0: }
snej@0: 
snej@3: - (MYPrivateKey*) generateRSAKeyPairOfSize: (unsigned)keySize {
snej@3:     return [MYPrivateKey _generateRSAKeyPairOfSize: keySize inKeychain: self];
snej@0: }
snej@0: 
snej@0: 
snej@0: @end
snej@0: 
snej@0: 
snej@0: 
snej@0: #pragma mark -
snej@0: @implementation MYKeyEnumerator
snej@0: 
snej@0: - (id) initWithQuery: (NSMutableDictionary*)query {
snej@0:     self = [super init];
snej@0:     if (self) {
snej@0:         if (![query objectForKey: (id)kSecMatchLimit])
snej@0:             [query setObject: (id)kSecMatchLimitAll forKey: (id)kSecMatchLimit];
snej@0:         OSStatus err = SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef*)&_results);
snej@0:         if (err && err != errSecItemNotFound) {
snej@0:             check(err,@"SecItemCopyMatching");
snej@0:             [self release];
snej@0:             return nil;
snej@0:         }
snej@0:         if (_results) CFRetain(_results);
snej@0:         _itemClass = (CFTypeRef)[query objectForKey: (id)kSecClass];
snej@0:         if (_itemClass == kSecClassKey)
snej@0:             _itemClass = (CFTypeRef)[query objectForKey: (id)kSecAttrKeyClass];
snej@0:         if (_itemClass) CFRetain(_itemClass);
snej@0:     }
snej@0:     return self;
snej@0: }
snej@0: 
snej@0: + (id) firstItemWithQuery: (NSMutableDictionary*)query {
snej@0:     MYKeyEnumerator *e = [[self alloc] initWithQuery: query];
snej@0:     MYKeychainItem *item = e.nextObject;
snej@0:     [e release];
snej@0:     return item;
snej@0: }    
snej@0: 
snej@0: - (void) dealloc
snej@0: {
snej@0:     if (_itemClass) CFRelease(_itemClass);
snej@0:     if (_results) CFRelease(_results);
snej@0:     [super dealloc];
snej@0: }
snej@0: 
snej@0: 
snej@0: - (id) nextObject {
snej@0:     if (!_results)
snej@0:         return nil;
snej@0:     MYKeychainItem *next = nil;
snej@2:     while (next==nil && _index < CFArrayGetCount(_results)) {
snej@2:         CFTypeRef found = CFArrayGetValueAtIndex(_results, _index++); 
snej@0:         if (_itemClass == kSecAttrKeyClassPrivate) {
snej@3:             next = [[MYPrivateKey alloc] initWithKeyRef: (SecKeyRef)found];
snej@0:         } else if (_itemClass == kSecAttrKeyClassPublic) {
snej@0:             next = [[[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found] autorelease];
snej@0:         } else if (_itemClass == kSecAttrKeyClassSymmetric) {
snej@0:             next = [[[MYSymmetricKey alloc] initWithKeyRef: (SecKeyRef)found] autorelease];
snej@0:         } else if (_itemClass == kSecClassCertificate) {
snej@0:             next = [[[MYCertificate alloc] initWithCertificateRef: (SecCertificateRef)found] autorelease];
snej@5:         } else if (_itemClass == kSecClassIdentity) {
snej@5:             next = [[[MYIdentity alloc] initWithIdentityRef: (SecIdentityRef)found] autorelease];
snej@0:         }
snej@0:         CFRelease(found);
snej@0:     }
snej@0:     return next;
snej@0: }
snej@0: 
snej@0: 
snej@0: @end
snej@0: 
snej@2: #endif MYCRYPTO_USE_IPHONE_API
snej@0: 
snej@0: 
snej@0: /*
snej@0:  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
snej@0:  
snej@0:  Redistribution and use in source and binary forms, with or without modification, are permitted
snej@0:  provided that the following conditions are met:
snej@0:  
snej@0:  * Redistributions of source code must retain the above copyright notice, this list of conditions
snej@0:  and the following disclaimer.
snej@0:  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
snej@0:  and the following disclaimer in the documentation and/or other materials provided with the
snej@0:  distribution.
snej@0:  
snej@0:  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
snej@0:  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
snej@0:  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
snej@0:  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
snej@0:  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
snej@0:   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
snej@0:  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
snej@0:  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
snej@0:  */