A snapshot taken during the long, agonizing crawl toward getting everything running on iPhone.
5 // Created by Jens Alfke on 3/26/09.
6 // Copyright 2009 Jens Alfke. All rights reserved.
9 #import "MYKeychainItem.h"
10 #import "MYCrypto_Private.h"
11 #import "MYErrorUtils.h"
14 NSString* const MYCSSMErrorDomain = @"CSSMErrorDomain";
17 @implementation MYKeychainItem
20 - (id) initWithKeychainItemRef: (MYKeychainItemRef)itemRef
22 Assert(itemRef!=NULL);
27 LogTo(INIT,@"%@, _itemRef=%@", [self class], itemRef);
33 @synthesize keychainItemRef=_itemRef;
37 if (_itemRef) CFRelease(_itemRef);
43 if (_itemRef) CFRelease(_itemRef);
47 - (id) copyWithZone: (NSZone*)zone {
48 // As keys are immutable, it's not necessary to make copies of them. This makes it more efficient
49 // to use instances as NSDictionary keys or store them in NSSets.
53 - (BOOL) isEqual: (id)obj {
54 return (obj == self) ||
55 ([obj isKindOfClass: [MYKeychainItem class]] && CFEqual(_itemRef, [obj keychainItemRef]));
59 return CFHash(_itemRef);
62 - (NSString*) description {
63 return $sprintf(@"%@[%p]", [self class], _itemRef); //FIX: Can we do anything better?
67 - (NSArray*) _itemList {
68 return $array((id)_itemRef);
71 #if MYCRYPTO_USE_IPHONE_API
72 - (CFDictionaryRef) asQuery {
73 return (CFDictionaryRef) $dict( {(id)kSecClass, (id)kSecClassKey},//FIX
74 {(id)kSecMatchItemList, self._itemList} );
79 - (MYKeychain*) keychain {
80 #if MYCRYPTO_USE_IPHONE_API
81 return [MYKeychain defaultKeychain];
83 MYKeychain *keychain = nil;
84 SecKeychainRef keychainRef = NULL;
85 if (check(SecKeychainItemCopyKeychain((SecKeychainItemRef)_itemRef, &keychainRef), @"SecKeychainItemCopyKeychain")) {
87 keychain = [[[MYKeychain alloc] initWithKeychainRef: keychainRef] autorelease];
88 CFRelease(keychainRef);
95 - (BOOL) removeFromKeychain {
97 #if MYCRYPTO_USE_IPHONE_API
98 err = SecItemDelete(self.asQuery);
100 err = SecKeychainItemDelete((SecKeychainItemRef)_itemRef);
101 if (err==errSecInvalidItemRef)
102 return YES; // result for an item that's not in a keychain
104 return err==errSecItemNotFound || check(err, @"SecKeychainItemDelete");
109 #pragma mark DATA / METADATA ACCESSORS:
112 - (NSData*) _getContents: (OSStatus*)outError {
113 NSData *contents = nil;
114 #if MYCRYPTO_USE_IPHONE_API
118 *outError = SecKeychainItemCopyAttributesAndData(_itemRef, NULL, NULL, NULL, &length, &bytes);
119 if (!*outError && bytes) {
120 contents = [NSData dataWithBytes: bytes length: length];
121 SecKeychainItemFreeAttributesAndData(NULL, bytes);
127 + (NSData*) _getAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
129 #if MYCRYPTO_USE_IPHONE_API
130 NSDictionary *info = $dict( {(id)kSecClass, (id)kSecClassKey},
131 {(id)kSecMatchItemList, $array((id)item)},
132 {(id)kSecReturnAttributes, $true} );
133 CFDictionaryRef attrs;
134 if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
136 CFTypeRef rawValue = CFDictionaryGetValue(attrs,attr);
137 value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
141 UInt32 format = kSecFormatUnknown;
142 SecKeychainAttributeInfo info = {.count=1, .tag=(UInt32*)&attr, .format=&format};
143 SecKeychainAttributeList *list = NULL;
145 if (check(SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)item, &info,
146 NULL, &list, NULL, NULL),
147 @"SecKeychainItemCopyAttributesAndData")) {
149 if (list->count == 1)
150 value = [NSData dataWithBytes: list->attr->data
151 length: list->attr->length];
152 else if (list->count > 1)
153 Warn(@"Multiple values for keychain item attribute");
154 SecKeychainItemFreeAttributesAndData(list, NULL);
161 + (NSString*) _getStringAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
162 NSData *value = [self _getAttribute: attr ofItem: item];
163 if (!value) return nil;
164 const char *bytes = value.bytes;
165 size_t length = value.length;
166 if (length>0 && bytes[length-1] == 0)
167 length--; // Some values are null-terminated!?
168 NSString *str = [[NSString alloc] initWithBytes: bytes length: length
169 encoding: NSUTF8StringEncoding];
171 Warn(@"MYKeychainItem: Couldn't decode attr value as string");
172 return [str autorelease];
175 - (NSString*) stringValueOfAttribute: (SecKeychainAttrType)attr {
176 return [[self class] _getStringAttribute: attr ofItem: _itemRef];
180 + (BOOL) _setAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item
181 stringValue: (NSString*)stringValue
183 #if MYCRYPTO_USE_IPHONE_API
184 id value = stringValue ?(id)stringValue :(id)[NSNull null];
185 NSDictionary *query = $dict({(id)kSecClass, (id)kSecClassKey},
186 {(id)kSecAttrKeyType, (id)attr},
187 {(id)kSecMatchItemList, $array((id)item)});
188 NSDictionary *attrs = $dict({(id)attr, value});
189 return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
192 NSData *data = [stringValue dataUsingEncoding: NSUTF8StringEncoding];
193 SecKeychainAttribute attribute = {.tag=attr, .length=data.length, .data=(void*)data.bytes};
194 SecKeychainAttributeList list = {.count=1, .attr=&attribute};
195 return check(SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)item, &list, 0, NULL),
196 @"SecKeychainItemModifyAttributesAndData");
200 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
201 return [[self class] _setAttribute: attr ofItem: _itemRef stringValue: valueStr];
210 BOOL check(OSStatus err, NSString *what) {
212 #if !MYCRYPTO_USE_IPHONE_API
213 if (err < -2000000000)
214 return checkcssm(err,what);
216 Warn(@"MYCrypto error, %@: %@", what, MYErrorName(NSOSStatusErrorDomain,err));
218 [NSException raise: NSGenericException format: @"%@ failed with paramErr (-50)",what];
224 #if !MYCRYPTO_USE_IPHONE_API
225 BOOL checkcssm(CSSM_RETURN err, NSString *what) {
226 if (err != CSSM_OK) {
227 Warn(@"MYCrypto error, %@: %@", what, MYErrorName(MYCSSMErrorDomain,err));
237 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
239 Redistribution and use in source and binary forms, with or without modification, are permitted
240 provided that the following conditions are met:
242 * Redistributions of source code must retain the above copyright notice, this list of conditions
243 and the following disclaimer.
244 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
245 and the following disclaimer in the documentation and/or other materials provided with the
248 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
249 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
250 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
251 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
252 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
253 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
254 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
255 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.