From 639c497230b95a69feea20bcc23284db67f8a4c7 Mon Sep 17 00:00:00 2001 From: Manjunath Basaralu Srinivasa Date: Thu, 3 Nov 2016 10:42:00 -0700 Subject: [PATCH 1/2] Changes include setting a label different from service and APIs to perform this while adding a new keychain item and retrieving password and password data for items using label-account-service. --- Sources/SAMKeychain.h | 60 +++++++++++++++++++++++++++++++++ Sources/SAMKeychain.m | 69 +++++++++++++++++++++++++++++++++++++- Sources/SAMKeychainQuery.m | 9 ++--- 3 files changed, 133 insertions(+), 5 deletions(-) diff --git a/Sources/SAMKeychain.h b/Sources/SAMKeychain.h index 2af305d..b868b87 100644 --- a/Sources/SAMKeychain.h +++ b/Sources/SAMKeychain.h @@ -80,6 +80,34 @@ extern NSString *const kSAMKeychainWhereKey; + (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account; + (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none))); +/** + Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a + password for the given parameters. + + @param serviceName The service for which to return the corresponding password. + + @param account The account for which to return the corresponding password. + + @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't + have a password for the given parameters. + */ ++ (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label; ++ (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError **)error __attribute__((swift_error(none))); + +/** + Returns a nsdata containing the password for a given account and service, or `nil` if the Keychain doesn't have a + password for the given parameters. + + @param serviceName The service for which to return the corresponding password. + + @param account The account for which to return the corresponding password. + + @return Returns a nsdata containing the password for a given account and service, or `nil` if the Keychain doesn't + have a password for the given parameters. + */ ++ (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label; ++ (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError **)error __attribute__((swift_error(none))); + /** Returns a nsdata containing the password for a given account and service, or `nil` if the Keychain doesn't have a password for the given parameters. @@ -122,6 +150,38 @@ extern NSString *const kSAMKeychainWhereKey; + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account; + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none))); +/** + Sets a password in the Keychain. + + @param password The password to store in the Keychain. + + @param serviceName The service for which to set the corresponding password (where field n UI). + + @param account The account for which to set the corresponding password. + + @param label The label set to the keychain item. This is what is displayed in the name field of a keychain item. + + @return Returns `YES` on success, or `NO` on failure. + */ ++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label; ++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError *__autoreleasing *)error; + +/** + Sets a password in the Keychain. + + @param password The password to store in the Keychain. + + @param serviceName The service for which to set the corresponding password. + + @param account The account for which to set the corresponding password. + + @param label The label set to the keychain item. This is what is displayed in the name field of a keychain item. + + @return Returns `YES` on success, or `NO` on failure. + */ ++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label; ++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError **)error __attribute__((swift_error(none))); + /** Sets a password in the Keychain. diff --git a/Sources/SAMKeychain.m b/Sources/SAMKeychain.m index 6d01d3d..9058d66 100644 --- a/Sources/SAMKeychain.m +++ b/Sources/SAMKeychain.m @@ -37,6 +37,48 @@ + (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSStr return query.password; } ++ (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label { + return [self passwordForService:serviceName account:account label:label error:nil]; +} + + ++ (nullable NSString *)passwordForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError *__autoreleasing *)error { + SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init]; + query.label = label; + query.service = serviceName; + query.account = account; + [query fetch:error]; + return query.password; +} + ++ (nullable NSData *)passwordDataForLabel:(NSString *)serviceName account:(NSString *)account { + return [self passwordDataForService:serviceName account:account error:nil]; +} + ++ (nullable NSData *)passwordDataForLabel:(NSString *)label service:(NSString *)serviceName account:(NSString *)account error:(NSError **)error { + SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init]; + query.label = label; + query.service = serviceName; + query.account = account; + [query fetch:error]; + + return query.passwordData; +} + ++ (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account label:(nonnull NSString *)label{ + return [self passwordDataForService:serviceName account:account label:label error:nil]; +} + ++ (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account label:(nonnull NSString *)label error:(NSError * _Nullable __autoreleasing * _Nullable)error { + SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init]; + query.label = label; + query.service = serviceName; + query.account = account; + [query fetch:error]; + + return query.passwordData; +} + + (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account { return [self passwordDataForService:serviceName account:account error:nil]; } @@ -50,7 +92,6 @@ + (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSS return query.passwordData; } - + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account { return [self deletePasswordForService:serviceName account:account error:nil]; } @@ -63,11 +104,22 @@ + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)acc return [query deleteItem:error]; } ++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label{ + return [self setPassword:password forService:serviceName account:account label:label error:nil]; +} + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account { return [self setPassword:password forService:serviceName account:account error:nil]; } ++ (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError *__autoreleasing *)error { + SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init]; + query.service = serviceName; + query.label = label; + query.account = account; + query.password = password; + return [query save:error]; +} + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName account:(NSString *)account error:(NSError *__autoreleasing *)error { SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init]; @@ -77,6 +129,21 @@ + (BOOL)setPassword:(NSString *)password forService:(NSString *)serviceName acco return [query save:error]; } ++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label{ + return [self setPasswordData:password forService:serviceName account:account label:label error:nil]; +} + + ++ (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError **)error{ + SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init]; + query.label = label; + query.service = serviceName; + query.account = account; + query.passwordData = password; + return [query save:error]; +} + + + (BOOL)setPasswordData:(NSData *)password forService:(NSString *)serviceName account:(NSString *)account { return [self setPasswordData:password forService:serviceName account:account error:nil]; } diff --git a/Sources/SAMKeychainQuery.m b/Sources/SAMKeychainQuery.m index 00ecb80..8754ad8 100644 --- a/Sources/SAMKeychainQuery.m +++ b/Sources/SAMKeychainQuery.m @@ -49,9 +49,6 @@ - (BOOL)save:(NSError *__autoreleasing *)error { status = SecItemUpdate((__bridge CFDictionaryRef)(searchQuery), (__bridge CFDictionaryRef)(query)); }else if(status == errSecItemNotFound){//item not found, create it! query = [self query]; - if (self.label) { - [query setObject:self.label forKey:(__bridge id)kSecAttrLabel]; - } [query setObject:self.passwordData forKey:(__bridge id)kSecValueData]; #if __IPHONE_4_0 && TARGET_OS_IPHONE CFTypeRef accessibilityType = [SAMKeychain accessibilityType]; @@ -130,7 +127,7 @@ - (nullable NSArray *)fetchAll:(NSError *__autoreleasing *)error { - (BOOL)fetch:(NSError *__autoreleasing *)error { OSStatus status = SAMKeychainErrorBadArguments; - if (!self.service || !self.account) { + if (!(self.service && self.account) || !(self.label && self.account)) { if (error) { *error = [[self class] errorWithCode:status]; } @@ -211,6 +208,10 @@ - (NSMutableDictionary *)query { if (self.account) { [dictionary setObject:self.account forKey:(__bridge id)kSecAttrAccount]; } + + if (self.label) { + [dictionary setObject:self.label forKey:(__bridge id)kSecAttrLabel]; + } #ifdef SAMKEYCHAIN_ACCESS_GROUP_AVAILABLE #if !TARGET_IPHONE_SIMULATOR From 265fbf06add97f6d559dada1bf7cd70dce589170 Mon Sep 17 00:00:00 2001 From: Manjunath Basaralu Srinivasa Date: Thu, 3 Nov 2016 11:00:54 -0700 Subject: [PATCH 2/2] Managed to fix some comments and add delete and fetch APIs. --- Sources/SAMKeychain.h | 30 ++++++++++++++++++++++++++++++ Sources/SAMKeychain.m | 24 ++++++++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/Sources/SAMKeychain.h b/Sources/SAMKeychain.h index b868b87..ec8f6e8 100644 --- a/Sources/SAMKeychain.h +++ b/Sources/SAMKeychain.h @@ -88,6 +88,7 @@ extern NSString *const kSAMKeychainWhereKey; @param account The account for which to return the corresponding password. + @param label The label set to the keychain item. This is what is displayed in the name field of a keychain item. @return Returns a string containing the password for a given account and service, or `nil` if the Keychain doesn't have a password for the given parameters. */ @@ -122,6 +123,19 @@ extern NSString *const kSAMKeychainWhereKey; + (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account; + (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSString *)account error:(NSError **)error __attribute__((swift_error(none))); +/** + Deletes a password from the Keychain. + + @param serviceName The service for which to delete the corresponding password. + + @param account The account for which to delete the corresponding password. + + @param label The label set to the keychain item. This is what is displayed in the name field of a keychain item. + + @return Returns `YES` on success, or `NO` on failure. + */ ++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label; ++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError **)error __attribute__((swift_error(none))); /** Deletes a password from the Keychain. @@ -208,6 +222,22 @@ extern NSString *const kSAMKeychainWhereKey; + (nullable NSArray *> *)allAccounts; + (nullable NSArray *> *)allAccounts:(NSError *__autoreleasing *)error __attribute__((swift_error(none))); +/** + Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any + accounts for the given service. + + See the `NSString` constants declared in SAMKeychain.h for a list of keys that can be used when accessing the + dictionaries returned by this method. + + @param serviceName The service for which to return the corresponding accounts. + + @param label The label set to the keychain item. This is what is displayed in the name field of a keychain item. + + @return An array of dictionaries containing the Keychain's accounts for a given `serviceName`, or `nil` if the Keychain + doesn't have any accounts for the given `serviceName`. The order of the objects in the array isn't defined. + */ ++ (nullable NSArray *> *)accountsForService:(nullable NSString *)serviceName label:(nullable NSString *)label; ++ (nullable NSArray *> *)accountsForService:(nullable NSString *)serviceName label:(nullable NSString *)label error:(NSError *__autoreleasing *)error __attribute__((swift_error(none))); /** Returns an array containing the Keychain's accounts for a given service, or `nil` if the Keychain doesn't have any diff --git a/Sources/SAMKeychain.m b/Sources/SAMKeychain.m index 9058d66..97dbd0c 100644 --- a/Sources/SAMKeychain.m +++ b/Sources/SAMKeychain.m @@ -92,6 +92,19 @@ + (nullable NSData *)passwordDataForService:(NSString *)serviceName account:(NSS return query.passwordData; } ++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account label:(nonnull NSString *)label{ + return [self deletePasswordForService:serviceName account:account label:label error:nil]; +} + + ++ (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account label:(NSString *)label error:(NSError *__autoreleasing *)error { + SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init]; + query.label = label; + query.service = serviceName; + query.account = account; + return [query deleteItem:error]; +} + + (BOOL)deletePasswordForService:(NSString *)serviceName account:(NSString *)account { return [self deletePasswordForService:serviceName account:account error:nil]; } @@ -166,6 +179,17 @@ + (nullable NSArray *)allAccounts:(NSError *__autoreleasing *)error { return [self accountsForService:nil error:error]; } ++ (nullable NSArray *)accountsForService:(nullable NSString *)serviceName label:(NSString *)label{ + return [self accountsForService:serviceName label:label error:nil]; +} + + ++ (nullable NSArray *)accountsForService:(nullable NSString *)serviceName label:(NSString *)label error:(NSError *__autoreleasing *)error { + SAMKeychainQuery *query = [[SAMKeychainQuery alloc] init]; + query.label = label; + query.service = serviceName; + return [query fetchAll:error]; +} + (nullable NSArray *)accountsForService:(nullable NSString *)serviceName { return [self accountsForService:serviceName error:nil];