1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
1.2 +++ b/MYKeychainItem.m Sat Apr 04 20:42:03 2009 -0700
1.3 @@ -0,0 +1,216 @@
1.4 +//
1.5 +// MYKeychainItem.m
1.6 +// MYCrypto
1.7 +//
1.8 +// Created by Jens Alfke on 3/26/09.
1.9 +// Copyright 2009 Jens Alfke. All rights reserved.
1.10 +//
1.11 +
1.12 +#import "MYKeychainItem.h"
1.13 +#import "MYCrypto_Private.h"
1.14 +#import "MYErrorUtils.h"
1.15 +
1.16 +
1.17 +NSString* const MYCSSMErrorDomain = @"CSSMErrorDomain";
1.18 +
1.19 +
1.20 +@implementation MYKeychainItem
1.21 +
1.22 +
1.23 +- (id) initWithKeychainItemRef: (MYKeychainItemRef)itemRef;
1.24 +{
1.25 + Assert(itemRef!=NULL);
1.26 + self = [super init];
1.27 + if (self != nil) {
1.28 + _itemRef = itemRef;
1.29 + CFRetain(_itemRef);
1.30 + }
1.31 + return self;
1.32 +}
1.33 +
1.34 +
1.35 +@synthesize keychainItemRef=_itemRef;
1.36 +
1.37 +- (void) dealloc
1.38 +{
1.39 + if (_itemRef) CFRelease(_itemRef);
1.40 + [super dealloc];
1.41 +}
1.42 +
1.43 +- (id) copyWithZone: (NSZone*)zone {
1.44 + // As keys are immutable, it's not necessary to make copies of them. This makes it more efficient
1.45 + // to use instances as NSDictionary keys or store them in NSSets.
1.46 + return [self retain];
1.47 +}
1.48 +
1.49 +- (BOOL) isEqual: (id)obj {
1.50 + // Require the objects to be of the same class, so that a MYPublicKey will not be equal to a
1.51 + // MYKeyPair with the same public key.
1.52 + return (obj == self) ||
1.53 + ([obj class] == [self class] && CFEqual(_itemRef, [obj keychainItemRef]));
1.54 +}
1.55 +
1.56 +- (NSUInteger) hash {
1.57 + return CFHash(_itemRef);
1.58 +}
1.59 +
1.60 +- (NSArray*) _itemList {
1.61 + return $array((id)_itemRef);
1.62 +}
1.63 +
1.64 +#if USE_IPHONE_API
1.65 +- (CFDictionaryRef) asQuery {
1.66 + return (CFDictionaryRef) $dict( {(id)kSecClass, (id)kSecClassKey},//FIX
1.67 + {(id)kSecMatchItemList, self._itemList} );
1.68 +}
1.69 +#endif
1.70 +
1.71 +
1.72 +- (MYKeychain*) keychain {
1.73 +#if USE_IPHONE_API
1.74 + return [MYKeychain defaultKeychain];
1.75 +#else
1.76 + MYKeychain *keychain = nil;
1.77 + SecKeychainRef keychainRef = NULL;
1.78 + if (check(SecKeychainItemCopyKeychain((SecKeychainItemRef)_itemRef, &keychainRef), @"SecKeychainItemCopyKeychain")) {
1.79 + if (keychainRef) {
1.80 + keychain = [[[MYKeychain alloc] initWithKeychainRef: keychainRef] autorelease];
1.81 + CFRelease(keychainRef);
1.82 + }
1.83 + }
1.84 + return keychain;
1.85 +#endif
1.86 +}
1.87 +
1.88 +- (BOOL) removeFromKeychain {
1.89 +#if USE_IPHONE_API
1.90 + return check(SecItemDelete(self.asQuery), @"SecItemDelete");
1.91 +#else
1.92 + return check(SecKeychainItemDelete((SecKeychainItemRef)_itemRef), @"SecKeychainItemDelete");
1.93 +#endif
1.94 +}
1.95 +
1.96 +
1.97 +#pragma mark -
1.98 +#pragma mark DATA / METADATA ACCESSORS:
1.99 +
1.100 +
1.101 +- (NSData*) _getContents: (OSStatus*)outError {
1.102 + NSData *contents = nil;
1.103 +#if USE_IPHONE_API
1.104 +#else
1.105 + UInt32 length = 0;
1.106 + void *bytes = NULL;
1.107 + *outError = SecKeychainItemCopyAttributesAndData(_itemRef, NULL, NULL, NULL, &length, &bytes);
1.108 + if (!*outError && bytes) {
1.109 + contents = [NSData dataWithBytes: bytes length: length];
1.110 + SecKeychainItemFreeAttributesAndData(NULL, bytes);
1.111 + }
1.112 +#endif
1.113 + return contents;
1.114 +}
1.115 +
1.116 ++ (NSData*) _getAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
1.117 + NSData *value = nil;
1.118 +#if USE_IPHONE_API
1.119 + NSDictionary *info = $dict( {(id)kSecClass, (id)kSecClassKey},
1.120 + {(id)kSecMatchItemList, $array((id)item)},
1.121 + {(id)kSecReturnAttributes, $true} );
1.122 + CFDictionaryRef attrs;
1.123 + if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
1.124 + return nil;
1.125 + CFTypeRef rawValue = CFDictionaryGetValue(attrs,attr);
1.126 + value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
1.127 + CFRelease(attrs);
1.128 +
1.129 +#else
1.130 + UInt32 format = kSecFormatUnknown;
1.131 + SecKeychainAttributeInfo info = {.count=1, .tag=(UInt32*)&attr, .format=&format};
1.132 + SecKeychainAttributeList *list = NULL;
1.133 +
1.134 + if (check(SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)item, &info,
1.135 + NULL, &list, NULL, NULL),
1.136 + @"SecKeychainItemCopyAttributesAndData")) {
1.137 + if (list) {
1.138 + if (list->count == 1)
1.139 + value = [NSData dataWithBytes: list->attr->data
1.140 + length: list->attr->length];
1.141 + else if (list->count > 1)
1.142 + Warn(@"Multiple values for keychain item attribute");
1.143 + SecKeychainItemFreeAttributesAndData(list, NULL);
1.144 + }
1.145 + }
1.146 +#endif
1.147 + return value;
1.148 +}
1.149 +
1.150 ++ (NSString*) _getStringAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
1.151 + NSData *value = [self _getAttribute: attr ofItem: item];
1.152 + if (!value) return nil;
1.153 + const char *bytes = value.bytes;
1.154 + size_t length = value.length;
1.155 + if (length>0 && bytes[length-1] == 0)
1.156 + length--; // Some values are null-terminated!?
1.157 + NSString *str = [[NSString alloc] initWithBytes: bytes length: length
1.158 + encoding: NSUTF8StringEncoding];
1.159 + if (!str)
1.160 + Warn(@"MYKeychainItem: Couldn't decode attr value as string");
1.161 + return [str autorelease];
1.162 +}
1.163 +
1.164 +- (NSString*) stringValueOfAttribute: (SecKeychainAttrType)attr {
1.165 + return [[self class] _getStringAttribute: attr ofItem: _itemRef];
1.166 +}
1.167 +
1.168 +
1.169 ++ (BOOL) _setAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item
1.170 + stringValue: (NSString*)stringValue
1.171 +{
1.172 +#if USE_IPHONE_API
1.173 + id value = stringValue ?(id)stringValue :(id)[NSNull null];
1.174 + NSDictionary *query = $dict({(id)kSecClass, (id)kSecClassKey},
1.175 + {(id)kSecAttrKeyType, (id)attr},
1.176 + {(id)kSecMatchItemList, $array((id)item)});
1.177 + NSDictionary *attrs = $dict({(id)attr, value});
1.178 + return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
1.179 +
1.180 +#else
1.181 + NSData *data = [stringValue dataUsingEncoding: NSUTF8StringEncoding];
1.182 + SecKeychainAttribute attribute = {.tag=attr, .length=data.length, .data=(void*)data.bytes};
1.183 + SecKeychainAttributeList list = {.count=1, .attr=&attribute};
1.184 + return check(SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)item, &list, 0, NULL),
1.185 + @"SecKeychainItemModifyAttributesAndData");
1.186 +#endif
1.187 +}
1.188 +
1.189 +- (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
1.190 + return [[self class] _setAttribute: attr ofItem: _itemRef stringValue: valueStr];
1.191 +}
1.192 +
1.193 +
1.194 +@end
1.195 +
1.196 +
1.197 +
1.198 +
1.199 +BOOL check(OSStatus err, NSString *what) {
1.200 + if (err) {
1.201 +#if !USE_IPHONE_API
1.202 + if (err < -2000000000)
1.203 + return checkcssm(err,what);
1.204 +#endif
1.205 + Warn(@"MYCrypto error, %@: %@", what, MYErrorName(NSOSStatusErrorDomain,err));
1.206 + return NO;
1.207 + } else
1.208 + return YES;
1.209 +}
1.210 +
1.211 +#if !USE_IPHONE_API
1.212 +BOOL checkcssm(CSSM_RETURN err, NSString *what) {
1.213 + if (err != CSSM_OK) {
1.214 + Warn(@"MYCrypto error, %@: %@", what, MYErrorName(MYCSSMErrorDomain,err));
1.215 + return NO;
1.216 + } else
1.217 + return YES;
1.218 +}
1.219 +#endif