snej@0
|
1 |
//
|
snej@0
|
2 |
// MYKeychainItem.m
|
snej@0
|
3 |
// MYCrypto
|
snej@0
|
4 |
//
|
snej@0
|
5 |
// Created by Jens Alfke on 3/26/09.
|
snej@0
|
6 |
// Copyright 2009 Jens Alfke. All rights reserved.
|
snej@0
|
7 |
//
|
snej@0
|
8 |
|
snej@0
|
9 |
#import "MYKeychainItem.h"
|
snej@0
|
10 |
#import "MYCrypto_Private.h"
|
snej@0
|
11 |
#import "MYErrorUtils.h"
|
snej@0
|
12 |
|
snej@0
|
13 |
|
snej@0
|
14 |
NSString* const MYCSSMErrorDomain = @"CSSMErrorDomain";
|
snej@0
|
15 |
|
snej@0
|
16 |
|
snej@0
|
17 |
@implementation MYKeychainItem
|
snej@0
|
18 |
|
snej@0
|
19 |
|
snej@0
|
20 |
- (id) initWithKeychainItemRef: (MYKeychainItemRef)itemRef;
|
snej@0
|
21 |
{
|
snej@0
|
22 |
Assert(itemRef!=NULL);
|
snej@0
|
23 |
self = [super init];
|
snej@0
|
24 |
if (self != nil) {
|
snej@0
|
25 |
_itemRef = itemRef;
|
snej@0
|
26 |
CFRetain(_itemRef);
|
snej@0
|
27 |
}
|
snej@0
|
28 |
return self;
|
snej@0
|
29 |
}
|
snej@0
|
30 |
|
snej@0
|
31 |
|
snej@0
|
32 |
@synthesize keychainItemRef=_itemRef;
|
snej@0
|
33 |
|
snej@0
|
34 |
- (void) dealloc
|
snej@0
|
35 |
{
|
snej@0
|
36 |
if (_itemRef) CFRelease(_itemRef);
|
snej@0
|
37 |
[super dealloc];
|
snej@0
|
38 |
}
|
snej@0
|
39 |
|
snej@2
|
40 |
- (void) finalize
|
snej@2
|
41 |
{
|
snej@2
|
42 |
if (_itemRef) CFRelease(_itemRef);
|
snej@2
|
43 |
[super finalize];
|
snej@2
|
44 |
}
|
snej@2
|
45 |
|
snej@0
|
46 |
- (id) copyWithZone: (NSZone*)zone {
|
snej@0
|
47 |
// As keys are immutable, it's not necessary to make copies of them. This makes it more efficient
|
snej@0
|
48 |
// to use instances as NSDictionary keys or store them in NSSets.
|
snej@0
|
49 |
return [self retain];
|
snej@0
|
50 |
}
|
snej@0
|
51 |
|
snej@0
|
52 |
- (BOOL) isEqual: (id)obj {
|
snej@3
|
53 |
return (obj == self) ||
|
snej@3
|
54 |
([obj isKindOfClass: [MYKeychainItem class]] && CFEqual(_itemRef, [obj keychainItemRef]));
|
snej@0
|
55 |
}
|
snej@0
|
56 |
|
snej@0
|
57 |
- (NSUInteger) hash {
|
snej@0
|
58 |
return CFHash(_itemRef);
|
snej@0
|
59 |
}
|
snej@0
|
60 |
|
snej@1
|
61 |
- (NSString*) description {
|
snej@1
|
62 |
return $sprintf(@"%@[%p]", [self class], _itemRef); //FIX: Can we do anything better?
|
snej@1
|
63 |
}
|
snej@1
|
64 |
|
snej@1
|
65 |
|
snej@0
|
66 |
- (NSArray*) _itemList {
|
snej@0
|
67 |
return $array((id)_itemRef);
|
snej@0
|
68 |
}
|
snej@0
|
69 |
|
snej@2
|
70 |
#if MYCRYPTO_USE_IPHONE_API
|
snej@0
|
71 |
- (CFDictionaryRef) asQuery {
|
snej@0
|
72 |
return (CFDictionaryRef) $dict( {(id)kSecClass, (id)kSecClassKey},//FIX
|
snej@0
|
73 |
{(id)kSecMatchItemList, self._itemList} );
|
snej@0
|
74 |
}
|
snej@0
|
75 |
#endif
|
snej@0
|
76 |
|
snej@0
|
77 |
|
snej@0
|
78 |
- (MYKeychain*) keychain {
|
snej@2
|
79 |
#if MYCRYPTO_USE_IPHONE_API
|
snej@0
|
80 |
return [MYKeychain defaultKeychain];
|
snej@0
|
81 |
#else
|
snej@0
|
82 |
MYKeychain *keychain = nil;
|
snej@0
|
83 |
SecKeychainRef keychainRef = NULL;
|
snej@0
|
84 |
if (check(SecKeychainItemCopyKeychain((SecKeychainItemRef)_itemRef, &keychainRef), @"SecKeychainItemCopyKeychain")) {
|
snej@0
|
85 |
if (keychainRef) {
|
snej@0
|
86 |
keychain = [[[MYKeychain alloc] initWithKeychainRef: keychainRef] autorelease];
|
snej@0
|
87 |
CFRelease(keychainRef);
|
snej@0
|
88 |
}
|
snej@0
|
89 |
}
|
snej@0
|
90 |
return keychain;
|
snej@0
|
91 |
#endif
|
snej@0
|
92 |
}
|
snej@0
|
93 |
|
snej@0
|
94 |
- (BOOL) removeFromKeychain {
|
snej@2
|
95 |
#if MYCRYPTO_USE_IPHONE_API
|
snej@0
|
96 |
return check(SecItemDelete(self.asQuery), @"SecItemDelete");
|
snej@0
|
97 |
#else
|
snej@0
|
98 |
return check(SecKeychainItemDelete((SecKeychainItemRef)_itemRef), @"SecKeychainItemDelete");
|
snej@0
|
99 |
#endif
|
snej@0
|
100 |
}
|
snej@0
|
101 |
|
snej@0
|
102 |
|
snej@0
|
103 |
#pragma mark -
|
snej@0
|
104 |
#pragma mark DATA / METADATA ACCESSORS:
|
snej@0
|
105 |
|
snej@0
|
106 |
|
snej@0
|
107 |
- (NSData*) _getContents: (OSStatus*)outError {
|
snej@0
|
108 |
NSData *contents = nil;
|
snej@2
|
109 |
#if MYCRYPTO_USE_IPHONE_API
|
snej@0
|
110 |
#else
|
snej@0
|
111 |
UInt32 length = 0;
|
snej@0
|
112 |
void *bytes = NULL;
|
snej@0
|
113 |
*outError = SecKeychainItemCopyAttributesAndData(_itemRef, NULL, NULL, NULL, &length, &bytes);
|
snej@0
|
114 |
if (!*outError && bytes) {
|
snej@0
|
115 |
contents = [NSData dataWithBytes: bytes length: length];
|
snej@0
|
116 |
SecKeychainItemFreeAttributesAndData(NULL, bytes);
|
snej@0
|
117 |
}
|
snej@0
|
118 |
#endif
|
snej@0
|
119 |
return contents;
|
snej@0
|
120 |
}
|
snej@0
|
121 |
|
snej@0
|
122 |
+ (NSData*) _getAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
|
snej@0
|
123 |
NSData *value = nil;
|
snej@2
|
124 |
#if MYCRYPTO_USE_IPHONE_API
|
snej@0
|
125 |
NSDictionary *info = $dict( {(id)kSecClass, (id)kSecClassKey},
|
snej@0
|
126 |
{(id)kSecMatchItemList, $array((id)item)},
|
snej@0
|
127 |
{(id)kSecReturnAttributes, $true} );
|
snej@0
|
128 |
CFDictionaryRef attrs;
|
snej@0
|
129 |
if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
|
snej@0
|
130 |
return nil;
|
snej@0
|
131 |
CFTypeRef rawValue = CFDictionaryGetValue(attrs,attr);
|
snej@0
|
132 |
value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
|
snej@0
|
133 |
CFRelease(attrs);
|
snej@0
|
134 |
|
snej@0
|
135 |
#else
|
snej@0
|
136 |
UInt32 format = kSecFormatUnknown;
|
snej@0
|
137 |
SecKeychainAttributeInfo info = {.count=1, .tag=(UInt32*)&attr, .format=&format};
|
snej@0
|
138 |
SecKeychainAttributeList *list = NULL;
|
snej@0
|
139 |
|
snej@0
|
140 |
if (check(SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)item, &info,
|
snej@0
|
141 |
NULL, &list, NULL, NULL),
|
snej@0
|
142 |
@"SecKeychainItemCopyAttributesAndData")) {
|
snej@0
|
143 |
if (list) {
|
snej@0
|
144 |
if (list->count == 1)
|
snej@0
|
145 |
value = [NSData dataWithBytes: list->attr->data
|
snej@0
|
146 |
length: list->attr->length];
|
snej@0
|
147 |
else if (list->count > 1)
|
snej@0
|
148 |
Warn(@"Multiple values for keychain item attribute");
|
snej@0
|
149 |
SecKeychainItemFreeAttributesAndData(list, NULL);
|
snej@0
|
150 |
}
|
snej@0
|
151 |
}
|
snej@0
|
152 |
#endif
|
snej@0
|
153 |
return value;
|
snej@0
|
154 |
}
|
snej@0
|
155 |
|
snej@0
|
156 |
+ (NSString*) _getStringAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
|
snej@0
|
157 |
NSData *value = [self _getAttribute: attr ofItem: item];
|
snej@0
|
158 |
if (!value) return nil;
|
snej@0
|
159 |
const char *bytes = value.bytes;
|
snej@0
|
160 |
size_t length = value.length;
|
snej@0
|
161 |
if (length>0 && bytes[length-1] == 0)
|
snej@0
|
162 |
length--; // Some values are null-terminated!?
|
snej@0
|
163 |
NSString *str = [[NSString alloc] initWithBytes: bytes length: length
|
snej@0
|
164 |
encoding: NSUTF8StringEncoding];
|
snej@0
|
165 |
if (!str)
|
snej@0
|
166 |
Warn(@"MYKeychainItem: Couldn't decode attr value as string");
|
snej@0
|
167 |
return [str autorelease];
|
snej@0
|
168 |
}
|
snej@0
|
169 |
|
snej@0
|
170 |
- (NSString*) stringValueOfAttribute: (SecKeychainAttrType)attr {
|
snej@0
|
171 |
return [[self class] _getStringAttribute: attr ofItem: _itemRef];
|
snej@0
|
172 |
}
|
snej@0
|
173 |
|
snej@0
|
174 |
|
snej@0
|
175 |
+ (BOOL) _setAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item
|
snej@0
|
176 |
stringValue: (NSString*)stringValue
|
snej@0
|
177 |
{
|
snej@2
|
178 |
#if MYCRYPTO_USE_IPHONE_API
|
snej@0
|
179 |
id value = stringValue ?(id)stringValue :(id)[NSNull null];
|
snej@0
|
180 |
NSDictionary *query = $dict({(id)kSecClass, (id)kSecClassKey},
|
snej@0
|
181 |
{(id)kSecAttrKeyType, (id)attr},
|
snej@0
|
182 |
{(id)kSecMatchItemList, $array((id)item)});
|
snej@0
|
183 |
NSDictionary *attrs = $dict({(id)attr, value});
|
snej@0
|
184 |
return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
|
snej@0
|
185 |
|
snej@0
|
186 |
#else
|
snej@0
|
187 |
NSData *data = [stringValue dataUsingEncoding: NSUTF8StringEncoding];
|
snej@0
|
188 |
SecKeychainAttribute attribute = {.tag=attr, .length=data.length, .data=(void*)data.bytes};
|
snej@0
|
189 |
SecKeychainAttributeList list = {.count=1, .attr=&attribute};
|
snej@0
|
190 |
return check(SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)item, &list, 0, NULL),
|
snej@0
|
191 |
@"SecKeychainItemModifyAttributesAndData");
|
snej@0
|
192 |
#endif
|
snej@0
|
193 |
}
|
snej@0
|
194 |
|
snej@0
|
195 |
- (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
|
snej@0
|
196 |
return [[self class] _setAttribute: attr ofItem: _itemRef stringValue: valueStr];
|
snej@0
|
197 |
}
|
snej@0
|
198 |
|
snej@0
|
199 |
|
snej@0
|
200 |
@end
|
snej@0
|
201 |
|
snej@0
|
202 |
|
snej@0
|
203 |
|
snej@0
|
204 |
|
snej@0
|
205 |
BOOL check(OSStatus err, NSString *what) {
|
snej@0
|
206 |
if (err) {
|
snej@2
|
207 |
#if !MYCRYPTO_USE_IPHONE_API
|
snej@0
|
208 |
if (err < -2000000000)
|
snej@0
|
209 |
return checkcssm(err,what);
|
snej@0
|
210 |
#endif
|
snej@0
|
211 |
Warn(@"MYCrypto error, %@: %@", what, MYErrorName(NSOSStatusErrorDomain,err));
|
snej@0
|
212 |
return NO;
|
snej@0
|
213 |
} else
|
snej@0
|
214 |
return YES;
|
snej@0
|
215 |
}
|
snej@0
|
216 |
|
snej@2
|
217 |
#if !MYCRYPTO_USE_IPHONE_API
|
snej@0
|
218 |
BOOL checkcssm(CSSM_RETURN err, NSString *what) {
|
snej@0
|
219 |
if (err != CSSM_OK) {
|
snej@0
|
220 |
Warn(@"MYCrypto error, %@: %@", what, MYErrorName(MYCSSMErrorDomain,err));
|
snej@0
|
221 |
return NO;
|
snej@0
|
222 |
} else
|
snej@0
|
223 |
return YES;
|
snej@0
|
224 |
}
|
snej@0
|
225 |
#endif
|