diff --git a/.gitignore b/.gitignore index 93ec50b..ad63ff8 100644 --- a/.gitignore +++ b/.gitignore @@ -44,7 +44,7 @@ Carthage/Build # fastlane # -# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the # screenshots whenever they are needed. # For more information about the recommended setup visit: # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md @@ -64,3 +64,5 @@ iOSInjectionProject/ #Test Login Credentials ChromeChain/ChromeChainConsole/test_files/ + +Pods/ \ No newline at end of file diff --git a/Chromate/AppDelegate.h b/Chromate/AppDelegate.h new file mode 100644 index 0000000..ba8fee8 --- /dev/null +++ b/Chromate/AppDelegate.h @@ -0,0 +1,15 @@ +// +// AppDelegate.h +// Chromate +// +// Created by Terry Lewis on 26/6/17. +// Copyright © 2017 terry1994. All rights reserved. +// + +#import + +@interface AppDelegate : NSObject + + +@end + diff --git a/Chromate/AppDelegate.m b/Chromate/AppDelegate.m new file mode 100644 index 0000000..03ed105 --- /dev/null +++ b/Chromate/AppDelegate.m @@ -0,0 +1,39 @@ +// +// AppDelegate.m +// Chromate +// +// Created by Terry Lewis on 26/6/17. +// Copyright © 2017 terry1994. All rights reserved. +// + +#import "AppDelegate.h" + +@interface AppDelegate () + +@end + +static NSString *const kSafariSharedFrameworkPath = @"/System/Library/PrivateFrameworks/SafariShared.framework"; +static NSString *const kSafariSharedPath = @"/System/Library/PrivateFrameworks/Safari.framework"; + +@implementation AppDelegate + +- (void)applicationDidFinishLaunching:(NSNotification *)aNotification { + // Insert code here to initialize your application + + NSBundle *safariSharedBundle = [NSBundle bundleWithPath:kSafariSharedFrameworkPath]; + NSLog(@"Load SafariShared: %@", [safariSharedBundle load] ? @"YES" : @"NO"); + + NSBundle *safariBundle = [NSBundle bundleWithPath:kSafariSharedPath]; + NSLog(@"Load Safari: %@", [safariBundle load] ? @"YES" : @"NO"); +} + + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + // Insert code here to tear down your application +} + +- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)sender { + return YES; +} + +@end diff --git a/Chromate/Assets.xcassets/AppIcon.appiconset/Contents.json b/Chromate/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..b3db95c --- /dev/null +++ b/Chromate/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,58 @@ +{ + "images": [ + { + "idiom": "mac", + "size": "16x16", + "scale": "1x" + }, + { + "idiom": "mac", + "size": "16x16", + "scale": "2x" + }, + { + "idiom": "mac", + "size": "32x32", + "scale": "1x" + }, + { + "idiom": "mac", + "size": "32x32", + "scale": "2x" + }, + { + "idiom": "mac", + "size": "128x128", + "scale": "1x" + }, + { + "idiom": "mac", + "size": "128x128", + "scale": "2x" + }, + { + "idiom": "mac", + "size": "256x256", + "scale": "1x" + }, + { + "idiom": "mac", + "size": "256x256", + "scale": "2x" + }, + { + "idiom": "mac", + "size": "512x512", + "scale": "1x" + }, + { + "idiom": "mac", + "size": "512x512", + "scale": "2x" + } + ], + "info": { + "version": 1, + "author": "xcode" + } +} \ No newline at end of file diff --git a/Chromate/Base.lproj/Main.storyboard b/Chromate/Base.lproj/Main.storyboard new file mode 100644 index 0000000..dc8c932 --- /dev/null +++ b/Chromate/Base.lproj/Main.storyboard @@ -0,0 +1,849 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + Default + + + + + + + Left to Right + + + + + + + Right to Left + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Chromate/Chromate.entitlements b/Chromate/Chromate.entitlements new file mode 100644 index 0000000..c39bcd9 --- /dev/null +++ b/Chromate/Chromate.entitlements @@ -0,0 +1,10 @@ + + + + + keychain-access-groups + + com.apple.cfnetwork + + + diff --git a/Chromate/Common/ChromiumConstants.h b/Chromate/Common/ChromiumConstants.h new file mode 100644 index 0000000..088a55f --- /dev/null +++ b/Chromate/Common/ChromiumConstants.h @@ -0,0 +1,56 @@ +// +// ChromiumConstants.h +// ChromeChain +// +// Created by Terry Lewis on 25/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef ChromiumConstants_h +#define ChromiumConstants_h + +// Salt for Symmetric key derivation. +const char kSalt[] = "saltysalt"; +// Key size required for 128 bit AES. +const size_t kDerivedKeySizeInBits = 128; +// Constant for Symmetic key derivation. +const unsigned int kEncryptionIterations = 1003; + +const size_t kIVBlockSizeAES128 = 16; + +const size_t kDerivedKeyLen = kDerivedKeySizeInBits / 8; + +// Prefix for cypher text returned by current encryption version. We prefix +// the cypher text with this string so that future data migration can detect +// this and migrate to different encryption without data loss. +const char kEncryptionVersionPrefixv10[] = "v10"; + +#endif /* ChromiumConstants_h */ diff --git a/Chromate/Common/KeychainWriter.h b/Chromate/Common/KeychainWriter.h new file mode 100644 index 0000000..89c1866 --- /dev/null +++ b/Chromate/Common/KeychainWriter.h @@ -0,0 +1,14 @@ +// +// Created by Terry Lewis on 26/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import +#import "LoginCredential.h" + + +@interface KeychainWriter : NSObject + ++ (BOOL)writeCredentialToKeychain:(LoginCredential *)loginCredential decryptedPassword:(NSString *)decryptedPassword; + +@end \ No newline at end of file diff --git a/Chromate/Common/KeychainWriter.mm b/Chromate/Common/KeychainWriter.mm new file mode 100644 index 0000000..8eaae26 --- /dev/null +++ b/Chromate/Common/KeychainWriter.mm @@ -0,0 +1,37 @@ +// +// Created by Terry Lewis on 26/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import "KeychainWriter.h" +#import "UICKeyChainStore.h" + +static NSString *const kCommentString = @"Imported with ChromeChain."; + +@implementation KeychainWriter { +} + +static NSString *labelForCredential(LoginCredential *loginCredential) { + NSMutableString *result = [[NSMutableString alloc] initWithString:loginCredential.signonRealmURL.host]; + if (loginCredential.username != nil && loginCredential.username.length > 0) { + [result appendFormat:@" (%@)", loginCredential.username]; + } + + return result; +} + + ++ (BOOL)writeCredentialToKeychain:(LoginCredential *)loginCredential decryptedPassword:(NSString *)decryptedPassword { + UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithServer:loginCredential.signonRealmURL + protocolType:UICKeyChainStoreProtocolTypeHTTPS + authenticationType:UICKeyChainStoreAuthenticationTypeHTMLForm]; + keychain.synchronizable = YES; + [keychain setString:decryptedPassword + forKey:loginCredential.username + label:labelForCredential(loginCredential) + comment:kCommentString]; + + return NO; +} + +@end \ No newline at end of file diff --git a/Chromate/Common/LoginCredential.h b/Chromate/Common/LoginCredential.h new file mode 100644 index 0000000..7eeb4ad --- /dev/null +++ b/Chromate/Common/LoginCredential.h @@ -0,0 +1,26 @@ +// +// Created by Terry Lewis on 20/3/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import +#import + + +@interface LoginCredential : NSObject + +@property(nonatomic, retain, readonly) NSURL *signonRealmURL; +@property(nonatomic, retain, readonly) NSString *username; +@property(nonatomic, retain, readonly) NSData *encryptedPassword; +@property(nonatomic, readonly) std::string encryptedPasswordStr; +@property(nonatomic, readonly, getter=isValid) BOOL valid; + +- (instancetype)initWithSignonRealm:(NSString *)origin username:(NSString *)username + encryptedPassword:(NSData *)encryptedPassword; + ++ (instancetype)credentialWithSignonRealm:(NSString *)origin username:(NSString *)username + encryptedPassword:(NSData *)encryptedPassword; + +- (NSString *)decryptedPassword:(NSString *)cypher; + +@end diff --git a/Chromate/Common/LoginCredential.mm b/Chromate/Common/LoginCredential.mm new file mode 100644 index 0000000..6691dea --- /dev/null +++ b/Chromate/Common/LoginCredential.mm @@ -0,0 +1,46 @@ +// +// Created by Terry Lewis on 20/3/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import "LoginCredential.h" +#import +#import + +@interface LoginCredential () { +} +@property(nonatomic, retain) NSString *password; +@end + +@implementation LoginCredential + +- (instancetype)initWithSignonRealm:(NSString *)origin username:(NSString *)username + encryptedPassword:(NSData *)encryptedPassword { + self = [super init]; + if (self) { + _signonRealmURL = [NSURL URLWithString:origin]; + _username = username; + _encryptedPasswordData = encryptedPassword; + _valid = _signonRealmURL != nil && encryptedPassword != nil && encryptedPassword.length > 0; + + if (_valid) { + // Explicitly define length in order to ensure (possibly present) null terminators do not truncate encrypted + // strings. + const char *encryptedPasswordBytes = static_cast(encryptedPassword.bytes); + _encryptedPasswordString = std::string(encryptedPasswordBytes, encryptedPassword.length); + } + } + + return self; +} + ++ (instancetype)credentialWithSignonRealm:(NSString *)origin username:(NSString *)username + encryptedPassword:(NSData *)encryptedPassword { + return [[self alloc] initWithSignonRealm:origin username:username encryptedPassword:encryptedPassword]; +} + +- (NSString *)decryptedPassword:(NSString *)cypher { + return nil; +} + +@end diff --git a/ChromeChain/ChromeChainConsole/LoginStore.h b/Chromate/Common/LoginStore.h similarity index 71% rename from ChromeChain/ChromeChainConsole/LoginStore.h rename to Chromate/Common/LoginStore.h index 44db4b7..d63556f 100644 --- a/ChromeChain/ChromeChainConsole/LoginStore.h +++ b/Chromate/Common/LoginStore.h @@ -8,8 +8,8 @@ @interface LoginStore : NSObject -@property (nonatomic, strong) NSString *path; -@property (nonatomic, copy, readonly) NSArray *credentials; +@property(nonatomic, strong) NSString *path; +@property(nonatomic, copy, readonly) NSArray *credentials; - (instancetype)initWithPath:(NSString *)path; diff --git a/ChromeChain/ChromeChainConsole/LoginStore.m b/Chromate/Common/LoginStore.mm similarity index 86% rename from ChromeChain/ChromeChainConsole/LoginStore.m rename to Chromate/Common/LoginStore.mm index 792398a..b94eaed 100644 --- a/ChromeChain/ChromeChainConsole/LoginStore.m +++ b/Chromate/Common/LoginStore.mm @@ -5,10 +5,9 @@ #import "LoginStore.h" #import "FMDB/FMDB.h" -#import "LoginCredential.h" static NSString *const kTableName = @"logins"; -static NSString *const kActionURL = @"action_url"; +static NSString *const kActionURL = @"signon_realm"; static NSString *const kUsernameValue = @"username_value"; static NSString *const kPasswordValue = @"password_value"; @@ -31,24 +30,25 @@ + (instancetype)storeWithPath:(NSString *)path { return [[self alloc] initWithPath:path]; } -- (BOOL)readData{ - if (!_database.open){ +- (BOOL)readData { + if (!_database.open) { return NO; } NSString *queryString = [NSString stringWithFormat:@"SELECT * FROM %@", kTableName]; FMResultSet *resultSet = [_database executeQuery:queryString]; NSMutableArray *mutableLoginCredentials = [NSMutableArray array]; - while (resultSet.next){ + while (resultSet.next) { NSString *actionURL = [resultSet stringForColumn:kActionURL]; NSString *username = [resultSet stringForColumn:kUsernameValue]; NSData *encPassword = [resultSet dataForColumn:kPasswordValue]; - LoginCredential *loginCredential = [LoginCredential credentialWithActionURL:actionURL - username:username - encryptedPassword:encPassword]; + LoginCredential *loginCredential = [LoginCredential credentialWithSignonRealm:actionURL + username:username + encryptedPassword:encPassword]; [mutableLoginCredentials addObject:loginCredential]; } + [resultSet close]; _loginCredentials = mutableLoginCredentials; return [_database close]; diff --git a/Chromate/Common/PasswordDecryptor.h b/Chromate/Common/PasswordDecryptor.h new file mode 100644 index 0000000..7048be7 --- /dev/null +++ b/Chromate/Common/PasswordDecryptor.h @@ -0,0 +1,40 @@ +// +// Created by Terry Lewis on 24/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import +#import + + +@interface PasswordDecryptor : NSObject { +} + +typedef NS_ENUM(int, Service) { + ServiceChrome, + ServiceChromium +}; + +typedef NS_ENUM(int, SymmetricKeyResult) { + SymmetricKeyResultSuccess, + SymmetricKeyResultKeychainError, + SymmetricKeyResultPBKDFDerivationError +}; + +typedef NS_ENUM(int, DecryptionResult) { + DecryptionResultSuccess, + DecryptionResultUndefined, + DecryptionResultDecryptionError +}; + +@property(nonatomic, readonly) Service service; + +@property(nonatomic, readonly, getter=isReady) BOOL ready; + +- (instancetype)initWithService:(Service)service; + +- (SymmetricKeyResult)tryObtainSymmetricKey; + +- (DecryptionResult)decryptPassword:(std::string)encryptedPassword decryptedPassword:(std::string *)decryptedPassword; + +@end \ No newline at end of file diff --git a/Chromate/Common/PasswordDecryptor.mm b/Chromate/Common/PasswordDecryptor.mm new file mode 100644 index 0000000..a7fbe29 --- /dev/null +++ b/Chromate/Common/PasswordDecryptor.mm @@ -0,0 +1,142 @@ +// +// Created by Terry Lewis on 24/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#include "PasswordDecryptor.h" +#include +#include +#include "ChromiumConstants.h" + +@implementation PasswordDecryptor { + void *_passwordData; + UInt32 _passwordLength; + unsigned char _derivedKey[kDerivedKeyLen]; + unsigned char _iv[kIVBlockSizeAES128]; +} + +- (instancetype)initWithService:(Service)service { + if (self = super.init) { + std::fill(_iv, _iv + kIVBlockSizeAES128, ' '); + _passwordData = NULL; + _service = service; + _ready = NO; + } + return self; +} + +- (void)dealloc { + if (_passwordData != NULL) { + SecKeychainItemFreeContent(NULL, _passwordData); + } +} + +- (NSString *)serviceName { + switch (self.service) { + case ServiceChrome: + return @"Chrome Safe Storage"; + case ServiceChromium: + return @"Chromium Safe Storage"; + } + @throw [NSException exceptionWithName:@"InvalidServiceException" + reason:@"The current service is unsupported." userInfo:nil]; +} + +- (NSString *)accountName { + switch (self.service) { + case ServiceChrome: + return @"Chrome"; + case ServiceChromium: + return @"Chromium"; + } + @throw [NSException exceptionWithName:@"InvalidAccountException" + reason:@"The current account is unsupported." userInfo:nil]; +} + +- (SymmetricKeyResult)tryObtainSymmetricKey { + _passwordLength = 0; + + NSString *serviceName = [self serviceName]; + UInt32 serviceNameLen = static_cast(serviceName.length); + + NSString *accountName = [self accountName]; + UInt32 accountNameLen = static_cast(accountName.length); + + OSStatus osStatus = SecKeychainFindGenericPassword(NULL, serviceNameLen, serviceName.UTF8String, + accountNameLen, accountName.UTF8String, &_passwordLength, &_passwordData, NULL); + + if (osStatus != errSecSuccess) { + return SymmetricKeyResultKeychainError; + } + + int derivationPBKDFResult = CCKeyDerivationPBKDF(kCCPBKDF2, + reinterpret_cast(_passwordData), + _passwordLength, + reinterpret_cast(kSalt), + strlen(kSalt), + kCCPRFHmacAlgSHA1, + kEncryptionIterations, + reinterpret_cast(_derivedKey), + kDerivedKeyLen); + + if (derivationPBKDFResult != kCCSuccess) { + SecKeychainItemFreeContent(NULL, _passwordData); + _passwordData = NULL; + return SymmetricKeyResultPBKDFDerivationError; + } + + _ready = YES; + return SymmetricKeyResultSuccess; +} + +- (DecryptionResult)decryptPasswordV10:(std::string)encryptedPassword decryptedPassword:(std::string *)decryptedPassword { + std::string data; + +// unsigned char *iv = new unsigned char[kIVBlockSizeAES128](); + + size_t encryptedStringLen = encryptedPassword.size(); + char *encryptedString = new char[encryptedStringLen]; + + size_t encryptedStringTerminatorLoc = 0; + + CCCryptorStatus cryptorStatus = CCCrypt(kCCDecrypt, + kCCAlgorithmAES128, + kCCOptionPKCS7Padding, + _derivedKey, + kDerivedKeyLen, + _iv, + encryptedPassword.data(), + encryptedPassword.size(), + encryptedString, + encryptedStringLen, + &encryptedStringTerminatorLoc); + if (cryptorStatus != kCCSuccess) { + delete[] encryptedString; + return DecryptionResultDecryptionError; + } + encryptedString[encryptedStringTerminatorLoc] = '\0'; + + *decryptedPassword = encryptedString; + delete[] encryptedString; + return DecryptionResultSuccess; +} + +- (DecryptionResult)decryptPassword:(std::string)encryptedPassword decryptedPassword:(std::string *)decryptedPassword { + if (!self.isReady) { + @throw [NSException exceptionWithName:@"DecryptorNotReadyException" + reason:@"A symmetric key has yet to be defined. " + "-tryObtainSymmetricKey must be called and must return SymmetricKeyResultSuccess." + userInfo:nil]; + } + + std::string trimmedCypher; + if (encryptedPassword.find(kEncryptionVersionPrefixv10) == 0) { + trimmedCypher = encryptedPassword.substr(strlen(kEncryptionVersionPrefixv10)); + return [self decryptPasswordV10:trimmedCypher decryptedPassword:decryptedPassword]; + } else { + *decryptedPassword = encryptedPassword; + return DecryptionResultUndefined; + } +} + +@end diff --git a/Chromate/Info.plist b/Chromate/Info.plist new file mode 100644 index 0000000..cdfa759 --- /dev/null +++ b/Chromate/Info.plist @@ -0,0 +1,32 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIconFile + + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSMinimumSystemVersion + $(MACOSX_DEPLOYMENT_TARGET) + NSHumanReadableCopyright + Copyright © 2017 terry1994. All rights reserved. + NSMainStoryboardFile + Main + NSPrincipalClass + NSApplication + + diff --git a/Chromate/Safari.h b/Chromate/Safari.h new file mode 100644 index 0000000..55d7ca2 --- /dev/null +++ b/Chromate/Safari.h @@ -0,0 +1,23 @@ +// +// Safari.h +// ChromeChain +// +// Created by Terry Lewis on 27/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#ifndef Safari_h +#define Safari_h + +#import + +@interface NSURLCredential (SafariExtras) +- (NSURLCredential *)safari_initWithUser:(id)user password:(id)password persistence:(NSURLCredentialPersistence)persistence; +@end + +@interface NSURLCredentialStorage (SafariExtras) +- (id)safari_addSynchronizableCopyOfCredentialIfNeeded:(NSURLCredential *)credential + forProtectionSpace:(NSURLProtectionSpace *)protectionSpace; +@end + +#endif /* Safari_h */ diff --git a/Chromate/SafariShared.h b/Chromate/SafariShared.h new file mode 100644 index 0000000..a1a0a33 --- /dev/null +++ b/Chromate/SafariShared.h @@ -0,0 +1,20 @@ +// +// SafariShared.h +// ChromeChain +// +// Created by Terry Lewis on 27/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#ifndef SafariShared_h +#define SafariShared_h + +#import + +@interface NSURLProtectionSpace (SafariShared) + ++ (NSURLProtectionSpace *)safari_HTMLFormProtectionSpaceForURL:(NSURL *)URL; + +@end + +#endif /* SafariShared_h */ diff --git a/Chromate/ViewController.h b/Chromate/ViewController.h new file mode 100644 index 0000000..1e3cfc0 --- /dev/null +++ b/Chromate/ViewController.h @@ -0,0 +1,17 @@ +// +// ViewController.h +// Chromate +// +// Created by Terry Lewis on 26/6/17. +// Copyright © 2017 terry1994. All rights reserved. +// + +#import + +@interface ViewController : NSViewController + +@property(weak) IBOutlet NSTableView *tableView; +@property(weak) IBOutlet NSButton *syncButton; + +@end + diff --git a/Chromate/ViewController.mm b/Chromate/ViewController.mm new file mode 100644 index 0000000..91e7cd3 --- /dev/null +++ b/Chromate/ViewController.mm @@ -0,0 +1,164 @@ +// +// ViewController.m +// Chromate +// +// Created by Terry Lewis on 26/6/17. +// Copyright © 2017 terry1994. All rights reserved. +// + +#import "ViewController.h" +#import "LoginStore.h" +#import "PasswordDecryptor.h" +#import "SafariShared.h" +#import "Safari.h" + +@implementation ViewController { + LoginStore *_loginStore; + NSArray *_credentials; + PasswordDecryptor *_passwordDecryptor; +} + +static NSString *const kRealmColumnID = @"RealmCellID"; +static NSString *const kUsernameColumnID = @"UsernameCellID"; +static NSString *const kPasswordColumnID = @"PasswordCellID"; + +static NSString *const kRealmColumnSortDescriptor = @"signonRealmURL.absoluteString"; +static NSString *const kUsernameColumnSortDescriptor = @"username"; + +- (void)viewDidLoad { + [super viewDidLoad]; + + [_syncButton setTarget:self]; + [_syncButton setAction:@selector(syncButtonClicked:)]; + + _tableView.dataSource = self; + _tableView.delegate = self; + [_tableView setDoubleAction:@selector(tableViewDoubleAction:)]; + for (NSTableColumn *tableColumn in _tableView.tableColumns) { + NSString *sortKey = nil; + if (tableColumn == _tableView.tableColumns[0]) { + sortKey = kRealmColumnSortDescriptor; + } else if (tableColumn == _tableView.tableColumns[1]) { + sortKey = kUsernameColumnSortDescriptor; + } else { + continue; + } + NSSortDescriptor *sortDescriptor; + sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:sortKey ascending:YES selector:@selector(compare:)]; + [tableColumn setSortDescriptorPrototype:sortDescriptor]; + } + + NSArray *paths = NSSearchPathForDirectoriesInDomains(NSApplicationSupportDirectory, NSUserDomainMask, YES); + NSString *applicationSupportDirectory = [paths firstObject]; + NSString *filePath = [applicationSupportDirectory stringByAppendingPathComponent:@"Google/Chrome/Default/Login Data"]; + + _loginStore = [[LoginStore alloc] initWithURL:[NSURL fileURLWithPath:filePath]]; + [self readLoginStore]; + + [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(applicationWillTerminate:) name:NSApplicationWillTerminateNotification object:nil]; + + _passwordDecryptor = [[PasswordDecryptor alloc] initWithService:ServiceChrome]; +} + +- (void)trySaveUsername:(NSString *)username password:(NSString *)password realm:(NSURL *)realm { + NSAlert *alert = [[NSAlert alloc] init]; + alert.messageText = @"Information"; + alert.informativeText = [NSString stringWithFormat:@"Password: %@", password]; + [alert runModal]; + NSURLCredential *urlCredential = +// [[NSURLCredential alloc] safari_initWithUser:username password:password persistence:NSURLCredentialPersistenceSynchronizable]; + [[NSURLCredential alloc] initWithUser:username password:password persistence:NSURLCredentialPersistenceSynchronizable]; + NSLog(@"%@", urlCredential); + NSURLProtectionSpace *protectionSpace = [NSURLProtectionSpace safari_HTMLFormProtectionSpaceForURL:realm]; + + NSURLCredentialStorage *sharedCredentialStorage = [NSURLCredentialStorage sharedCredentialStorage]; +// NSURLCredential *addedCredential = [sharedCredentialStorage safari_addSynchronizableCopyOfCredentialIfNeeded:urlCredential forProtectionSpace:protectionSpace]; + [sharedCredentialStorage setCredential:urlCredential forProtectionSpace:protectionSpace]; + NSLog(@"%@", sharedCredentialStorage.allCredentials); +// if (addedCredential){ +// NSLog(@"Successfully added credential."); +// }else{ +// NSLog(@"Failed to add credential."); +// } +} + +- (void)tableViewDoubleAction:(id)sender { + if (!_passwordDecryptor.ready) { + [_passwordDecryptor tryObtainSymmetricKey]; + } + NSTableView *tableView = reinterpret_cast(sender); + NSInteger selectedRowIndex = [tableView selectedRow]; + LoginCredential *loginCredential = _credentials[static_cast(selectedRowIndex)]; + std::string decryptedPassword; + DecryptionResult decryptionResult = + [_passwordDecryptor decryptPassword:loginCredential.encryptedPasswordString decryptedPassword:&decryptedPassword]; + switch (decryptionResult) { + case DecryptionResultDecryptionError:break; + case DecryptionResultUndefined: + case DecryptionResultSuccess: { + NSString *decryptedPasswordString = [NSString stringWithUTF8String:decryptedPassword.c_str()]; +// NSLog(@"Realm %@", ); + [self trySaveUsername:loginCredential.username password:decryptedPasswordString realm:loginCredential.signonRealmURL]; + } + } +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification { + _loginStore = nil; +} + +- (void)dealloc { + _loginStore = nil; +} + +- (void)setRepresentedObject:(id)representedObject { + [super setRepresentedObject:representedObject]; + + // Update the view, if already loaded. +} + +- (void)syncButtonClicked:(id)sender { + NSLog(@"Click!"); +} + +- (void)readLoginStore { + NSLog(@"Read: %i", [_loginStore readData]); + _credentials = _loginStore.credentials; +} + +#pragma mark - NSTableViewDataSource + +- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView { + return _credentials.count; +} + +#pragma mark - NSTableViewDelegate + +- (nullable NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(nullable NSTableColumn *)tableColumn row:(NSInteger)row { + NSString *textRepresentation; + NSString *cellIdentifier; + LoginCredential *credential = _credentials[row]; + if (tableColumn == tableView.tableColumns[0]) { + textRepresentation = credential.signonRealmURL.absoluteString; + cellIdentifier = kRealmColumnID; + } else if (tableColumn == tableView.tableColumns[1]) { + textRepresentation = credential.username; + cellIdentifier = kUsernameColumnID; + } else if (tableColumn == tableView.tableColumns[2]) { + textRepresentation = @"••••••••"; + cellIdentifier = kPasswordColumnID; + } + + NSTableCellView *cell = [tableView makeViewWithIdentifier:cellIdentifier owner:nil]; + if (cell) { + cell.textField.stringValue = textRepresentation ?: @""; + } + return cell; +} + +- (void)tableView:(NSTableView *)tableView sortDescriptorsDidChange:(NSArray *)oldDescriptors { + _credentials = [_credentials sortedArrayUsingDescriptors:tableView.sortDescriptors]; + [tableView reloadData]; +} + +@end diff --git a/Chromate/main.m b/Chromate/main.m new file mode 100644 index 0000000..a7c00cb --- /dev/null +++ b/Chromate/main.m @@ -0,0 +1,13 @@ +// +// main.m +// Chromate +// +// Created by Terry Lewis on 26/6/17. +// Copyright © 2017 terry1994. All rights reserved. +// + +#import + +int main(int argc, const char *argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/ChromateTests/ChromateTests.m b/ChromateTests/ChromateTests.m new file mode 100644 index 0000000..dbe8c3d --- /dev/null +++ b/ChromateTests/ChromateTests.m @@ -0,0 +1,39 @@ +// +// ChromateTests.m +// ChromateTests +// +// Created by Terry Lewis on 26/6/17. +// Copyright © 2017 terry1994. All rights reserved. +// + +#import + +@interface ChromateTests : XCTestCase + +@end + +@implementation ChromateTests + +- (void)setUp { + [super setUp]; + // Put setup code here. This method is called before the invocation of each test method in the class. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // This is an example of a functional test case. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +- (void)testPerformanceExample { + // This is an example of a performance test case. + [self measureBlock:^{ + // Put the code you want to measure the time of here. + }]; +} + +@end diff --git a/ChromateTests/Info.plist b/ChromateTests/Info.plist new file mode 100644 index 0000000..8b3568f --- /dev/null +++ b/ChromateTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/ChromateUITests/ChromateUITests.m b/ChromateUITests/ChromateUITests.m new file mode 100644 index 0000000..57d31f0 --- /dev/null +++ b/ChromateUITests/ChromateUITests.m @@ -0,0 +1,40 @@ +// +// ChromateUITests.m +// ChromateUITests +// +// Created by Terry Lewis on 26/6/17. +// Copyright © 2017 terry1994. All rights reserved. +// + +#import + +@interface ChromateUITests : XCTestCase + +@end + +@implementation ChromateUITests + +- (void)setUp { + [super setUp]; + + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + self.continueAfterFailure = NO; + // UI tests must launch the application that they test. Doing this in setup will make sure it happens for each test method. + [[[XCUIApplication alloc] init] launch]; + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. +} + +- (void)tearDown { + // Put teardown code here. This method is called after the invocation of each test method in the class. + [super tearDown]; +} + +- (void)testExample { + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. +} + +@end diff --git a/ChromateUITests/Info.plist b/ChromateUITests/Info.plist new file mode 100644 index 0000000..8b3568f --- /dev/null +++ b/ChromateUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/ChromeChain.xcodeproj/project.pbxproj b/ChromeChain.xcodeproj/project.pbxproj index b6a8892..995694c 100644 --- a/ChromeChain.xcodeproj/project.pbxproj +++ b/ChromeChain.xcodeproj/project.pbxproj @@ -7,12 +7,44 @@ objects = { /* Begin PBXBuildFile section */ - 333E30411E7FF07B00C834CB /* LoginCredential.m in Sources */ = {isa = PBXBuildFile; fileRef = 333E303E1E7FF07B00C834CB /* LoginCredential.m */; }; - 333E30421E7FF07B00C834CB /* LoginStore.m in Sources */ = {isa = PBXBuildFile; fileRef = 333E30401E7FF07B00C834CB /* LoginStore.m */; }; - C5E1D9D9737FDA7C74394ADB /* libPods-ChromeChainConsole.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC619E92FE21CDF3D19CD1C /* libPods-ChromeChainConsole.a */; }; - D5AF6874D5EA552B46C223B0 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = D5AF687DB23078E4B59279EA /* main.m */; }; + 3328AF511F00A3FC00D8AF1E /* KeychainWriter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3328AF4A1F00A3FC00D8AF1E /* KeychainWriter.mm */; }; + 3328AF521F00A3FC00D8AF1E /* KeychainWriter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3328AF4A1F00A3FC00D8AF1E /* KeychainWriter.mm */; }; + 3328AF531F00A3FC00D8AF1E /* LoginCredential.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3328AF4C1F00A3FC00D8AF1E /* LoginCredential.mm */; }; + 3328AF541F00A3FC00D8AF1E /* LoginCredential.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3328AF4C1F00A3FC00D8AF1E /* LoginCredential.mm */; }; + 3328AF551F00A3FC00D8AF1E /* LoginStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3328AF4E1F00A3FC00D8AF1E /* LoginStore.mm */; }; + 3328AF561F00A3FC00D8AF1E /* LoginStore.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3328AF4E1F00A3FC00D8AF1E /* LoginStore.mm */; }; + 3328AF571F00A3FC00D8AF1E /* PasswordDecryptor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3328AF501F00A3FC00D8AF1E /* PasswordDecryptor.mm */; }; + 3328AF581F00A3FC00D8AF1E /* PasswordDecryptor.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3328AF501F00A3FC00D8AF1E /* PasswordDecryptor.mm */; }; + 3352D1921EFD67CD005A47EA /* libstdc++.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 3352D1911EFD67CD005A47EA /* libstdc++.tbd */; }; + 338F4F931F0015920007AB1C /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 338F4F921F0015920007AB1C /* AppDelegate.m */; }; + 338F4F961F0015920007AB1C /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 338F4F951F0015920007AB1C /* main.m */; }; + 338F4F991F0015920007AB1C /* ViewController.mm in Sources */ = {isa = PBXBuildFile; fileRef = 338F4F981F0015920007AB1C /* ViewController.mm */; }; + 338F4F9B1F0015920007AB1C /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 338F4F9A1F0015920007AB1C /* Assets.xcassets */; }; + 338F4F9E1F0015920007AB1C /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 338F4F9C1F0015920007AB1C /* Main.storyboard */; }; + 338F4FA91F0015920007AB1C /* ChromateTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 338F4FA81F0015920007AB1C /* ChromateTests.m */; }; + 338F4FB41F0015920007AB1C /* ChromateUITests.m in Sources */ = {isa = PBXBuildFile; fileRef = 338F4FB31F0015920007AB1C /* ChromateUITests.m */; }; + 6C5722EE140BF056B3252989 /* libPods-Chromate.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3FA434CE84FBC0C70886E5E2 /* libPods-Chromate.a */; }; + D5AF6874D5EA552B46C223B0 /* main.mm in Sources */ = {isa = PBXBuildFile; fileRef = D5AF687DB23078E4B59279EA /* main.mm */; }; + E7CC40469A26C9208DE4B415 /* libPods-ChromeChainConsole.a in Frameworks */ = {isa = PBXBuildFile; fileRef = CCC619E92FE21CDF3D19CD1C /* libPods-ChromeChainConsole.a */; }; /* End PBXBuildFile section */ +/* Begin PBXContainerItemProxy section */ + 338F4FA51F0015920007AB1C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D5AF60DB0CA4547E0400A7C9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 338F4F8E1F0015920007AB1C; + remoteInfo = Chromate; + }; + 338F4FB01F0015920007AB1C /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = D5AF60DB0CA4547E0400A7C9 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 338F4F8E1F0015920007AB1C; + remoteInfo = Chromate; + }; +/* End PBXContainerItemProxy section */ + /* Begin PBXCopyFilesBuildPhase section */ D5AF6F29A9604A2416630DD4 /* CopyFiles */ = { isa = PBXCopyFilesBuildPhase; @@ -26,40 +58,157 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ + 02C6340C7F1847BFC1B815E1 /* Pods-Chromate.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Chromate.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Chromate/Pods-Chromate.debug.xcconfig"; sourceTree = ""; }; + 3328AF481F00A3FC00D8AF1E /* ChromiumConstants.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ChromiumConstants.h; sourceTree = ""; }; + 3328AF491F00A3FC00D8AF1E /* KeychainWriter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KeychainWriter.h; sourceTree = ""; }; + 3328AF4A1F00A3FC00D8AF1E /* KeychainWriter.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = KeychainWriter.mm; sourceTree = ""; }; + 3328AF4B1F00A3FC00D8AF1E /* LoginCredential.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginCredential.h; sourceTree = ""; }; + 3328AF4C1F00A3FC00D8AF1E /* LoginCredential.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LoginCredential.mm; sourceTree = ""; }; + 3328AF4D1F00A3FC00D8AF1E /* LoginStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginStore.h; sourceTree = ""; }; + 3328AF4E1F00A3FC00D8AF1E /* LoginStore.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = LoginStore.mm; sourceTree = ""; }; + 3328AF4F1F00A3FC00D8AF1E /* PasswordDecryptor.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PasswordDecryptor.h; sourceTree = ""; }; + 3328AF501F00A3FC00D8AF1E /* PasswordDecryptor.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = PasswordDecryptor.mm; sourceTree = ""; }; 333E30261E7FECA900C834CB /* libFMDB.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libFMDB.a; path = "../../../Library/Developer/Xcode/DerivedData/ChromeChain-gvsudhphjgqcphanbbxgrmdzxgtk/Build/Products/Debug/FMDB/libFMDB.a"; sourceTree = ""; }; - 333E303D1E7FF07B00C834CB /* LoginCredential.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginCredential.h; sourceTree = ""; }; - 333E303E1E7FF07B00C834CB /* LoginCredential.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginCredential.m; sourceTree = ""; }; - 333E303F1E7FF07B00C834CB /* LoginStore.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LoginStore.h; sourceTree = ""; }; - 333E30401E7FF07B00C834CB /* LoginStore.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = LoginStore.m; sourceTree = ""; }; + 3352D1911EFD67CD005A47EA /* libstdc++.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = "libstdc++.tbd"; path = "usr/lib/libstdc++.tbd"; sourceTree = SDKROOT; }; + 338F4F8F1F0015920007AB1C /* Chromate.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Chromate.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 338F4F911F0015920007AB1C /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = ""; }; + 338F4F921F0015920007AB1C /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = ""; }; + 338F4F951F0015920007AB1C /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 338F4F971F0015920007AB1C /* ViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = ""; }; + 338F4F981F0015920007AB1C /* ViewController.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = ViewController.mm; sourceTree = ""; }; + 338F4F9A1F0015920007AB1C /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 338F4F9D1F0015920007AB1C /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; + 338F4F9F1F0015920007AB1C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 338F4FA41F0015920007AB1C /* ChromateTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChromateTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 338F4FA81F0015920007AB1C /* ChromateTests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChromateTests.m; sourceTree = ""; }; + 338F4FAA1F0015920007AB1C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 338F4FAF1F0015920007AB1C /* ChromateUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ChromateUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 338F4FB31F0015920007AB1C /* ChromateUITests.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ChromateUITests.m; sourceTree = ""; }; + 338F4FB51F0015920007AB1C /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 33ABC80F1EFE457E009BD3AB /* libcrypto.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libcrypto.a; path = ../../../../../../usr/local/Cellar/openssl/1.0.2l/lib/libcrypto.a; sourceTree = ""; }; + 33ABC8101EFE457E009BD3AB /* libssl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libssl.a; path = ../../../../../../usr/local/Cellar/openssl/1.0.2l/lib/libssl.a; sourceTree = ""; }; + 33B7648E1F014BAA00120374 /* Chromate.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = Chromate.entitlements; sourceTree = ""; }; + 33BE20B91F0282000067DEAA /* SafariShared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SafariShared.framework; path = ../../../../../../System/Library/PrivateFrameworks/SafariShared.framework; sourceTree = ""; }; + 33BE20BB1F0283490067DEAA /* SafariShared.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SafariShared.framework; path = Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/PrivateFrameworks/SafariShared.framework; sourceTree = DEVELOPER_DIR; }; + 3FA434CE84FBC0C70886E5E2 /* libPods-Chromate.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Chromate.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 48AB2EE598BD65E37EDB039F /* Pods-ChromeChain.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChromeChain.release.xcconfig"; path = "Pods/Target Support Files/Pods-ChromeChain/Pods-ChromeChain.release.xcconfig"; sourceTree = ""; }; - 664B0FCB1F1CBAE79ACD2A30 /* Pods-ChromeChainConsole.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChromeChainConsole.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ChromeChainConsole/Pods-ChromeChainConsole.debug.xcconfig"; sourceTree = ""; }; + 4C96354214E8B9F243997F32 /* Pods-ChromeChainConsole.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChromeChainConsole.release.xcconfig"; path = "Pods/Target Support Files/Pods-ChromeChainConsole/Pods-ChromeChainConsole.release.xcconfig"; sourceTree = ""; }; + 677A2431B5F36C600AD5CAA1 /* Pods-ChromeChainConsole.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChromeChainConsole.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ChromeChainConsole/Pods-ChromeChainConsole.debug.xcconfig"; sourceTree = ""; }; 695FFB85A1509D77943E1FFF /* libPods-ChromeChain.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ChromeChain.a"; sourceTree = BUILT_PRODUCTS_DIR; }; 8570C1BC247B956575E1575B /* Pods-ChromeChain.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChromeChain.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ChromeChain/Pods-ChromeChain.debug.xcconfig"; sourceTree = ""; }; - B14FE5F50AF5F447D4C802DB /* Pods-ChromeChainConsole.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ChromeChainConsole.release.xcconfig"; path = "Pods/Target Support Files/Pods-ChromeChainConsole/Pods-ChromeChainConsole.release.xcconfig"; sourceTree = ""; }; + 8CA7C71AC2558496B2774B43 /* Pods-Chromate.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Chromate.release.xcconfig"; path = "Pods/Target Support Files/Pods-Chromate/Pods-Chromate.release.xcconfig"; sourceTree = ""; }; CCC619E92FE21CDF3D19CD1C /* libPods-ChromeChainConsole.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-ChromeChainConsole.a"; sourceTree = BUILT_PRODUCTS_DIR; }; - D5AF687DB23078E4B59279EA /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + D5AF610309660383421870AC /* Safari.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Safari.h; sourceTree = ""; }; + D5AF687DB23078E4B59279EA /* main.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = main.mm; sourceTree = ""; }; D5AF6A598DC26AD658D1628A /* ChromeChainConsole */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ChromeChainConsole; sourceTree = BUILT_PRODUCTS_DIR; }; + D5AF6ACCACBF11D52A491194 /* SafariShared.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SafariShared.h; sourceTree = ""; }; + FF1588ED519CC6C58D76A34E /* libPods-Cr24.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Cr24.a"; sourceTree = BUILT_PRODUCTS_DIR; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 338F4F8C1F0015920007AB1C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 6C5722EE140BF056B3252989 /* libPods-Chromate.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 338F4FA11F0015920007AB1C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 338F4FAC1F0015920007AB1C /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; D5AF68C0CCE4B4BDFD0C7ABB /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - C5E1D9D9737FDA7C74394ADB /* libPods-ChromeChainConsole.a in Frameworks */, + 3352D1921EFD67CD005A47EA /* libstdc++.tbd in Frameworks */, + E7CC40469A26C9208DE4B415 /* libPods-ChromeChainConsole.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 3328AF471F00A3FC00D8AF1E /* Common */ = { + isa = PBXGroup; + children = ( + 3328AF481F00A3FC00D8AF1E /* ChromiumConstants.h */, + 3328AF491F00A3FC00D8AF1E /* KeychainWriter.h */, + 3328AF4A1F00A3FC00D8AF1E /* KeychainWriter.mm */, + 3328AF4B1F00A3FC00D8AF1E /* LoginCredential.h */, + 3328AF4C1F00A3FC00D8AF1E /* LoginCredential.mm */, + 3328AF4D1F00A3FC00D8AF1E /* LoginStore.h */, + 3328AF4E1F00A3FC00D8AF1E /* LoginStore.mm */, + 3328AF4F1F00A3FC00D8AF1E /* PasswordDecryptor.h */, + 3328AF501F00A3FC00D8AF1E /* PasswordDecryptor.mm */, + ); + path = Common; + sourceTree = ""; + }; + 338F4F901F0015920007AB1C /* Chromate */ = { + isa = PBXGroup; + children = ( + 33B7648E1F014BAA00120374 /* Chromate.entitlements */, + 338F4F911F0015920007AB1C /* AppDelegate.h */, + 338F4F921F0015920007AB1C /* AppDelegate.m */, + 338F4F971F0015920007AB1C /* ViewController.h */, + 338F4F981F0015920007AB1C /* ViewController.mm */, + 338F4F9A1F0015920007AB1C /* Assets.xcassets */, + 338F4F9C1F0015920007AB1C /* Main.storyboard */, + 338F4F9F1F0015920007AB1C /* Info.plist */, + 338F4F941F0015920007AB1C /* Supporting Files */, + D5AF6ACCACBF11D52A491194 /* SafariShared.h */, + D5AF610309660383421870AC /* Safari.h */, + ); + path = Chromate; + sourceTree = ""; + }; + 338F4F941F0015920007AB1C /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 338F4F951F0015920007AB1C /* main.m */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 338F4FA71F0015920007AB1C /* ChromateTests */ = { + isa = PBXGroup; + children = ( + 338F4FA81F0015920007AB1C /* ChromateTests.m */, + 338F4FAA1F0015920007AB1C /* Info.plist */, + ); + path = ChromateTests; + sourceTree = ""; + }; + 338F4FB21F0015920007AB1C /* ChromateUITests */ = { + isa = PBXGroup; + children = ( + 338F4FB31F0015920007AB1C /* ChromateUITests.m */, + 338F4FB51F0015920007AB1C /* Info.plist */, + ); + path = ChromateUITests; + sourceTree = ""; + }; 84D1A5814773C04752A87664 /* Pods */ = { isa = PBXGroup; children = ( 8570C1BC247B956575E1575B /* Pods-ChromeChain.debug.xcconfig */, 48AB2EE598BD65E37EDB039F /* Pods-ChromeChain.release.xcconfig */, - 664B0FCB1F1CBAE79ACD2A30 /* Pods-ChromeChainConsole.debug.xcconfig */, - B14FE5F50AF5F447D4C802DB /* Pods-ChromeChainConsole.release.xcconfig */, + 02C6340C7F1847BFC1B815E1 /* Pods-Chromate.debug.xcconfig */, + 8CA7C71AC2558496B2774B43 /* Pods-Chromate.release.xcconfig */, + 677A2431B5F36C600AD5CAA1 /* Pods-ChromeChainConsole.debug.xcconfig */, + 4C96354214E8B9F243997F32 /* Pods-ChromeChainConsole.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -67,11 +216,7 @@ D5AF6239C3527E8FF9D0B08B /* ChromeChainConsole */ = { isa = PBXGroup; children = ( - 333E303D1E7FF07B00C834CB /* LoginCredential.h */, - 333E303E1E7FF07B00C834CB /* LoginCredential.m */, - 333E303F1E7FF07B00C834CB /* LoginStore.h */, - 333E30401E7FF07B00C834CB /* LoginStore.m */, - D5AF687DB23078E4B59279EA /* main.m */, + D5AF687DB23078E4B59279EA /* main.mm */, ); name = ChromeChainConsole; path = ChromeChain/ChromeChainConsole; @@ -81,6 +226,10 @@ isa = PBXGroup; children = ( D5AF6239C3527E8FF9D0B08B /* ChromeChainConsole */, + 338F4F901F0015920007AB1C /* Chromate */, + 3328AF471F00A3FC00D8AF1E /* Common */, + 338F4FA71F0015920007AB1C /* ChromateTests */, + 338F4FB21F0015920007AB1C /* ChromateUITests */, D5AF6618A8321CB8154728A0 /* Products */, E7F835AFBED83C30F30BDE25 /* Frameworks */, 84D1A5814773C04752A87664 /* Pods */, @@ -91,6 +240,9 @@ isa = PBXGroup; children = ( D5AF6A598DC26AD658D1628A /* ChromeChainConsole */, + 338F4F8F1F0015920007AB1C /* Chromate.app */, + 338F4FA41F0015920007AB1C /* ChromateTests.xctest */, + 338F4FAF1F0015920007AB1C /* ChromateUITests.xctest */, ); name = Products; sourceTree = ""; @@ -98,9 +250,16 @@ E7F835AFBED83C30F30BDE25 /* Frameworks */ = { isa = PBXGroup; children = ( + 33BE20BB1F0283490067DEAA /* SafariShared.framework */, + 33BE20B91F0282000067DEAA /* SafariShared.framework */, + 33ABC80F1EFE457E009BD3AB /* libcrypto.a */, + 33ABC8101EFE457E009BD3AB /* libssl.a */, + 3352D1911EFD67CD005A47EA /* libstdc++.tbd */, 333E30261E7FECA900C834CB /* libFMDB.a */, 695FFB85A1509D77943E1FFF /* libPods-ChromeChain.a */, CCC619E92FE21CDF3D19CD1C /* libPods-ChromeChainConsole.a */, + FF1588ED519CC6C58D76A34E /* libPods-Cr24.a */, + 3FA434CE84FBC0C70886E5E2 /* libPods-Chromate.a */, ); name = Frameworks; sourceTree = ""; @@ -108,15 +267,71 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 338F4F8E1F0015920007AB1C /* Chromate */ = { + isa = PBXNativeTarget; + buildConfigurationList = 338F4FB61F0015920007AB1C /* Build configuration list for PBXNativeTarget "Chromate" */; + buildPhases = ( + 9851A3F32C81874BE9579D92 /* [CP] Check Pods Manifest.lock */, + 338F4F8B1F0015920007AB1C /* Sources */, + 338F4F8C1F0015920007AB1C /* Frameworks */, + 338F4F8D1F0015920007AB1C /* Resources */, + 3405E6D67A73C53A2F07C69B /* [CP] Embed Pods Frameworks */, + D860F5112F6D7E6F71D41AEB /* [CP] Copy Pods Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = Chromate; + productName = Chromate; + productReference = 338F4F8F1F0015920007AB1C /* Chromate.app */; + productType = "com.apple.product-type.application"; + }; + 338F4FA31F0015920007AB1C /* ChromateTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 338F4FB91F0015920007AB1C /* Build configuration list for PBXNativeTarget "ChromateTests" */; + buildPhases = ( + 338F4FA01F0015920007AB1C /* Sources */, + 338F4FA11F0015920007AB1C /* Frameworks */, + 338F4FA21F0015920007AB1C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 338F4FA61F0015920007AB1C /* PBXTargetDependency */, + ); + name = ChromateTests; + productName = ChromateTests; + productReference = 338F4FA41F0015920007AB1C /* ChromateTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 338F4FAE1F0015920007AB1C /* ChromateUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 338F4FBC1F0015920007AB1C /* Build configuration list for PBXNativeTarget "ChromateUITests" */; + buildPhases = ( + 338F4FAB1F0015920007AB1C /* Sources */, + 338F4FAC1F0015920007AB1C /* Frameworks */, + 338F4FAD1F0015920007AB1C /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 338F4FB11F0015920007AB1C /* PBXTargetDependency */, + ); + name = ChromateUITests; + productName = ChromateUITests; + productReference = 338F4FAF1F0015920007AB1C /* ChromateUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; D5AF633F157D13E528ED122A /* ChromeChainConsole */ = { isa = PBXNativeTarget; buildConfigurationList = D5AF606AC7AF66244AB81D78 /* Build configuration list for PBXNativeTarget "ChromeChainConsole" */; buildPhases = ( - 14272DD9C968B28738635909 /* [CP] Check Pods Manifest.lock */, + FCB0035D9E5885854ED3F025 /* [CP] Check Pods Manifest.lock */, D5AF659FEC12D1E3F8207197 /* Sources */, D5AF68C0CCE4B4BDFD0C7ABB /* Frameworks */, D5AF6F29A9604A2416630DD4 /* CopyFiles */, - DBE87AA6BD37904E110CB6CC /* [CP] Copy Pods Resources */, + A800416A1689478849C7B90C /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -133,7 +348,32 @@ D5AF60DB0CA4547E0400A7C9 /* Project object */ = { isa = PBXProject; attributes = { + LastSwiftUpdateCheck = 0830; ORGANIZATIONNAME = terry1994; + TargetAttributes = { + 338F4F8E1F0015920007AB1C = { + CreatedOnToolsVersion = 8.3.3; + ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.Keychain = { + enabled = 1; + }; + com.apple.iCloud = { + enabled = 0; + }; + }; + }; + 338F4FA31F0015920007AB1C = { + CreatedOnToolsVersion = 8.3.3; + ProvisioningStyle = Automatic; + TestTargetID = 338F4F8E1F0015920007AB1C; + }; + 338F4FAE1F0015920007AB1C = { + CreatedOnToolsVersion = 8.3.3; + ProvisioningStyle = Automatic; + TestTargetID = 338F4F8E1F0015920007AB1C; + }; + }; }; buildConfigurationList = D5AF69966A7D39D5951EA26A /* Build configuration list for PBXProject "ChromeChain" */; compatibilityVersion = "Xcode 3.2"; @@ -141,6 +381,7 @@ hasScannedForEncodings = 0; knownRegions = ( en, + Base, ); mainGroup = D5AF62EA4EF6792854193ED4; productRefGroup = D5AF6618A8321CB8154728A0 /* Products */; @@ -148,27 +389,74 @@ projectRoot = ""; targets = ( D5AF633F157D13E528ED122A /* ChromeChainConsole */, + 338F4F8E1F0015920007AB1C /* Chromate */, + 338F4FA31F0015920007AB1C /* ChromateTests */, + 338F4FAE1F0015920007AB1C /* ChromateUITests */, ); }; /* End PBXProject section */ +/* Begin PBXResourcesBuildPhase section */ + 338F4F8D1F0015920007AB1C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 338F4F9B1F0015920007AB1C /* Assets.xcassets in Resources */, + 338F4F9E1F0015920007AB1C /* Main.storyboard in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 338F4FA21F0015920007AB1C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 338F4FAD1F0015920007AB1C /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + /* Begin PBXShellScriptBuildPhase section */ - 14272DD9C968B28738635909 /* [CP] Check Pods Manifest.lock */ = { + 3405E6D67A73C53A2F07C69B /* [CP] Embed Pods Frameworks */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( ); inputPaths = ( ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Chromate/Pods-Chromate-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 9851A3F32C81874BE9579D92 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); name = "[CP] Check Pods Manifest.lock"; outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Chromate-checkManifestLockResult.txt", ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - DBE87AA6BD37904E110CB6CC /* [CP] Copy Pods Resources */ = { + A800416A1689478849C7B90C /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; files = ( @@ -183,22 +471,209 @@ shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-ChromeChainConsole/Pods-ChromeChainConsole-resources.sh\"\n"; showEnvVarsInLog = 0; }; + D860F5112F6D7E6F71D41AEB /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-Chromate/Pods-Chromate-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; + FCB0035D9E5885854ED3F025 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-ChromeChainConsole-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 338F4F8B1F0015920007AB1C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 338F4F991F0015920007AB1C /* ViewController.mm in Sources */, + 3328AF581F00A3FC00D8AF1E /* PasswordDecryptor.mm in Sources */, + 3328AF561F00A3FC00D8AF1E /* LoginStore.mm in Sources */, + 338F4F961F0015920007AB1C /* main.m in Sources */, + 3328AF521F00A3FC00D8AF1E /* KeychainWriter.mm in Sources */, + 338F4F931F0015920007AB1C /* AppDelegate.m in Sources */, + 3328AF541F00A3FC00D8AF1E /* LoginCredential.mm in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 338F4FA01F0015920007AB1C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 338F4FA91F0015920007AB1C /* ChromateTests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 338F4FAB1F0015920007AB1C /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 338F4FB41F0015920007AB1C /* ChromateUITests.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; D5AF659FEC12D1E3F8207197 /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 333E30411E7FF07B00C834CB /* LoginCredential.m in Sources */, - 333E30421E7FF07B00C834CB /* LoginStore.m in Sources */, - D5AF6874D5EA552B46C223B0 /* main.m in Sources */, + 3328AF511F00A3FC00D8AF1E /* KeychainWriter.mm in Sources */, + 3328AF571F00A3FC00D8AF1E /* PasswordDecryptor.mm in Sources */, + D5AF6874D5EA552B46C223B0 /* main.mm in Sources */, + 3328AF531F00A3FC00D8AF1E /* LoginCredential.mm in Sources */, + 3328AF551F00A3FC00D8AF1E /* LoginStore.mm in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXSourcesBuildPhase section */ +/* Begin PBXTargetDependency section */ + 338F4FA61F0015920007AB1C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 338F4F8E1F0015920007AB1C /* Chromate */; + targetProxy = 338F4FA51F0015920007AB1C /* PBXContainerItemProxy */; + }; + 338F4FB11F0015920007AB1C /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 338F4F8E1F0015920007AB1C /* Chromate */; + targetProxy = 338F4FB01F0015920007AB1C /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 338F4F9C1F0015920007AB1C /* Main.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 338F4F9D1F0015920007AB1C /* Base */, + ); + name = Main.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + /* Begin XCBuildConfiguration section */ + 338F4FB71F0015920007AB1C /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 02C6340C7F1847BFC1B815E1 /* Pods-Chromate.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Chromate/Chromate.entitlements; + CODE_SIGN_IDENTITY = ""; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + INFOPLIST_FILE = Chromate/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.terry1994.Chromate; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + }; + name = Debug; + }; + 338F4FB81F0015920007AB1C /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8CA7C71AC2558496B2774B43 /* Pods-Chromate.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = Chromate/Chromate.entitlements; + CODE_SIGN_IDENTITY = ""; + COMBINE_HIDPI_IMAGES = YES; + DEVELOPMENT_TEAM = ""; + FRAMEWORK_SEARCH_PATHS = ( + "$(inherited)", + "$(SYSTEM_LIBRARY_DIR)/PrivateFrameworks", + ); + INFOPLIST_FILE = Chromate/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.terry1994.Chromate; + PRODUCT_NAME = "$(TARGET_NAME)"; + PROVISIONING_PROFILE_SPECIFIER = ""; + }; + name = Release; + }; + 338F4FBA1F0015920007AB1C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = ChromateTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.terry1994.ChromateTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Chromate.app/Contents/MacOS/Chromate"; + }; + name = Debug; + }; + 338F4FBB1F0015920007AB1C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + BUNDLE_LOADER = "$(TEST_HOST)"; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = ChromateTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.terry1994.ChromateTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Chromate.app/Contents/MacOS/Chromate"; + }; + name = Release; + }; + 338F4FBD1F0015920007AB1C /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = ChromateUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.terry1994.ChromateUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Chromate; + }; + name = Debug; + }; + 338F4FBE1F0015920007AB1C /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + COMBINE_HIDPI_IMAGES = YES; + INFOPLIST_FILE = ChromateUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = com.terry1994.ChromateUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + TEST_TARGET_NAME = Chromate; + }; + name = Release; + }; D5AF61997A030E6CD1B2F351 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -220,6 +695,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = Entitlements.plist; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = dwarf; @@ -248,13 +724,23 @@ }; D5AF6745A4BEC7E35046E54B /* Debug */ = { isa = XCBuildConfiguration; - baseConfigurationReference = 664B0FCB1F1CBAE79ACD2A30 /* Pods-ChromeChainConsole.debug.xcconfig */; + baseConfigurationReference = 677A2431B5F36C600AD5CAA1 /* Pods-ChromeChainConsole.debug.xcconfig */; buildSettings = { GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "TEST_FILES_PATH=$(SRCROOT)/$(PROJECT_NAME)/$(TARGET_NAME)/test_files", "DEBUG=1", ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/Headers/Public\"", + "\"${PODS_ROOT}/Headers/Public/FMDB\"", + /usr/local/opt/openssl/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/Cellar/openssl/1.0.2l/lib, + ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Debug; @@ -280,6 +766,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_ENTITLEMENTS = Entitlements.plist; CODE_SIGN_IDENTITY = "-"; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; @@ -301,12 +788,22 @@ }; D5AF6B9A3AB6BB19EEA4A778 /* Release */ = { isa = XCBuildConfiguration; - baseConfigurationReference = B14FE5F50AF5F447D4C802DB /* Pods-ChromeChainConsole.release.xcconfig */; + baseConfigurationReference = 4C96354214E8B9F243997F32 /* Pods-ChromeChainConsole.release.xcconfig */; buildSettings = { GCC_PREPROCESSOR_DEFINITIONS = ( "$(inherited)", "TEST_FILES_PATH=$(SRCROOT)/$(PROJECT_NAME)/$(TARGET_NAME)/test_files", ); + HEADER_SEARCH_PATHS = ( + "$(inherited)", + "\"${PODS_ROOT}/Headers/Public\"", + "\"${PODS_ROOT}/Headers/Public/FMDB\"", + /usr/local/opt/openssl/include, + ); + LIBRARY_SEARCH_PATHS = ( + "$(inherited)", + /usr/local/Cellar/openssl/1.0.2l/lib, + ); PRODUCT_NAME = "$(TARGET_NAME)"; }; name = Release; @@ -314,6 +811,33 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 338F4FB61F0015920007AB1C /* Build configuration list for PBXNativeTarget "Chromate" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 338F4FB71F0015920007AB1C /* Debug */, + 338F4FB81F0015920007AB1C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 338F4FB91F0015920007AB1C /* Build configuration list for PBXNativeTarget "ChromateTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 338F4FBA1F0015920007AB1C /* Debug */, + 338F4FBB1F0015920007AB1C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 338F4FBC1F0015920007AB1C /* Build configuration list for PBXNativeTarget "ChromateUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 338F4FBD1F0015920007AB1C /* Debug */, + 338F4FBE1F0015920007AB1C /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; D5AF606AC7AF66244AB81D78 /* Build configuration list for PBXNativeTarget "ChromeChainConsole" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/ChromeChain.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/ChromeChain.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/ChromeChain.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/ChromeChain/ChromeChainConsole/LoginCredential.h b/ChromeChain/ChromeChainConsole/LoginCredential.h deleted file mode 100644 index 1ed9f93..0000000 --- a/ChromeChain/ChromeChainConsole/LoginCredential.h +++ /dev/null @@ -1,24 +0,0 @@ -// -// Created by Terry Lewis on 20/3/17. -// Copyright (c) 2017 terry1994. All rights reserved. -// - -#import - - -@interface LoginCredential : NSObject - -@property (nonatomic, retain, readonly) NSString *actionURL; -@property (nonatomic, retain, readonly) NSString *username; -@property (nonatomic, retain, readonly) NSData *encryptedPassword; -@property (nonatomic, retain, readonly) NSString *encryptedPasswordString; - -- (instancetype)initWithActionURL:(NSString *)actionURL username:(NSString *)username - encryptedPassword:(NSData *)encryptedPassword; - -+ (instancetype)credentialWithActionURL:(NSString *)actionURL username:(NSString *)username - encryptedPassword:(NSData *)encryptedPassword; - -- (NSString *)decryptedPassword:(NSString *)cypher; - -@end \ No newline at end of file diff --git a/ChromeChain/ChromeChainConsole/LoginCredential.m b/ChromeChain/ChromeChainConsole/LoginCredential.m deleted file mode 100644 index c351ad4..0000000 --- a/ChromeChain/ChromeChainConsole/LoginCredential.m +++ /dev/null @@ -1,38 +0,0 @@ -// -// Created by Terry Lewis on 20/3/17. -// Copyright (c) 2017 terry1994. All rights reserved. -// - -#import "LoginCredential.h" - -@interface LoginCredential (Private) -@property (nonatomic, retain) NSString *password; -@end - -@implementation LoginCredential { -} -- (instancetype)initWithActionURL:(NSString *)actionURL username:(NSString *)username - encryptedPassword:(NSData *)encryptedPassword { - self = [super init]; - if (self) { - _actionURL = actionURL; - _username = username; - _encryptedPassword = encryptedPassword; - _encryptedPasswordString = [[NSString alloc] - initWithData:encryptedPassword encoding:NSUTF16StringEncoding]; - } - - return self; -} - -+ (instancetype)credentialWithActionURL:(NSString *)actionURL username:(NSString *)username - encryptedPassword:(NSData *)encryptedPassword { - return [[self alloc] initWithActionURL:actionURL username:username encryptedPassword:encryptedPassword]; -} - -- (NSString *)decryptedPassword:(NSString *)cypher { - return nil; -} - - -@end \ No newline at end of file diff --git a/ChromeChain/ChromeChainConsole/main.m b/ChromeChain/ChromeChainConsole/main.m deleted file mode 100644 index 411ff9b..0000000 --- a/ChromeChain/ChromeChainConsole/main.m +++ /dev/null @@ -1,35 +0,0 @@ -// -// main.m -// ChromeChain -// -// Created by Terry Lewis on 20/3/17. -// Copyright (c) 2017 terry1994. All rights reserved. -// - -#import -#import "LoginStore.h" - -#define _NSStringify(x) #x -#define NSStringify(x) @_NSStringify(x) - -static NSString *const kTestFilesPath = NSStringify(TEST_FILES_PATH); - -int main(int argc, const char * argv[]) { - @autoreleasepool { - // insert code here... - NSLog(@"Hello, World! (%@)", kTestFilesPath); - } - - NSString *testDatabasePath = [kTestFilesPath stringByAppendingPathComponent:@"login_data"]; - LoginStore *loginStore = [LoginStore storeWithPath:testDatabasePath]; - if (!loginStore.readData){ - NSLog(@"Unable to read data."); - return -1; - } - - for (LoginCredential *credential in loginStore.credentials) { - NSLog(@"%@", credential.encryptedPasswordString); - } - - return 0; -} diff --git a/ChromeChain/ChromeChainConsole/main.mm b/ChromeChain/ChromeChainConsole/main.mm new file mode 100644 index 0000000..4acec79 --- /dev/null +++ b/ChromeChain/ChromeChainConsole/main.mm @@ -0,0 +1,72 @@ +// +// main.m +// ChromeChain +// +// Created by Terry Lewis on 20/3/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import +#import +#import "LoginStore.h" +#import "PasswordDecryptor.h" +#import "KeychainWriter.h" + +#define _NSStringify(x) #x +#define NSStringify(x) @_NSStringify(x) + +static NSString *const kTestFilesPath = NSStringify(TEST_FILES_PATH); + +int main(int argc, const char *argv[]) { + @autoreleasepool { + // insert code here... + } + + NSString *testDatabasePath = [kTestFilesPath stringByAppendingPathComponent:@"login_data"]; + + LoginStore *loginStore = [LoginStore storeWithURL:[[NSURL alloc] initFileURLWithPath:testDatabasePath]]; + if (!loginStore.readData) { + NSLog(@"Unable to read data."); + return -1; + } + + PasswordDecryptor *passwordDecryptor = [[PasswordDecryptor alloc] initWithService:ServiceChrome]; + switch ([passwordDecryptor tryObtainSymmetricKey]) { + case SymmetricKeyResultSuccess: + break; + case SymmetricKeyResultKeychainError: + std::cerr << "Unable to obtain password from keychain." << std::endl; + break; + case SymmetricKeyResultPBKDFDerivationError: + std::cerr << "Unable to derive PBKDF." << std::endl; + break; + } + for (LoginCredential *credential in loginStore.credentials) { + if (!credential.isValid) {continue;} + std::string password = credential.encryptedPasswordString; + + std::string result; + auto x = [passwordDecryptor decryptPassword:password decryptedPassword:&result]; + switch (x) { + case DecryptionResultDecryptionError: + std::cerr << "Error decrypting password for: " << credential.signonRealmURL.absoluteString.UTF8String + << "." + << std::endl; + break; + case DecryptionResultUndefined: + std::cout << "Password did not match any prefix." << std::endl; + case DecryptionResultSuccess: { +// std::cout << "URL: " << credential.signonRealmURL.absoluteString.UTF8String << " Password: " << result +// << std::endl; + + NSString *decryptedPassword = [NSString stringWithUTF8String:result.c_str()]; + [KeychainWriter writeCredentialToKeychain:credential decryptedPassword:decryptedPassword]; + + break; + } + } + } + + passwordDecryptor = nil; + return 0; +} diff --git a/Common/ChromiumConstants.h b/Common/ChromiumConstants.h new file mode 100644 index 0000000..5e2c1d6 --- /dev/null +++ b/Common/ChromiumConstants.h @@ -0,0 +1,58 @@ +// +// ChromiumConstants.h +// ChromeChain +// +// Created by Terry Lewis on 25/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// + +#ifndef ChromiumConstants_h +#define ChromiumConstants_h + +// Salt for Symmetric key derivation. +const char kSalt[] = "saltysalt"; +// Key size required for 128 bit AES. +const size_t kDerivedKeySizeInBits = 128; +// Constant for Symmetic key derivation. +const unsigned int kEncryptionIterations = 1003; + +// Size of initialization vector for AES 128-bit. +const size_t kIVBlockSizeAES128 = 16; + +// The length of the derived key, in bytes. +const size_t kDerivedKeyInBytes = kDerivedKeySizeInBits / 8; + +// Prefix for cypher text returned by current encryption version. We prefix +// the cypher text with this string so that future data migration can detect +// this and migrate to different encryption without data loss. +const char kEncryptionVersionPrefixv10[] = "v10"; + +#endif /* ChromiumConstants_h */ diff --git a/Common/KeychainWriter.h b/Common/KeychainWriter.h new file mode 100644 index 0000000..89c1866 --- /dev/null +++ b/Common/KeychainWriter.h @@ -0,0 +1,14 @@ +// +// Created by Terry Lewis on 26/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import +#import "LoginCredential.h" + + +@interface KeychainWriter : NSObject + ++ (BOOL)writeCredentialToKeychain:(LoginCredential *)loginCredential decryptedPassword:(NSString *)decryptedPassword; + +@end \ No newline at end of file diff --git a/Common/KeychainWriter.mm b/Common/KeychainWriter.mm new file mode 100644 index 0000000..a86af86 --- /dev/null +++ b/Common/KeychainWriter.mm @@ -0,0 +1,38 @@ +// +// Created by Terry Lewis on 26/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import "KeychainWriter.h" +#import "UICKeyChainStore.h" + +static NSString *const kCommentString = @"Imported with ChromeChain."; + +@implementation KeychainWriter { +} + +static NSString *labelForCredential(LoginCredential *loginCredential) { + NSMutableString *result = [[NSMutableString alloc] initWithString:loginCredential.signonRealmURL.host]; + if (loginCredential.username != nil && loginCredential.username.length > 0) { + [result appendFormat:@" (%@)", loginCredential.username]; + } + + return result; +} + + ++ (BOOL)writeCredentialToKeychain:(LoginCredential *)loginCredential decryptedPassword:(NSString *)decryptedPassword { + UICKeyChainStore *keychain = [UICKeyChainStore keyChainStoreWithServer:loginCredential.signonRealmURL + protocolType:UICKeyChainStoreProtocolTypeHTTPS + authenticationType:UICKeyChainStoreAuthenticationTypeHTMLForm]; + keychain.synchronizable = YES; + [keychain setString:decryptedPassword + forKey:loginCredential.username + label:labelForCredential(loginCredential) + comment:kCommentString]; +// [keychain setAccessibility:<#(UICKeyChainStoreAccessibility)accessibility#>]; + + return NO; +} + +@end \ No newline at end of file diff --git a/Common/LoginCredential.h b/Common/LoginCredential.h new file mode 100644 index 0000000..3604679 --- /dev/null +++ b/Common/LoginCredential.h @@ -0,0 +1,24 @@ +// +// Created by Terry Lewis on 20/3/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import +#import + + +@interface LoginCredential : NSObject + +@property(nonatomic, retain, readonly) NSURL *signonRealmURL; +@property(nonatomic, retain, readonly) NSString *username; +@property(nonatomic, retain, readonly) NSData *encryptedPasswordData; +@property(nonatomic, readonly) std::string encryptedPasswordString; +@property(nonatomic, readonly, getter=isValid) BOOL valid; + +- (instancetype)initWithSignonRealm:(NSString *)origin username:(NSString *)username + encryptedPassword:(NSData *)encryptedPassword; + ++ (instancetype)credentialWithSignonRealm:(NSString *)origin username:(NSString *)username + encryptedPassword:(NSData *)encryptedPassword; + +@end diff --git a/Common/LoginCredential.mm b/Common/LoginCredential.mm new file mode 100644 index 0000000..a93fe3b --- /dev/null +++ b/Common/LoginCredential.mm @@ -0,0 +1,41 @@ +// +// Created by Terry Lewis on 20/3/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import "LoginCredential.h" +#import + +@interface LoginCredential () { +} +@property(nonatomic, retain) NSString *password; +@end + +@implementation LoginCredential + +- (instancetype)initWithSignonRealm:(NSString *)origin username:(NSString *)username + encryptedPassword:(NSData *)encryptedPassword { + self = [super init]; + if (self) { + _signonRealmURL = [NSURL URLWithString:origin]; + _username = username; + _encryptedPasswordData = encryptedPassword; + _valid = _signonRealmURL != nil && encryptedPassword != nil && encryptedPassword.length > 0; + + if (_valid) { + // Explicitly define length in order to ensure (possibly present) null terminators do not truncate encrypted + // strings. + const char *encryptedPasswordBytes = static_cast(encryptedPassword.bytes); + _encryptedPasswordString = std::string(encryptedPasswordBytes, encryptedPassword.length); + } + } + + return self; +} + ++ (instancetype)credentialWithSignonRealm:(NSString *)origin username:(NSString *)username + encryptedPassword:(NSData *)encryptedPassword { + return [[self alloc] initWithSignonRealm:origin username:username encryptedPassword:encryptedPassword]; +} + +@end diff --git a/Common/LoginStore.h b/Common/LoginStore.h new file mode 100644 index 0000000..05a5e33 --- /dev/null +++ b/Common/LoginStore.h @@ -0,0 +1,21 @@ +// +// Created by Terry Lewis on 20/3/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import +#import "LoginCredential.h" + +@interface LoginStore : NSObject + +@property(nonatomic, strong) NSURL *path; +@property(nonatomic, copy, readonly) NSArray *credentials; +@property(nonatomic) BOOL useStagingDirectory; + +- (instancetype)initWithURL:(NSURL *)path; + +- (BOOL)readData; + ++ (instancetype)storeWithURL:(NSURL *)path; + +@end \ No newline at end of file diff --git a/Common/LoginStore.mm b/Common/LoginStore.mm new file mode 100644 index 0000000..87bc9b4 --- /dev/null +++ b/Common/LoginStore.mm @@ -0,0 +1,108 @@ +// +// Created by Terry Lewis on 20/3/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import "LoginStore.h" +#import "FMDB/FMDB.h" + +static NSString *const kTableName = @"logins"; +static NSString *const kActionURL = @"signon_realm"; +static NSString *const kUsernameValue = @"username_value"; +static NSString *const kPasswordValue = @"password_value"; + +@implementation LoginStore { + FMDatabase *_database; + NSArray *_loginCredentials; + NSURL *_stagingPath; + BOOL _hasStaged; +} + +- (instancetype)initWithURL:(NSURL *)path { + self = [super init]; + if (self) { + self.path = path; + self.useStagingDirectory = YES; + } + + return self; +} + ++ (instancetype)storeWithURL:(NSURL *)path { + return [[self alloc] initWithURL:path]; +} + +- (void)dealloc { + [self tryCleanStagedDatabase]; +} + +- (BOOL)tryCleanStagedDatabase { + NSLog(@"Attempting cleanup."); + if (_hasStaged && [self.path isEqual:_stagingPath]) { + // Delete the file, only now, we know it is not the user's original file. + NSLog(@"Deleted old, staged file."); + } + return YES; +} + +//- (BOOL)stageDatabase { +// if (!self.useStagingDirectory) { +// _stagingPath = self.path; +// return YES; +// } +// +// [self tryCleanStagedDatabase]; +// +// NSFileManager *defaultManager = [NSFileManager defaultManager]; +// BOOL isDirectory = NO; +// if (![defaultManager fileExistsAtPath:self.path.path isDirectory:&isDirectory] || isDirectory) { +// NSLog(@"Input file does not exist."); +// return NO; +// } +// +// NSError *error = nil; +// NSURL *stagingPath = [NSURL fileURLWithPath:@"chromate_staging.sqlite" relativeToURL:defaultManager.temporaryDirectory]; +// [defaultManager copyItemAtURL:self.path toURL:stagingPath error:&error]; +// _stagingPath = stagingPath; +// NSLog(@"Staged to: %@", stagingPath.path); +// +// return error == nil; +// +//} + +- (BOOL)readData { +// if (![self stageDatabase]) { +// NSLog(@"Stage fail."); +// return NO; +// } + _database = [FMDatabase databaseWithURL:self.path]; + + if (!_database.open) { + NSLog(@"Open fail."); + return NO; + } + + NSString *queryString = [NSString stringWithFormat:@"SELECT * FROM %@", kTableName]; + FMResultSet *resultSet = [_database executeQuery:queryString]; + NSMutableArray *mutableLoginCredentials = [NSMutableArray array]; + while (resultSet.next) { + NSString *actionURL = [resultSet stringForColumn:kActionURL]; + NSString *username = [resultSet stringForColumn:kUsernameValue]; + NSData *encPassword = [resultSet dataForColumn:kPasswordValue]; + + LoginCredential *loginCredential = [LoginCredential credentialWithSignonRealm:actionURL + username:username + encryptedPassword:encPassword]; + [mutableLoginCredentials addObject:loginCredential]; + } + [resultSet close]; + _loginCredentials = mutableLoginCredentials; + + return [_database close]; +} + +- (NSArray *)credentials { + return _loginCredentials; +} + +@end \ No newline at end of file diff --git a/Common/PasswordDecryptor.h b/Common/PasswordDecryptor.h new file mode 100644 index 0000000..7048be7 --- /dev/null +++ b/Common/PasswordDecryptor.h @@ -0,0 +1,40 @@ +// +// Created by Terry Lewis on 24/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#import +#import + + +@interface PasswordDecryptor : NSObject { +} + +typedef NS_ENUM(int, Service) { + ServiceChrome, + ServiceChromium +}; + +typedef NS_ENUM(int, SymmetricKeyResult) { + SymmetricKeyResultSuccess, + SymmetricKeyResultKeychainError, + SymmetricKeyResultPBKDFDerivationError +}; + +typedef NS_ENUM(int, DecryptionResult) { + DecryptionResultSuccess, + DecryptionResultUndefined, + DecryptionResultDecryptionError +}; + +@property(nonatomic, readonly) Service service; + +@property(nonatomic, readonly, getter=isReady) BOOL ready; + +- (instancetype)initWithService:(Service)service; + +- (SymmetricKeyResult)tryObtainSymmetricKey; + +- (DecryptionResult)decryptPassword:(std::string)encryptedPassword decryptedPassword:(std::string *)decryptedPassword; + +@end \ No newline at end of file diff --git a/Common/PasswordDecryptor.mm b/Common/PasswordDecryptor.mm new file mode 100644 index 0000000..9638746 --- /dev/null +++ b/Common/PasswordDecryptor.mm @@ -0,0 +1,141 @@ +// +// Created by Terry Lewis on 24/6/17. +// Copyright (c) 2017 terry1994. All rights reserved. +// + +#include "PasswordDecryptor.h" +#include +#include +#include "ChromiumConstants.h" + +@implementation PasswordDecryptor { + void *_passwordData; + UInt32 _passwordLength; + unsigned char _derivedKey[kDerivedKeyInBytes]; + unsigned char _iv[kIVBlockSizeAES128]; +} + +- (instancetype)initWithService:(Service)service { + self = super.init; + if (self) { + std::fill(_iv, _iv + kIVBlockSizeAES128, ' '); + _passwordData = NULL; + _service = service; + _ready = NO; + } + return self; +} + +- (void)dealloc { + if (_passwordData != NULL) { + SecKeychainItemFreeContent(NULL, _passwordData); + } +} + +- (NSString *)serviceName { + switch (self.service) { + case ServiceChrome: + return @"Chrome Safe Storage"; + case ServiceChromium: + return @"Chromium Safe Storage"; + } + @throw [NSException exceptionWithName:@"InvalidServiceException" + reason:@"The current service is unsupported." userInfo:nil]; +} + +- (NSString *)accountName { + switch (self.service) { + case ServiceChrome: + return @"Chrome"; + case ServiceChromium: + return @"Chromium"; + } + @throw [NSException exceptionWithName:@"InvalidAccountException" + reason:@"The selected account is of unsupported or unknown type." userInfo:nil]; +} + +- (SymmetricKeyResult)tryObtainSymmetricKey { + _passwordLength = 0; + + NSString *serviceName = [self serviceName]; + UInt32 serviceNameLen = static_cast(serviceName.length); + + NSString *accountName = [self accountName]; + UInt32 accountNameLen = static_cast(accountName.length); + + OSStatus osStatus = SecKeychainFindGenericPassword(NULL, serviceNameLen, serviceName.UTF8String, + accountNameLen, accountName.UTF8String, &_passwordLength, &_passwordData, NULL); + + if (osStatus != errSecSuccess) { + return SymmetricKeyResultKeychainError; + } + + int derivationPBKDFResult = CCKeyDerivationPBKDF(kCCPBKDF2, + reinterpret_cast(_passwordData), + _passwordLength, + reinterpret_cast(kSalt), + strlen(kSalt), + kCCPRFHmacAlgSHA1, + kEncryptionIterations, + reinterpret_cast(_derivedKey), + kDerivedKeyInBytes); + + if (derivationPBKDFResult != kCCSuccess) { + SecKeychainItemFreeContent(NULL, _passwordData); + _passwordData = NULL; + return SymmetricKeyResultPBKDFDerivationError; + } + + _ready = YES; + return SymmetricKeyResultSuccess; +} + +- (DecryptionResult)decryptPasswordV10:(std::string)encryptedPassword decryptedPassword:(std::string *)decryptedPassword { + std::string data; + + size_t encryptedStringLen = encryptedPassword.size(); + char *encryptedString = new char[encryptedStringLen]; + + size_t encryptedStringTerminatorLoc = 0; + + CCCryptorStatus cryptorStatus = CCCrypt(kCCDecrypt, + kCCAlgorithmAES128, + kCCOptionPKCS7Padding, + _derivedKey, + kDerivedKeyInBytes, + _iv, + encryptedPassword.data(), + encryptedPassword.size(), + encryptedString, + encryptedStringLen, + &encryptedStringTerminatorLoc); + if (cryptorStatus != kCCSuccess) { + delete[] encryptedString; + return DecryptionResultDecryptionError; + } + encryptedString[encryptedStringTerminatorLoc] = '\0'; + + *decryptedPassword = encryptedString; + delete[] encryptedString; + return DecryptionResultSuccess; +} + +- (DecryptionResult)decryptPassword:(std::string)encryptedPassword decryptedPassword:(std::string *)decryptedPassword { + if (!self.isReady) { + @throw [NSException exceptionWithName:@"DecryptorNotReadyException" + reason:@"A symmetric key has yet to be defined. " + "-tryObtainSymmetricKey must be called and must return SymmetricKeyResultSuccess." + userInfo:nil]; + } + + std::string trimmedCypher; + if (encryptedPassword.find(kEncryptionVersionPrefixv10) == 0) { + trimmedCypher = encryptedPassword.substr(strlen(kEncryptionVersionPrefixv10)); + return [self decryptPasswordV10:trimmedCypher decryptedPassword:decryptedPassword]; + } else { + *decryptedPassword = encryptedPassword; + return DecryptionResultUndefined; + } +} + +@end diff --git a/Podfile b/Podfile index 4d09165..6990aba 100644 --- a/Podfile +++ b/Podfile @@ -1,5 +1,14 @@ -platform:osx, '10.9' +platform :osx, '10.9' -target 'ChromeChainConsole' do +def shared_pods pod 'FMDB' + pod 'UICKeyChainStore' +end + +target 'Chromate' do + shared_pods +end + +target 'ChromeChainConsole' do + shared_pods end \ No newline at end of file diff --git a/Podfile.lock b/Podfile.lock new file mode 100644 index 0000000..ff954d3 --- /dev/null +++ b/Podfile.lock @@ -0,0 +1,17 @@ +PODS: + - FMDB (2.7.2): + - FMDB/standard (= 2.7.2) + - FMDB/standard (2.7.2) + - UICKeyChainStore (2.1.1) + +DEPENDENCIES: + - FMDB + - UICKeyChainStore + +SPEC CHECKSUMS: + FMDB: 6198a90e7b6900cfc046e6bc0ef6ebb7be9236aa + UICKeyChainStore: 239558492fa260531a0774cfc611ea83a6eaff3a + +PODFILE CHECKSUM: f7e662986fe14edc004bf98485628a2882f30066 + +COCOAPODS: 1.3.0.beta.2