FXKeychain icon indicating copy to clipboard operation
FXKeychain copied to clipboard

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

Open AndyQ opened this issue 12 years ago • 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
/// ---------------------------------------------------------

AndyQ avatar Feb 12 '14 12:02 AndyQ

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

zackbraksa avatar Apr 10 '14 11:04 zackbraksa

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

nicklockwood avatar May 30 '14 08:05 nicklockwood

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 [email protected] 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.

AndyQ avatar May 30 '14 14:05 AndyQ