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; jens@23: MYKeychainItem *_currentObject; 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: jens@23: $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPublic}, jens@23: {(id)kSecAttrApplicationLabel, pubKeyDigest.asData})]; snej@0: } snej@0: snej@0: - (NSEnumerator*) enumeratePublicKeys { jens@23: NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPublic}); 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: jens@23: $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate}, jens@23: {(id)kSecAttrApplicationLabel, pubKeyDigest.asData})]; snej@0: } snej@0: snej@3: - (NSEnumerator*) enumeratePrivateKeys { jens@23: NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassPrivate}); 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}, jens@23: {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData})]; snej@0: } snej@0: snej@0: - (NSEnumerator*) enumerateCertificates { jens@23: NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassCertificate}); snej@0: return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease]; snej@0: } snej@0: jens@21: - (MYIdentity*) identityWithDigest: (MYSHA1Digest*)pubKeyDigest { jens@21: return [MYKeyEnumerator firstItemWithQuery: jens@21: $mdict({(id)kSecClass, (id)kSecClassIdentity}, jens@23: {(id)kSecAttrPublicKeyHash, pubKeyDigest.asData})]; jens@21: } jens@21: snej@5: - (NSEnumerator*) enumerateIdentities { jens@23: NSMutableDictionary *query = $mdict({(id)kSecClass, (id)kSecClassIdentity}); snej@5: return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease]; snej@5: } snej@5: snej@0: - (NSEnumerator*) enumerateSymmetricKeys { jens@23: NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric}); snej@0: return [[[MYKeyEnumerator alloc] initWithQuery: query] autorelease]; snej@0: } snej@0: snej@0: - (NSEnumerator*) symmetricKeysWithAlias: (NSString*)alias { jens@23: NSMutableDictionary *query = $mdict({(id)kSecAttrKeyClass, (id)kSecAttrKeyClassSymmetric}, jens@23: {(id)kSecAttrApplicationTag, alias}); 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) { jens@23: _itemClass = (CFTypeRef)[query objectForKey: (id)kSecAttrKeyClass]; jens@23: if (_itemClass) jens@23: [query setObject: (id)kSecClassKey forKey: (id)kSecClass]; jens@23: else jens@23: _itemClass = (CFTypeRef)[query objectForKey: (id)kSecClass]; jens@23: Assert(_itemClass); jens@23: CFRetain(_itemClass); jens@23: jens@23: // Ask for all results unless caller specified fewer: jens@23: CFTypeRef limit = [query objectForKey: (id)kSecMatchLimit]; jens@23: if (! limit) { jens@23: limit = kSecMatchLimitAll; jens@23: [query setObject: (id)limit forKey: (id)kSecMatchLimit]; jens@23: } jens@23: jens@23: [query setObject: $true forKey: (id)kSecReturnRef]; jens@23: 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: } jens@23: Log(@"Enumerator results = %@", _results);//TEMP jens@23: jens@23: if (_results && CFEqual(limit,kSecMatchLimitOne)) { jens@23: // If you ask for only one, it gives you the object back instead of an array: jens@23: CFArrayRef resultsArray = CFArrayCreate(NULL, (const void**)&_results, 1, jens@23: &kCFTypeArrayCallBacks); jens@23: CFRelease(_results); jens@23: _results = resultsArray; jens@23: } snej@0: } snej@0: return self; snej@0: } snej@0: snej@0: + (id) firstItemWithQuery: (NSMutableDictionary*)query { jens@23: [query setObject: (id)kSecMatchLimitOne forKey: (id)kSecMatchLimit]; snej@0: MYKeyEnumerator *e = [[self alloc] initWithQuery: query]; jens@23: MYKeychainItem *item = [e.nextObject retain]; snej@0: [e release]; jens@23: return [item autorelease]; snej@0: } snej@0: snej@0: - (void) dealloc snej@0: { jens@23: [_currentObject release]; jens@23: CFRelease(_itemClass); snej@0: if (_results) CFRelease(_results); snej@0: [super dealloc]; snej@0: } snej@0: snej@0: jens@24: - (BOOL) _verifyPublicKeyRef: (MYKeychainItemRef)itemRef { jens@24: // Enumerating the keychain sometimes returns public-key refs that give not-found errors jens@24: // when you try to use them for anything. As a workaround, detect these early on before jens@24: // even creating a MYPublicKey: jens@24: NSDictionary *info = $dict({(id)kSecValueRef, (id)itemRef}, jens@24: {(id)kSecReturnAttributes, $true}); jens@24: CFDictionaryRef attrs = NULL; jens@24: OSStatus err = SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs); jens@24: if (attrs) CFRelease(attrs); jens@24: if (err == errSecItemNotFound) { jens@24: Log(@"MYKeyEnumerator: Ignoring bogus(?) key with ref %p", itemRef); jens@24: return NO; jens@24: } else jens@24: return YES; jens@24: } jens@24: snej@0: - (id) nextObject { snej@0: if (!_results) snej@0: return nil; jens@23: setObj(&_currentObject,nil); jens@23: while (_currentObject==nil && _index < CFArrayGetCount(_results)) { snej@2: CFTypeRef found = CFArrayGetValueAtIndex(_results, _index++); snej@0: if (_itemClass == kSecAttrKeyClassPrivate) { jens@23: _currentObject = [[MYPrivateKey alloc] initWithKeyRef: (SecKeyRef)found]; snej@0: } else if (_itemClass == kSecAttrKeyClassPublic) { jens@24: if ([self _verifyPublicKeyRef: found]) jens@24: _currentObject = [[MYPublicKey alloc] initWithKeyRef: (SecKeyRef)found]; snej@0: } else if (_itemClass == kSecAttrKeyClassSymmetric) { jens@23: _currentObject = [[MYSymmetricKey alloc] initWithKeyRef: (SecKeyRef)found]; snej@0: } else if (_itemClass == kSecClassCertificate) { jens@23: _currentObject = [[MYCertificate alloc] initWithCertificateRef: (SecCertificateRef)found]; snej@5: } else if (_itemClass == kSecClassIdentity) { jens@23: _currentObject = [[MYIdentity alloc] initWithIdentityRef: (SecIdentityRef)found]; jens@23: } else { jens@23: Assert(NO,@"Unknown _itemClass: %@",_itemClass); snej@0: } snej@0: } jens@23: return _currentObject; 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 . 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: */