MYKeychainItem.m
author Jens Alfke <jens@mooseyard.com>
Tue Jun 09 23:58:03 2009 -0700 (2009-06-09)
changeset 24 6856e071d25a
parent 23 39fec79de6e8
child 26 d9c2a06d4e4e
permissions -rw-r--r--
* More work on iPhone compatibility.
* Restored the signature-verification code to MYCertInfo, which I'd removed earlier. I now need it to verify self-signed certs, since the Security framework won't do it for me.
* Merged MYCertificate-iPhone.m into MYCertificate.m since there's more shared code now.
     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         LogTo(INIT,@"%@, _itemRef=%@", [self class], itemRef);
    28     }
    29     return self;
    30 }
    31 
    32 
    33 @synthesize keychainItemRef=_itemRef;
    34 
    35 - (void) dealloc
    36 {
    37     if (_itemRef) CFRelease(_itemRef);
    38     [super dealloc];
    39 }
    40 
    41 - (void) finalize
    42 {
    43     if (_itemRef) CFRelease(_itemRef);
    44     [super finalize];
    45 }
    46 
    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.
    50     return [self retain];
    51 }
    52 
    53 - (BOOL) isEqual: (id)obj {
    54     return (obj == self) ||
    55            ([obj isKindOfClass: [MYKeychainItem class]] && CFEqual(_itemRef, [obj keychainItemRef]));
    56 }
    57 
    58 - (NSUInteger) hash {
    59     return CFHash(_itemRef);
    60 }
    61 
    62 - (NSString*) description {
    63     return $sprintf(@"%@[%p]", [self class], _itemRef);     //FIX: Can we do anything better?
    64 }
    65 
    66 
    67 - (NSArray*) _itemList {
    68     return $array((id)_itemRef);
    69 }
    70 
    71 
    72 - (MYKeychain*) keychain {
    73 #if MYCRYPTO_USE_IPHONE_API
    74     return [MYKeychain defaultKeychain];
    75 #else
    76     MYKeychain *keychain = nil;
    77     SecKeychainRef keychainRef = NULL;
    78     if (check(SecKeychainItemCopyKeychain((SecKeychainItemRef)_itemRef, &keychainRef), @"SecKeychainItemCopyKeychain")) {
    79         if (keychainRef) {
    80             keychain = [[[MYKeychain alloc] initWithKeychainRef: keychainRef] autorelease];
    81             CFRelease(keychainRef);
    82         }
    83     }
    84     return keychain;
    85 #endif
    86 }
    87 
    88 - (BOOL) removeFromKeychain {
    89     OSStatus err;
    90 #if MYCRYPTO_USE_IPHONE_API
    91     err = SecItemDelete((CFDictionaryRef) $dict( {(id)kSecValueRef, (id)_itemRef} ));
    92 #else
    93     err = SecKeychainItemDelete((SecKeychainItemRef)_itemRef);
    94     if (err==errSecInvalidItemRef)
    95         return YES;     // result for an item that's not in a keychain
    96 #endif
    97     return err==errSecItemNotFound || check(err, @"SecKeychainItemDelete");
    98 }
    99 
   100 
   101 #pragma mark -
   102 #pragma mark DATA / METADATA ACCESSORS:
   103 
   104 
   105 - (NSData*) _getContents: (OSStatus*)outError {
   106     NSData *contents = nil;
   107 #if MYCRYPTO_USE_IPHONE_API
   108 #else
   109 	UInt32 length = 0;
   110     void *bytes = NULL;
   111     *outError = SecKeychainItemCopyAttributesAndData(_itemRef, NULL, NULL, NULL, &length, &bytes);
   112     if (!*outError && bytes) {
   113         contents = [NSData dataWithBytes: bytes length: length];
   114         SecKeychainItemFreeAttributesAndData(NULL, bytes);
   115     }
   116 #endif
   117     return contents;
   118 }
   119 
   120 + (NSData*) _getAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item {
   121     NSData *value = nil;
   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"))
   127         return nil;
   128     CFTypeRef rawValue = CFDictionaryGetValue(attrs,attr);
   129     value = rawValue ?[[(id)CFMakeCollectable(rawValue) retain] autorelease] :nil;
   130     CFRelease(attrs);
   131     
   132 #else
   133 	UInt32 format = kSecFormatUnknown;
   134 	SecKeychainAttributeInfo info = {.count=1, .tag=(UInt32*)&attr, .format=&format};
   135     SecKeychainAttributeList *list = NULL;
   136 	
   137     if (check(SecKeychainItemCopyAttributesAndData((SecKeychainItemRef)item, &info,
   138                                                    NULL, &list, NULL, NULL),
   139               @"SecKeychainItemCopyAttributesAndData")) {
   140         if (list) {
   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);
   147         }
   148     }
   149 #endif
   150     return value;
   151 }
   152 
   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];
   162     if (!str)
   163         Warn(@"MYKeychainItem: Couldn't decode attr value as string");
   164     return [str autorelease];
   165 }
   166 
   167 - (NSString*) stringValueOfAttribute: (SecKeychainAttrType)attr {
   168     return [[self class] _getStringAttribute: attr ofItem: _itemRef];
   169 }
   170 
   171 
   172 + (BOOL) _setAttribute: (SecKeychainAttrType)attr ofItem: (MYKeychainItemRef)item
   173            stringValue: (NSString*)stringValue
   174 {
   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");
   180     
   181 #else
   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");
   187 #endif
   188 }
   189 
   190 - (BOOL) setValue: (NSString*)valueStr ofAttribute: (SecKeychainAttrType)attr {
   191     return [[self class] _setAttribute: attr ofItem: _itemRef stringValue: valueStr];
   192 }
   193 
   194 
   195 @end
   196 
   197 
   198 
   199 
   200 BOOL check(OSStatus err, NSString *what) {
   201     if (err) {
   202 #if !MYCRYPTO_USE_IPHONE_API
   203         if (err < -2000000000)
   204             return checkcssm(err,what);
   205 #endif
   206         Warn(@"MYCrypto error, %@: %@", what, MYErrorName(NSOSStatusErrorDomain,err));
   207         if (err==-50)
   208             [NSException raise: NSGenericException format: @"%@ failed with paramErr (-50)",what];
   209         return NO;
   210     } else
   211         return YES;
   212 }
   213 
   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));
   218         return NO;
   219     } else
   220         return YES;
   221 }
   222 #endif
   223 
   224 
   225 
   226 /*
   227  Copyright (c) 2009, Jens Alfke <jens@mooseyard.com>. All rights reserved.
   228  
   229  Redistribution and use in source and binary forms, with or without modification, are permitted
   230  provided that the following conditions are met:
   231  
   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
   236  distribution.
   237  
   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.
   246  */