nicklockwood/FXKeychain

Updating key fails in simulator with ios 7.1B5 (works fine on device)

Opened this issue · 3 comments

Don't think this is an FXKeyChain issue but for info - updating a key fails with a -25300 error (errSecItemNotFound).

Sample code (as per radar which is a cut down version of FXKeyChain attached below)

/// ---------------------------------------------------------

//
//  ViewController.m
//  KeychainBugSample
//
//  Created by Andy Qua on 12/02/2014.
//  Copyright (c) 2014 Andy Qua. All rights reserved.
//

#import "ViewController.h"


/*******
 * Sample demonstrating a bug with updating a value stored in the KeyChain.
 * When running on Simulator this fails with a -25300 error (errSecItemNotFound)
 * But works fine when running on device
 *
 * Note this is simplified keychain code and assumes that you will be saving and retrieving an NSString value
 *******/

@interface ViewController ()
{
    NSString *service;
}
@end

@implementation ViewController

- (void)viewDidLoad
{
    [super viewDidLoad];

    service = @"test";
    NSString *key = @"MyKey";
    NSString *text;

    // Key shouldn't exist
    text = [self stringForKey:key];
    NSLog( @"Before add - value stored - [%@]", text );

    // Set value
    [self addString:@"Test" forKey:key];

    // Make sure that value has been set correctly
    text = [self stringForKey:key];
    NSLog( @"After initial add - value stored - [%@]", text );

    // Now Update the value - This will log error  -25300 (keyNotFound)
    [self updateString:@"This has changed" forKey:key];

    // Make sure that value has been set correctly
    text = [self stringForKey:key];
    NSLog( @"After update value - [%@]", text );

    // Clear outs data from keychain for next run
    [self deleteDataForKey:key];
    text = [self stringForKey:key];
    NSLog( @"After delete value - [%@]", text );
}

- (void)didReceiveMemoryWarning
{
    [super didReceiveMemoryWarning];
    // Dispose of any resources that can be recreated.
}


- (NSString *)stringForKey:(id)key
{
    //generate query
    NSMutableDictionary *query = [NSMutableDictionary dictionary];
    if ([service length]) query[(__bridge NSString *)kSecAttrService] = service;
    query[(__bridge NSString *)kSecClass] = (__bridge id)kSecClassGenericPassword;
    query[(__bridge NSString *)kSecMatchLimit] = (__bridge id)kSecMatchLimitOne;
    query[(__bridge NSString *)kSecReturnData] = (__bridge id)kCFBooleanTrue;
    query[(__bridge NSString *)kSecAttrAccount] = [key description];

    //recover data
    CFDataRef data = NULL;
    OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&data);
    if (status != errSecSuccess && status != errSecItemNotFound)
    {
        NSLog(@"Failed to retrieve data for key '%@', error: %ld", key, (long)status);
    }

    NSData *dataObj = CFBridgingRelease(data);
    NSString *text = [[NSString alloc] initWithData:dataObj encoding:NSUTF8StringEncoding];

    return text;
}


- (BOOL) addString:(NSString *)string forKey:(NSString *)key
{
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];

    NSMutableDictionary *query = [NSMutableDictionary dictionary];
    if ([service length]) query[(__bridge NSString *)kSecAttrService] = service;
    query[(__bridge NSString *)kSecClass] = (__bridge id)kSecClassGenericPassword;
    query[(__bridge NSString *)kSecAttrAccount] = [key description];
    query[(__bridge NSString *)kSecValueData] = data;

    OSStatus status = SecItemAdd ((__bridge CFDictionaryRef)query, NULL);

    if (status != errSecSuccess)
    {
        NSLog(@"Failed to add data for key '%@', error: %ld", key, (long)status);
        return NO;
    }

    return YES;
}

- (BOOL) updateString:(NSString *)string forKey:(NSString *)key
{
    NSData *data = [string dataUsingEncoding:NSUTF8StringEncoding];

    NSMutableDictionary *query = [NSMutableDictionary dictionary];
    if ([service length]) query[(__bridge NSString *)kSecAttrService] = service;
    query[(__bridge NSString *)kSecClass] = (__bridge id)kSecClassGenericPassword;
    query[(__bridge NSString *)kSecAttrAccount] = [key description];

    NSMutableDictionary *update = [@{(__bridge NSString *)kSecValueData: data} mutableCopy];

    OSStatus status = SecItemUpdate((__bridge CFDictionaryRef)query, (__bridge CFDictionaryRef)update);

    if (status != errSecSuccess)
    {
        NSLog(@"Failed to update data for key '%@', error: %ld", key, (long)status);
        return NO;
    }

    return YES;
}

- (BOOL)deleteDataForKey:(id)key
{
    //generate query
    NSMutableDictionary *query = [NSMutableDictionary dictionary];
    if ([service length]) query[(__bridge NSString *)kSecAttrService] = service;
    query[(__bridge NSString *)kSecClass] = (__bridge id)kSecClassGenericPassword;
    query[(__bridge NSString *)kSecAttrAccount] = [key description];

    //delete existing data

    OSStatus status = SecItemDelete((__bridge CFDictionaryRef)query);
    if (status != errSecSuccess)
    {
        NSLog(@"Failed to delete data for key '%@', error: %ld", key, (long)status);
        return NO;
    }

    return YES;
}

@end
/// ---------------------------------------------------------

Would be really awesome if you could add some formatting to that code!

Did you ever get a response from Apple on this? Is the issues still happening?

Hi Nick,

Bug was closed as a dupe but the issue had not been fixed in 7.1 - although haven't yet tested with latest version as worked around the issue for the moment.

Will do soon though

Andy

Sent from my iPhone

On 30 May 2014, at 10:56, Nick Lockwood notifications@github.com wrote:

Did you ever get a response from Apple on this? Is the issues still happening?


Reply to this email directly or view it on GitHub.