Index: remoting/client/ios/keychain_wrapper.mm |
diff --git a/remoting/client/ios/keychain_wrapper.mm b/remoting/client/ios/keychain_wrapper.mm |
new file mode 100644 |
index 0000000000000000000000000000000000000000..c1ae602c8ce20a8acc90d09a27a5df4e17550d68 |
--- /dev/null |
+++ b/remoting/client/ios/keychain_wrapper.mm |
@@ -0,0 +1,206 @@ |
+// Copyright 2017 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#if !defined(__has_feature) || !__has_feature(objc_arc) |
+#error "This file requires ARC support." |
+#endif |
+ |
+#import "remoting/client/ios/keychain_wrapper.h" |
+ |
+#import "remoting/client/ios/domain/host_info.h" |
+ |
+static const UInt8 kKeychainItemIdentifier[] = "org.chromium.RemoteDesktop\0"; |
+ |
+@interface KeychainWrapper () { |
+ NSMutableDictionary* _keychainData; |
+ NSMutableDictionary* _userInfoQuery; |
+} |
+@end |
+ |
+@implementation KeychainWrapper |
+ |
+- (id)init { |
+ if ((self = [super init])) { |
+ OSStatus keychainErr = noErr; |
+ _userInfoQuery = [[NSMutableDictionary alloc] init]; |
+ [_userInfoQuery setObject:(__bridge id)kSecClassGenericPassword |
+ forKey:(__bridge id)kSecClass]; |
+ NSData* keychainItemID = |
+ [NSData dataWithBytes:kKeychainItemIdentifier |
+ length:strlen((const char*)kKeychainItemIdentifier)]; |
+ [_userInfoQuery setObject:keychainItemID |
+ forKey:(__bridge id)kSecAttrGeneric]; |
+ [_userInfoQuery setObject:(__bridge id)kSecMatchLimitOne |
+ forKey:(__bridge id)kSecMatchLimit]; |
+ [_userInfoQuery setObject:(__bridge id)kCFBooleanTrue |
+ forKey:(__bridge id)kSecReturnAttributes]; |
+ |
+ CFMutableDictionaryRef outDictionary = nil; |
+ keychainErr = SecItemCopyMatching((__bridge CFDictionaryRef)_userInfoQuery, |
+ (CFTypeRef*)&outDictionary); |
+ if (keychainErr == noErr) { |
+ _keychainData = [self |
+ secItemFormatToDictionary:(__bridge_transfer NSMutableDictionary*) |
+ outDictionary]; |
+ } else if (keychainErr == errSecItemNotFound) { |
+ [self resetKeychainItem]; |
+ |
+ if (outDictionary) { |
+ CFRelease(outDictionary); |
+ _keychainData = nil; |
+ } |
+ } else { |
+ NSLog(@"Serious error."); |
+ if (outDictionary) { |
+ CFRelease(outDictionary); |
+ _keychainData = nil; |
+ } |
+ } |
+ } |
+ return self; |
+} |
+ |
+- (void)setRefreshToken:(NSString*)refreshToken { |
+ [self setObject:refreshToken forKey:(__bridge id)kSecValueData]; |
+} |
+ |
+- (NSString*)refreshToken { |
+ return [self objectForKey:(__bridge id)kSecValueData]; |
+} |
+ |
+// Implement the mySetObject:forKey method, which writes attributes to the |
+// keychain: |
+- (void)setObject:(id)inObject forKey:(id)key { |
+ if (inObject == nil) |
+ return; |
+ id currentObject = [_keychainData objectForKey:key]; |
+ if (![currentObject isEqual:inObject]) { |
+ [_keychainData setObject:inObject forKey:key]; |
+ [self writeToKeychain]; |
+ } |
+} |
+ |
+// Implement the myObjectForKey: method, which reads an attribute value from a |
+// dictionary: |
+- (id)objectForKey:(id)key { |
+ return [_keychainData objectForKey:key]; |
+} |
+ |
+- (void)resetKeychainItem { |
+ if (!_keychainData) { |
+ _keychainData = [[NSMutableDictionary alloc] init]; |
+ } else if (_keychainData) { |
+ NSMutableDictionary* tmpDictionary = |
+ [self dictionaryToSecItemFormat:_keychainData]; |
+ OSStatus errorcode = SecItemDelete((__bridge CFDictionaryRef)tmpDictionary); |
+ if (errorcode == errSecItemNotFound) { |
+ // this is ok. |
+ } else if (errorcode != noErr) { |
+ NSLog(@"Problem deleting current keychain item."); |
+ } |
+ } |
+ |
+ [_keychainData setObject:@"gaia_refresh_token" |
+ forKey:(__bridge id)kSecAttrLabel]; |
+ [_keychainData setObject:@"Gaia fresh token" |
+ forKey:(__bridge id)kSecAttrDescription]; |
+ [_keychainData setObject:@"" forKey:(__bridge id)kSecValueData]; |
+} |
+ |
+- (NSMutableDictionary*)dictionaryToSecItemFormat: |
+ (NSDictionary*)dictionaryToConvert { |
+ NSMutableDictionary* returnDictionary = |
+ [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert]; |
+ |
+ NSData* keychainItemID = |
+ [NSData dataWithBytes:kKeychainItemIdentifier |
+ length:strlen((const char*)kKeychainItemIdentifier)]; |
+ [returnDictionary setObject:keychainItemID |
+ forKey:(__bridge id)kSecAttrGeneric]; |
+ [returnDictionary setObject:(__bridge id)kSecClassGenericPassword |
+ forKey:(__bridge id)kSecClass]; |
+ |
+ NSString* passwordString = |
+ [dictionaryToConvert objectForKey:(__bridge id)kSecValueData]; |
+ [returnDictionary |
+ setObject:[passwordString dataUsingEncoding:NSUTF8StringEncoding] |
+ forKey:(__bridge id)kSecValueData]; |
+ return returnDictionary; |
+} |
+ |
+- (NSMutableDictionary*)secItemFormatToDictionary: |
+ (NSDictionary*)dictionaryToConvert { |
+ NSMutableDictionary* returnDictionary = |
+ [NSMutableDictionary dictionaryWithDictionary:dictionaryToConvert]; |
+ |
+ [returnDictionary setObject:(__bridge id)kCFBooleanTrue |
+ forKey:(__bridge id)kSecReturnData]; |
+ [returnDictionary setObject:(__bridge id)kSecClassGenericPassword |
+ forKey:(__bridge id)kSecClass]; |
+ |
+ CFDataRef passwordData = NULL; |
+ OSStatus keychainError = noErr; |
+ keychainError = SecItemCopyMatching( |
+ (__bridge CFDictionaryRef)returnDictionary, (CFTypeRef*)&passwordData); |
+ if (keychainError == noErr) { |
+ [returnDictionary removeObjectForKey:(__bridge id)kSecReturnData]; |
+ |
+ NSString* password = [[NSString alloc] |
+ initWithBytes:[(__bridge_transfer NSData*)passwordData bytes] |
+ length:[(__bridge NSData*)passwordData length] |
+ encoding:NSUTF8StringEncoding]; |
+ [returnDictionary setObject:password forKey:(__bridge id)kSecValueData]; |
+ } else if (keychainError == errSecItemNotFound) { |
+ NSLog(@"Nothing was found in the keychain."); |
+ if (passwordData) { |
+ CFRelease(passwordData); |
+ passwordData = nil; |
+ } |
+ } else { |
+ NSLog(@"Serious error.\n"); |
+ if (passwordData) { |
+ CFRelease(passwordData); |
+ passwordData = nil; |
+ } |
+ } |
+ return returnDictionary; |
+} |
+ |
+- (void)writeToKeychain { |
+ CFDictionaryRef attributes = nil; |
+ NSMutableDictionary* updateItem = nil; |
+ |
+ if (SecItemCopyMatching((__bridge CFDictionaryRef)_userInfoQuery, |
+ (CFTypeRef*)&attributes) == noErr) { |
+ updateItem = [NSMutableDictionary |
+ dictionaryWithDictionary:(__bridge_transfer NSDictionary*)attributes]; |
+ |
+ [updateItem setObject:[_userInfoQuery objectForKey:(__bridge id)kSecClass] |
+ forKey:(__bridge id)kSecClass]; |
+ |
+ NSMutableDictionary* tempCheck = |
+ [self dictionaryToSecItemFormat:_keychainData]; |
+ [tempCheck removeObjectForKey:(__bridge id)kSecClass]; |
+ |
+ OSStatus errorcode = SecItemUpdate((__bridge CFDictionaryRef)updateItem, |
+ (__bridge CFDictionaryRef)tempCheck); |
+ if (errorcode != noErr) { |
+ NSLog(@"Couldn't update the Keychain Item. %d", (int)errorcode); |
+ } |
+ } else { |
+ OSStatus errorcode = |
+ SecItemAdd((__bridge CFDictionaryRef) |
+ [self dictionaryToSecItemFormat:_keychainData], |
+ NULL); |
+ if (errorcode != noErr) { |
+ NSLog(@"Couldn't add the Keychain Item. %d", (int)errorcode); |
+ } |
+ if (attributes) { |
+ CFRelease(attributes); |
+ attributes = nil; |
+ } |
+ } |
+} |
+ |
+@end |