MYKeychainItem.m
changeset 0 0a6527af039b
child 1 60e4cbbb5128
     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