MYCertificate now checks validity of self-signed certs loaded from the keychain (because the Security framework doesn't validate self-signed certs.)
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 "MYBERParser.h"
12 #import "MYErrorUtils.h"
15 NSString* const MYCSSMErrorDomain = @"CSSMErrorDomain";
18 @implementation MYKeychainItem
21 - (id) initWithKeychainItemRef: (MYKeychainItemRef)itemRef
23 Assert(itemRef!=NULL);
28 LogTo(INIT,@"%@, _itemRef=%@", [self class], itemRef);
29 #if MYCRYPTO_USE_IPHONE_API
37 @synthesize keychainItemRef=_itemRef;
39 #if MYCRYPTO_USE_IPHONE_API
40 @synthesize isPersistent = _isPersistent;
45 if (_itemRef) CFRelease(_itemRef);
51 if (_itemRef) CFRelease(_itemRef);
55 - (id) copyWithZone: (NSZone*)zone {
56 // As keys are immutable, it's not necessary to make copies of them. This makes it more efficient
57 // to use instances as NSDictionary keys or store them in NSSets.
61 - (BOOL) isEqual: (id)obj {
62 return (obj == self) ||
63 ([obj isKindOfClass: [MYKeychainItem class]] && CFEqual(_itemRef, [obj keychainItemRef]));
67 return CFHash(_itemRef);
70 - (NSString*) description {
71 return $sprintf(@"%@[%p]", [self class], _itemRef); //FIX: Can we do anything better?
75 - (NSArray*) _itemList {
76 return $array((id)_itemRef);
80 - (MYKeychain*) keychain {
81 #if MYCRYPTO_USE_IPHONE_API
82 return _isPersistent ? [MYKeychain defaultKeychain] : nil;
84 MYKeychain *keychain = nil;
85 SecKeychainRef keychainRef = NULL;
86 if (check(SecKeychainItemCopyKeychain((SecKeychainItemRef)_itemRef, &keychainRef), @"SecKeychainItemCopyKeychain")) {
88 keychain = [[[MYKeychain alloc] initWithKeychainRef: keychainRef] autorelease];
89 CFRelease(keychainRef);
96 - (BOOL) removeFromKeychain {
98 #if MYCRYPTO_USE_IPHONE_API
99 err = SecItemDelete((CFDictionaryRef) $dict( {(id)kSecValueRef, (id)_itemRef} ));
103 err = SecKeychainItemDelete((SecKeychainItemRef)_itemRef);
104 if (err==errSecInvalidItemRef)
105 return YES; // result for an item that's not in a keychain
107 return err==errSecItemNotFound || check(err, @"SecKeychainItemDelete");
111 /* (Not useful yet, as only password items have dates.)
112 - (NSDate*) dateValueOfAttribute: (SecKeychainAttrType)attr {
113 NSString *dateStr = [self stringValueOfAttribute: attr];
114 if (dateStr.length == 0)
116 NSDate *date = [MYBERGeneralizedTimeFormatter() dateFromString: dateStr];
118 Warn(@"MYKeychainItem: unable to parse date '%@'", dateStr);
122 - (void) setDateValue: (NSDate*)date ofAttribute: (SecKeychainAttrType)attr {
123 NSString *timeStr = nil;
125 timeStr = [MYBERGeneralizedTimeFormatter() stringFromDate: date];
126 [self setValue: timeStr ofAttribute: attr];
130 - (NSDate*) creationDate {
131 return [self dateValueOfAttribute: kSecCreationDateItemAttr];
134 - (void) setCreationDate: (NSDate*)date {
135 [self setDateValue: date ofAttribute: kSecCreationDateItemAttr];
138 - (NSDate*) modificationDate {
139 return [self dateValueOfAttribute: kSecModDateItemAttr];
142 - (void) setModificationDate: (NSDate*)date {
143 [self setDateValue: date ofAttribute: kSecModDateItemAttr];
149 #pragma mark DATA / METADATA ACCESSORS:
152 - (NSData*) _getContents: (OSStatus*)outError {
153 NSData *contents = nil;
154 #if MYCRYPTO_USE_IPHONE_API
158 *outError = SecKeychainItemCopyAttributesAndData(_itemRef, NULL, NULL, NULL, &length, &bytes);
159 if (!*outError && bytes) {
160 contents = [NSData dataWithBytes: bytes length: length];
161 SecKeychainItemFreeAttributesAndData(NULL, bytes);
167 + (NSData*) _getAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
169 #if MYCRYPTO_USE_IPHONE_API
170 NSDictionary *info = $dict( {(id)kSecValueRef, (id)item},
171 {(id)kSecReturnAttributes, $true} );
172 CFDictionaryRef attrs;
173 if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
175 CFTypeRef rawValue = CFDictionaryGetValue(attrs,attr);
176 value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
180 UInt32 format = kSecFormatUnknown;
181 SecKeychainAttributeInfo info = {.count=1, .tag=(UInt32*)&attr, .format=&format};
182 SecKeychainAttributeList *list = NULL;
184 if (check(SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)item, &info,
185 NULL, &list, NULL, NULL),
186 @"SecKeychainItemCopyAttributesAndData")) {
188 if (list->count == 1)
189 value = [NSData dataWithBytes: list->attr->data
190 length: list->attr->length];
191 else if (list->count > 1)
192 Warn(@"Multiple values for keychain item attribute");
193 SecKeychainItemFreeAttributesAndData(list, NULL);
200 + (NSString*) _getStringAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
201 NSData *value = [self _getAttribute: attr ofItem: item];
202 if (!value) return nil;
203 const char *bytes = value.bytes;
204 size_t length = value.length;
205 if (length>0 && bytes[length-1] == 0)
206 length--; // Some values are null-terminated!?
207 NSString *str = [[NSString alloc] initWithBytes: bytes length: length
208 encoding: NSUTF8StringEncoding];
210 Warn(@"MYKeychainItem: Couldn't decode attr value as string");
211 return [str autorelease];
214 - (NSString*) stringValueOfAttribute: (SecKeychainAttrType)attr {
215 #if MYCRYPTO_USE_IPHONE_API
216 if (!self.isPersistent)
219 return [[self class] _getStringAttribute: attr ofItem: _itemRef];
223 + (BOOL) _setAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item
224 stringValue: (NSString*)stringValue
226 #if MYCRYPTO_USE_IPHONE_API
227 id value = stringValue ?(id)stringValue :(id)[NSNull null];
228 NSDictionary *query = $dict({(id)kSecValueRef, (id)item});
229 NSDictionary *attrs = $dict({(id)attr, value});
230 return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
233 NSData *data = [stringValue dataUsingEncoding: NSUTF8StringEncoding];
234 SecKeychainAttribute attribute = {.tag=attr, .length=data.length, .data=(void*)data.bytes};
235 SecKeychainAttributeList list = {.count=1, .attr=&attribute};
236 return check(SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)item, &list, 0, NULL),
237 @"SecKeychainItemModifyAttributesAndData");
241 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
242 return [[self class] _setAttribute: attr ofItem: _itemRef stringValue: valueStr];
251 BOOL check(OSStatus err, NSString *what) {
253 #if !MYCRYPTO_USE_IPHONE_API
254 if (err < -2000000000)
255 return checkcssm(err,what);
257 Warn(@"MYCrypto error, %@: %@", what, MYErrorName(NSOSStatusErrorDomain,err));
259 [NSException raise: NSGenericException format: @"%@ failed with paramErr (-50)",what];
265 #if !MYCRYPTO_USE_IPHONE_API
266 BOOL checkcssm(CSSM_RETURN err, NSString *what) {
267 if (err != CSSM_OK) {
268 Warn(@"MYCrypto error, %@: %@", what, MYErrorName(MYCSSMErrorDomain,err));
278 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
280 Redistribution and use in source and binary forms, with or without modification, are permitted
281 provided that the following conditions are met:
283 * Redistributions of source code must retain the above copyright notice, this list of conditions
284 and the following disclaimer.
285 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
286 and the following disclaimer in the documentation and/or other materials provided with the
289 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
290 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
291 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
292 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
293 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
294 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
295 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
296 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.