Changed the X.509 version number in generated certs from 1 to 3, so that SecCertificateCreateFromData on iPhone will accept them. :-/
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);
72 - (MYKeychain*) keychain {
73 #if MYCRYPTO_USE_IPHONE_API
74 return [MYKeychain defaultKeychain];
76 MYKeychain *keychain = nil;
77 SecKeychainRef keychainRef = NULL;
78 if (check(SecKeychainItemCopyKeychain((SecKeychainItemRef)_itemRef, &keychainRef), @"SecKeychainItemCopyKeychain")) {
80 keychain = [[[MYKeychain alloc] initWithKeychainRef: keychainRef] autorelease];
81 CFRelease(keychainRef);
88 - (BOOL) removeFromKeychain {
90 #if MYCRYPTO_USE_IPHONE_API
91 err = SecItemDelete((CFDictionaryRef) $dict( {(id)kSecValueRef, (id)_itemRef} ));
93 err = SecKeychainItemDelete((SecKeychainItemRef)_itemRef);
94 if (err==errSecInvalidItemRef)
95 return YES; // result for an item that's not in a keychain
97 return err==errSecItemNotFound || check(err, @"SecKeychainItemDelete");
102 #pragma mark DATA / METADATA ACCESSORS:
105 - (NSData*) _getContents: (OSStatus*)outError {
106 NSData *contents = nil;
107 #if MYCRYPTO_USE_IPHONE_API
111 *outError = SecKeychainItemCopyAttributesAndData(_itemRef, NULL, NULL, NULL, &length, &bytes);
112 if (!*outError && bytes) {
113 contents = [NSData dataWithBytes: bytes length: length];
114 SecKeychainItemFreeAttributesAndData(NULL, bytes);
120 + (NSData*) _getAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
122 #if MYCRYPTO_USE_IPHONE_API
123 NSDictionary *info = $dict( {(id)kSecValueRef, (id)item},
124 {(id)kSecReturnAttributes, $true} );
125 CFDictionaryRef attrs;
126 if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
128 CFTypeRef rawValue = CFDictionaryGetValue(attrs,attr);
129 value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
133 UInt32 format = kSecFormatUnknown;
134 SecKeychainAttributeInfo info = {.count=1, .tag=(UInt32*)&attr, .format=&format};
135 SecKeychainAttributeList *list = NULL;
137 if (check(SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)item, &info,
138 NULL, &list, NULL, NULL),
139 @"SecKeychainItemCopyAttributesAndData")) {
141 if (list->count == 1)
142 value = [NSData dataWithBytes: list->attr->data
143 length: list->attr->length];
144 else if (list->count > 1)
145 Warn(@"Multiple values for keychain item attribute");
146 SecKeychainItemFreeAttributesAndData(list, NULL);
153 + (NSString*) _getStringAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
154 NSData *value = [self _getAttribute: attr ofItem: item];
155 if (!value) return nil;
156 const char *bytes = value.bytes;
157 size_t length = value.length;
158 if (length>0 && bytes[length-1] == 0)
159 length--; // Some values are null-terminated!?
160 NSString *str = [[NSString alloc] initWithBytes: bytes length: length
161 encoding: NSUTF8StringEncoding];
163 Warn(@"MYKeychainItem: Couldn't decode attr value as string");
164 return [str autorelease];
167 - (NSString*) stringValueOfAttribute: (SecKeychainAttrType)attr {
168 return [[self class] _getStringAttribute: attr ofItem: _itemRef];
172 + (BOOL) _setAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item
173 stringValue: (NSString*)stringValue
175 #if MYCRYPTO_USE_IPHONE_API
176 id value = stringValue ?(id)stringValue :(id)[NSNull null];
177 NSDictionary *query = $dict({(id)kSecValueRef, (id)item});
178 NSDictionary *attrs = $dict({(id)attr, value});
179 return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
182 NSData *data = [stringValue dataUsingEncoding: NSUTF8StringEncoding];
183 SecKeychainAttribute attribute = {.tag=attr, .length=data.length, .data=(void*)data.bytes};
184 SecKeychainAttributeList list = {.count=1, .attr=&attribute};
185 return check(SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)item, &list, 0, NULL),
186 @"SecKeychainItemModifyAttributesAndData");
190 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
191 return [[self class] _setAttribute: attr ofItem: _itemRef stringValue: valueStr];
200 BOOL check(OSStatus err, NSString *what) {
202 #if !MYCRYPTO_USE_IPHONE_API
203 if (err < -2000000000)
204 return checkcssm(err,what);
206 Warn(@"MYCrypto error, %@: %@", what, MYErrorName(NSOSStatusErrorDomain,err));
208 [NSException raise: NSGenericException format: @"%@ failed with paramErr (-50)",what];
214 #if !MYCRYPTO_USE_IPHONE_API
215 BOOL checkcssm(CSSM_RETURN err, NSString *what) {
216 if (err != CSSM_OK) {
217 Warn(@"MYCrypto error, %@: %@", what, MYErrorName(MYCSSMErrorDomain,err));
227 Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
229 Redistribution and use in source and binary forms, with or without modification, are permitted
230 provided that the following conditions are met:
232 * Redistributions of source code must retain the above copyright notice, this list of conditions
233 and the following disclaimer.
234 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
235 and the following disclaimer in the documentation and/or other materials provided with the
238 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
239 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
240 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
241 BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
242 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
243 PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
244 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
245 THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.