MYKeychainItem.m
author Jens Alfke <jens@mooseyard.com>
Fri Jun 05 08:57:18 2009 -0700 (2009-06-05)
changeset 20 df9da0f6b358
parent 13 6fd9177eb6da
child 23 39fec79de6e8
permissions -rw-r--r--
Factored out the name accessors of MYParsedCertificate into a new class MYCertificateName, so that both subject and issuer can be accessed. A bit of other cleanup too.
     1 //
     2 //  MYKeychainItem.m
     3 //  MYCrypto
     4 //
     5 //  Created by Jens Alfke on 3/26/09.
     6 //  Copyright 2009 Jens Alfke. All rights reserved.
     7 //
     8 
     9 #import "MYKeychainItem.h"
    10 #import "MYCrypto_Private.h"
    11 #import "MYErrorUtils.h"
    12 
    13 
    14 NSString* const MYCSSMErrorDomain = @"CSSMErrorDomain";
    15 
    16 
    17 @implementation MYKeychainItem
    18 
    19 
    20 - (id) initWithKeychainItemRef: (MYKeychainItemRef)itemRef;
    21 {
    22     Assert(itemRef!=NULL);
    23     self = [super init];
    24     if (self != nil) {
    25         _itemRef = itemRef;
    26         CFRetain(_itemRef);
    27     }
    28     return self;
    29 }
    30 
    31 
    32 @synthesize keychainItemRef=_itemRef;
    33 
    34 - (void) dealloc
    35 {
    36     if (_itemRef) CFRelease(_itemRef);
    37     [super dealloc];
    38 }
    39 
    40 - (void) finalize
    41 {
    42     if (_itemRef) CFRelease(_itemRef);
    43     [super finalize];
    44 }
    45 
    46 - (id) copyWithZone: (NSZone*)zone {
    47     // As keys are immutable, it's not necessary to make copies of them. This makes it more efficient
    48     // to use instances as NSDictionary keys or store them in NSSets.
    49     return [self retain];
    50 }
    51 
    52 - (BOOL) isEqual: (id)obj {
    53     return (obj == self) ||
    54            ([obj isKindOfClass: [MYKeychainItem class]] && CFEqual(_itemRef, [obj keychainItemRef]));
    55 }
    56 
    57 - (NSUInteger) hash {
    58     return CFHash(_itemRef);
    59 }
    60 
    61 - (NSString*) description {
    62     return $sprintf(@"%@[%p]", [self class], _itemRef);     //FIX: Can we do anything better?
    63 }
    64 
    65 
    66 - (NSArray*) _itemList {
    67     return $array((id)_itemRef);
    68 }
    69 
    70 #if MYCRYPTO_USE_IPHONE_API
    71 - (CFDictionaryRef) asQuery {
    72     return (CFDictionaryRef) $dict( {(id)kSecClass, (id)kSecClassKey},//FIX
    73                                     {(id)kSecMatchItemList, self._itemList} );
    74 }
    75 #endif
    76 
    77 
    78 - (MYKeychain*) keychain {
    79 #if MYCRYPTO_USE_IPHONE_API
    80     return [MYKeychain defaultKeychain];
    81 #else
    82     MYKeychain *keychain = nil;
    83     SecKeychainRef keychainRef = NULL;
    84     if (check(SecKeychainItemCopyKeychain((SecKeychainItemRef)_itemRef, &keychainRef), @"SecKeychainItemCopyKeychain")) {
    85         if (keychainRef) {
    86             keychain = [[[MYKeychain alloc] initWithKeychainRef: keychainRef] autorelease];
    87             CFRelease(keychainRef);
    88         }
    89     }
    90     return keychain;
    91 #endif
    92 }
    93 
    94 - (BOOL) removeFromKeychain {
    95     OSStatus err;
    96 #if MYCRYPTO_USE_IPHONE_API
    97     err = SecItemDelete(self.asQuery);
    98 #else
    99     err = SecKeychainItemDelete((SecKeychainItemRef)_itemRef);
   100     if (err==errSecInvalidItemRef)
   101         return YES;     // result for an item that's not in a keychain
   102 #endif
   103     return err==errSecItemNotFound || check(err, @"SecKeychainItemDelete");
   104 }
   105 
   106 
   107 #pragma mark -
   108 #pragma mark DATA / METADATA ACCESSORS:
   109 
   110 
   111 - (NSData*) _getContents: (OSStatus*)outError {
   112     NSData *contents = nil;
   113 #if MYCRYPTO_USE_IPHONE_API
   114 #else
   115 	UInt32 length = 0;
   116     void *bytes = NULL;
   117     *outError = SecKeychainItemCopyAttributesAndData(_itemRef, NULL, NULL, NULL, &length, &bytes);
   118     if (!*outError && bytes) {
   119         contents = [NSData dataWithBytes: bytes length: length];
   120         SecKeychainItemFreeAttributesAndData(NULL, bytes);
   121     }
   122 #endif
   123     return contents;
   124 }
   125 
   126 + (NSData*) _getAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
   127     NSData *value = nil;
   128 #if MYCRYPTO_USE_IPHONE_API
   129     NSDictionary *info = $dict( {(id)kSecClass, (id)kSecClassKey},
   130                                 {(id)kSecMatchItemList, $array((id)item)},
   131                                 {(id)kSecReturnAttributes, $true} );
   132     CFDictionaryRef attrs;
   133     if (!check(SecItemCopyMatching((CFDictionaryRef)info, (CFTypeRef*)&attrs), @"SecItemCopyMatching"))
   134         return nil;
   135     CFTypeRef rawValue = CFDictionaryGetValue(attrs,attr);
   136     value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
   137     CFRelease(attrs);
   138     
   139 #else
   140 	UInt32 format = kSecFormatUnknown;
   141 	SecKeychainAttributeInfo info = {.count=1, .tag=(UInt32*)&attr, .format=&format};
   142     SecKeychainAttributeList *list = NULL;
   143 	
   144     if (check(SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)item, &info,
   145                                                    NULL, &list, NULL, NULL),
   146               @"SecKeychainItemCopyAttributesAndData")) {
   147         if (list) {
   148             if (list->count == 1)
   149                 value = [NSData dataWithBytes: list->attr->data
   150                                        length: list->attr->length];
   151             else if (list->count > 1)
   152                 Warn(@"Multiple values for keychain item attribute");
   153             SecKeychainItemFreeAttributesAndData(list, NULL);
   154         }
   155     }
   156 #endif
   157     return value;
   158 }
   159 
   160 + (NSString*) _getStringAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
   161     NSData *value = [self _getAttribute: attr ofItem: item];
   162     if (!value) return nil;
   163     const char *bytes = value.bytes;
   164     size_t length = value.length;
   165     if (length>0 && bytes[length-1] == 0)
   166         length--;           // Some values are null-terminated!?
   167     NSString *str = [[NSString alloc] initWithBytes: bytes length: length
   168                                            encoding: NSUTF8StringEncoding];
   169     if (!str)
   170         Warn(@"MYKeychainItem: Couldn't decode attr value as string");
   171     return [str autorelease];
   172 }
   173 
   174 - (NSString*) stringValueOfAttribute: (SecKeychainAttrType)attr {
   175     return [[self class] _getStringAttribute: attr ofItem: _itemRef];
   176 }
   177 
   178 
   179 + (BOOL) _setAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item
   180            stringValue: (NSString*)stringValue
   181 {
   182 #if MYCRYPTO_USE_IPHONE_API
   183     id value = stringValue ?(id)stringValue :(id)[NSNull null];
   184     NSDictionary *query = $dict({(id)kSecClass, (id)kSecClassKey},
   185                                 {(id)kSecAttrKeyType, (id)attr},
   186                                 {(id)kSecMatchItemList, $array((id)item)});
   187     NSDictionary *attrs = $dict({(id)attr, value});
   188     return check(SecItemUpdate((CFDictionaryRef)query, (CFDictionaryRef)attrs), @"SecItemUpdate");
   189     
   190 #else
   191     NSData *data = [stringValue dataUsingEncoding: NSUTF8StringEncoding];
   192     SecKeychainAttribute attribute = {.tag=attr, .length=data.length, .data=(void*)data.bytes};
   193 	SecKeychainAttributeList list = {.count=1, .attr=&attribute};
   194     return check(SecKeychainItemModifyAttributesAndData((SecKeychainItemRef)item, &list, 0, NULL),
   195                  @"SecKeychainItemModifyAttributesAndData");
   196 #endif
   197 }
   198 
   199 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
   200     return [[self class] _setAttribute: attr ofItem: _itemRef stringValue: valueStr];
   201 }
   202 
   203 
   204 @end
   205 
   206 
   207 
   208 
   209 BOOL check(OSStatus err, NSString *what) {
   210     if (err) {
   211 #if !MYCRYPTO_USE_IPHONE_API
   212         if (err < -2000000000)
   213             return checkcssm(err,what);
   214 #endif
   215         Warn(@"MYCrypto error, %@: %@", what, MYErrorName(NSOSStatusErrorDomain,err));
   216         return NO;
   217     } else
   218         return YES;
   219 }
   220 
   221 #if !MYCRYPTO_USE_IPHONE_API
   222 BOOL checkcssm(CSSM_RETURN err, NSString *what) {
   223     if (err != CSSM_OK) {
   224         Warn(@"MYCrypto error, %@: %@", what, MYErrorName(MYCSSMErrorDomain,err));
   225         return NO;
   226     } else
   227         return YES;
   228 }
   229 #endif
   230 
   231 
   232 
   233 /*
   234  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   235  
   236  Redistribution and use in source and binary forms, with or without modification, are permitted
   237  provided that the following conditions are met:
   238  
   239  * Redistributions of source code must retain the above copyright notice, this list of conditions
   240  and the following disclaimer.
   241  * Redistributions in binary form must reproduce the above copyright notice, this list of conditions
   242  and the following disclaimer in the documentation and/or other materials provided with the
   243  distribution.
   244  
   245  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR
   246  IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 
   247  FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI-
   248  BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
   249  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 
   250   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 
   251  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 
   252  THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
   253  */