diff --git a/KeePassKit b/KeePassKit index 43af7b31..7def350e 160000 --- a/KeePassKit +++ b/KeePassKit @@ -1 +1 @@ -Subproject commit 43af7b31f0127f21220ffff346ccb81479ed6f90 +Subproject commit 7def350e3eb2d5bbfce3360a3a94f706c426c04b diff --git a/MacPass.xcodeproj/project.pbxproj b/MacPass.xcodeproj/project.pbxproj index 79ee03b5..93415770 100644 --- a/MacPass.xcodeproj/project.pbxproj +++ b/MacPass.xcodeproj/project.pbxproj @@ -156,7 +156,7 @@ 4C6D1D2B17858A250014C5A5 /* MacPass.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4C6D1D2A17858A250014C5A5 /* MacPass.icns */; }; 4C6FDD2117BC4F4C004AEEC8 /* KPKTestPlaceholder.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C6FDD2017BC4F4C004AEEC8 /* KPKTestPlaceholder.m */; }; 4C701CBC178618A000581B88 /* 12_RemoteTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4C701CBB178618A000581B88 /* 12_RemoteTemplate.pdf */; }; - 4C70D100179092F200652EE9 /* KPKPassword.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70D0FF179092F200652EE9 /* KPKPassword.m */; }; + 4C70D100179092F200652EE9 /* KPKCompositeKey.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C70D0FF179092F200652EE9 /* KPKCompositeKey.m */; }; 4C74DD07177BD1640034A9DB /* MPCustomFieldView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4C74DD06177BD1640034A9DB /* MPCustomFieldView.m */; }; 4C76155C1764C04C0015A1A6 /* GeneralSettings.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C76155E1764C04C0015A1A6 /* GeneralSettings.xib */; }; 4C7615681764C0C40015A1A6 /* PasswordInputView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4C76156A1764C0C40015A1A6 /* PasswordInputView.xib */; }; @@ -250,6 +250,7 @@ 4CEAF85717BA9B1D001307A6 /* Keepass2Key.xml in Resources */ = {isa = PBXBuildFile; fileRef = 4CEAF85617BA9B1D001307A6 /* Keepass2Key.xml */; }; 4CEAF85B17BA9B44001307A6 /* KPKTestKeyfileParsing.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CEAF85A17BA9B44001307A6 /* KPKTestKeyfileParsing.m */; }; 4CECB31717AC326D00EAFB0F /* KPKTestLegacyWriting.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CECB31617AC326D00EAFB0F /* KPKTestLegacyWriting.m */; }; + 4CEE46DD181C301D006BF1E5 /* MPAutotypeDaemon.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CEE46DC181C301D006BF1E5 /* MPAutotypeDaemon.m */; }; 4CEED1C617D7BD0E007180F1 /* NSError+Messages.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CEED1C517D7BD0E007180F1 /* NSError+Messages.m */; }; 4CF29BF417879D0000851B60 /* 26_FileSaveTemplate.pdf in Resources */ = {isa = PBXBuildFile; fileRef = 4CF29BF317879D0000851B60 /* 26_FileSaveTemplate.pdf */; }; 4CF62B86179385D700B660B6 /* KPKAttribute.m in Sources */ = {isa = PBXBuildFile; fileRef = 4CF62B85179385D700B660B6 /* KPKAttribute.m */; }; @@ -545,8 +546,8 @@ 4C6FDD1F17BC4F4C004AEEC8 /* KPKTestPlaceholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKTestPlaceholder.h; sourceTree = ""; }; 4C6FDD2017BC4F4C004AEEC8 /* KPKTestPlaceholder.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKTestPlaceholder.m; sourceTree = ""; }; 4C701CBB178618A000581B88 /* 12_RemoteTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 12_RemoteTemplate.pdf; sourceTree = ""; }; - 4C70D0FE179092F200652EE9 /* KPKPassword.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKPassword.h; sourceTree = ""; }; - 4C70D0FF179092F200652EE9 /* KPKPassword.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = KPKPassword.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; + 4C70D0FE179092F200652EE9 /* KPKCompositeKey.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKCompositeKey.h; sourceTree = ""; }; + 4C70D0FF179092F200652EE9 /* KPKCompositeKey.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = KPKCompositeKey.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; 4C74DD05177BD1640034A9DB /* MPCustomFieldView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPCustomFieldView.h; sourceTree = ""; }; 4C74DD06177BD1640034A9DB /* MPCustomFieldView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPCustomFieldView.m; sourceTree = ""; }; 4C76155F1764C0590015A1A6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/GeneralSettings.xib; sourceTree = ""; }; @@ -720,6 +721,8 @@ 4CEAF85A17BA9B44001307A6 /* KPKTestKeyfileParsing.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKTestKeyfileParsing.m; sourceTree = ""; }; 4CECB31517AC326D00EAFB0F /* KPKTestLegacyWriting.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = KPKTestLegacyWriting.h; sourceTree = ""; }; 4CECB31617AC326D00EAFB0F /* KPKTestLegacyWriting.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = KPKTestLegacyWriting.m; sourceTree = ""; }; + 4CEE46DB181C301D006BF1E5 /* MPAutotypeDaemon.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MPAutotypeDaemon.h; sourceTree = ""; }; + 4CEE46DC181C301D006BF1E5 /* MPAutotypeDaemon.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MPAutotypeDaemon.m; sourceTree = ""; }; 4CEED1C417D7BD0E007180F1 /* NSError+Messages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSError+Messages.h"; sourceTree = ""; }; 4CEED1C517D7BD0E007180F1 /* NSError+Messages.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSError+Messages.m"; sourceTree = ""; }; 4CF29BF317879D0000851B60 /* 26_FileSaveTemplate.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = 26_FileSaveTemplate.pdf; sourceTree = ""; }; @@ -1051,6 +1054,8 @@ 4C4B7EF717A4B335000234C7 /* MPUniqueCharactersFormatter.m */, 4C224B4017DFCB2300FF6AEE /* MPNumericalInputFormatter.h */, 4C224B4117DFCB2400FF6AEE /* MPNumericalInputFormatter.m */, + 4CEE46DB181C301D006BF1E5 /* MPAutotypeDaemon.h */, + 4CEE46DC181C301D006BF1E5 /* MPAutotypeDaemon.m */, ); name = Helper; sourceTree = ""; @@ -1466,8 +1471,8 @@ 4CD3ABB1178F71B50073F5C5 /* Core */ = { isa = PBXGroup; children = ( - 4C70D0FE179092F200652EE9 /* KPKPassword.h */, - 4C70D0FF179092F200652EE9 /* KPKPassword.m */, + 4C70D0FE179092F200652EE9 /* KPKCompositeKey.h */, + 4C70D0FF179092F200652EE9 /* KPKCompositeKey.m */, 4CD3ABB3178F71B50073F5C5 /* KPKTree.h */, 4CD3ABB4178F71B50073F5C5 /* KPKTree.m */, 4CD3ABBD178F72610073F5C5 /* KPKEntry.h */, @@ -1940,8 +1945,9 @@ 4CD3ABBF178F72610073F5C5 /* KPKEntry.m in Sources */, 4CD3ABC2178F72720073F5C5 /* KPKGroup.m in Sources */, 4C591B57178F897A0080B16B /* KPKBinary.m in Sources */, + 4CEE46DD181C301D006BF1E5 /* MPAutotypeDaemon.m in Sources */, 4C39B3F5178FEFAE0027DC7C /* KPKNode.m in Sources */, - 4C70D100179092F200652EE9 /* KPKPassword.m in Sources */, + 4C70D100179092F200652EE9 /* KPKCompositeKey.m in Sources */, 4C8A173D1790AA41008B5C17 /* NSData+Keyfile.m in Sources */, 4C3F28541791EDFD00703281 /* KPKErrors.m in Sources */, 4C4436771792BE810099E220 /* KPKFormat.m in Sources */, @@ -2171,7 +2177,7 @@ MACOSX_DEPLOYMENT_TARGET = 10.8; ONLY_ACTIVE_ARCH = YES; OTHER_LDFLAGS = "-lxml2"; - SDKROOT = macosx; + SDKROOT = macosx10.8; }; name = Debug; }; @@ -2200,7 +2206,7 @@ HEADER_SEARCH_PATHS = "$(SDKROOT)/usr/include/libxml2/**"; MACOSX_DEPLOYMENT_TARGET = 10.8; OTHER_LDFLAGS = "-lxml2"; - SDKROOT = macosx; + SDKROOT = macosx10.8; }; name = Release; }; diff --git a/MacPass/EntryInspectorView.xib b/MacPass/EntryInspectorView.xib index 600bcfd2..171f85f8 100644 --- a/MacPass/EntryInspectorView.xib +++ b/MacPass/EntryInspectorView.xib @@ -1,8 +1,8 @@ - + - + @@ -31,7 +31,7 @@ - + @@ -42,7 +42,7 @@ - + @@ -50,13 +50,13 @@ - + - + - + @@ -84,14 +84,14 @@ - + - + - + @@ -125,7 +125,7 @@ - + @@ -160,7 +160,7 @@ - + @@ -216,11 +216,11 @@ - + + + - + - + @@ -266,14 +269,14 @@ - + - + - + @@ -311,7 +314,7 @@ - + @@ -369,7 +372,7 @@ - + @@ -402,11 +405,11 @@ - + + + @@ -441,7 +447,7 @@ - + @@ -457,9 +463,12 @@ + + + - + @@ -484,9 +493,12 @@ + + + - + @@ -495,7 +507,7 @@ - + @@ -512,6 +524,7 @@ + @@ -525,6 +538,9 @@ NSAllRomanInputSourcesLocaleIdentifier + + + @@ -557,17 +576,20 @@ - - + @@ -576,12 +598,15 @@ @@ -604,7 +632,7 @@ - + @@ -660,7 +688,7 @@ - + diff --git a/MacPass/MPAppDelegate.m b/MacPass/MPAppDelegate.m index 45114ffa..9ae3f715 100644 --- a/MacPass/MPAppDelegate.m +++ b/MacPass/MPAppDelegate.m @@ -16,12 +16,14 @@ #import "MPStripLineBreaksTransformer.h" #import "MPServerDaemon.h" #import "MPLockDaemon.h" +#import "MPAutotypeDaemon.h" #import "MPDocumentWindowController.h" @interface MPAppDelegate () { @private MPServerDaemon *serverDaemon; MPLockDaemon *lockDaemon; + MPAutotypeDaemon *autotypeDaemon; BOOL _restoredWindows; BOOL _shouldOpenFile; } @@ -82,6 +84,7 @@ - (void)applicationDidFinishLaunching:(NSNotification *)notification { serverDaemon = [[MPServerDaemon alloc] init]; lockDaemon = [[MPLockDaemon alloc] init]; + autotypeDaemon = [[MPAutotypeDaemon alloc] init]; //autotypeDaemon = [[MPAutotypeDaemon alloc] init]; BOOL reopen = [[NSUserDefaults standardUserDefaults] boolForKey:kMPSettingsKeyReopenLastDatabaseOnLaunch]; diff --git a/MacPass/MPAutotypeDaemon.h b/MacPass/MPAutotypeDaemon.h new file mode 100644 index 00000000..4c992a4b --- /dev/null +++ b/MacPass/MPAutotypeDaemon.h @@ -0,0 +1,16 @@ +// +// MPAutotypeDaemon.h +// MacPass +// +// Created by Michael Starke on 26.10.13. +// Copyright (c) 2013 HicknHack Software GmbH. All rights reserved. +// + +#import + +/** + * The autotype daemon is repsonsible for registering the globa hotkey and to perform any autotype actions + */ +@interface MPAutotypeDaemon : NSObject + +@end diff --git a/MacPass/MPAutotypeDaemon.m b/MacPass/MPAutotypeDaemon.m new file mode 100644 index 00000000..9b211cab --- /dev/null +++ b/MacPass/MPAutotypeDaemon.m @@ -0,0 +1,261 @@ +// +// MPAutotypeDaemon.m +// MacPass +// +// Created by Michael Starke on 26.10.13. +// Copyright (c) 2013 HicknHack Software GmbH. All rights reserved. +// + +#import "MPAutotypeDaemon.h" +#import "DDHotKeyCenter.h" +#import "MPPasteBoardController.h" +#import "MPDocument.h" + +#import "KPKEntry.h" + +#import + +/* + + Autotype workflow: + + run copy/paste with content + + special keys: + + Tab {TAB} + Enter {ENTER} or ~ + Arrow Up {UP} + Arrow Down {DOWN} + Arrow Left {LEFT} + Arrow Right {RIGHT} + Insert {INSERT} or {INS} + Delete {DELETE} or {DEL} + Home {HOME} + End {END} + Page Up {PGUP} + Page Down {PGDN} + Backspace {BACKSPACE}, {BS} or {BKSP} + Break {BREAK} + Caps-Lock {CAPSLOCK} + Escape {ESC} + Windows Key {WIN} (equ. to {LWIN}) + Windows Key: left, right {LWIN}, {RWIN} + Apps / Menu {APPS} + Help {HELP} + Numlock {NUMLOCK} + Print Screen {PRTSC} + Scroll Lock {SCROLLLOCK} + F1 - F16 {F1} - {F16} + Numeric Keypad + {ADD} + Numeric Keypad - {SUBTRACT} + Numeric Keypad * {MULTIPLY} + Numeric Keypad / {DIVIDE} + Numeric Keypad 0 to 9 {NUMPAD0} to {NUMPAD9} + Shift + + Ctrl ^ + Alt % + + {+} + ^ {^} + % {%} + ~ {~} + (, ) {(}, {)} + [, ] {[}, {]} + {, } {{}, {}} + + special commands: + + {DELAY X} Delays X milliseconds. + {CLEARFIELD} Clears the contents of the edit control that currently has the focus (only single-line edit controls). + {VKEY X} + */ + +@implementation MPAutotypeDaemon + +- (id)init { + self = [super init]; + if (self) { + /* + Test the system for enabled access for assistive devices. Otherwise we cannot work properly + + Use defaults to determine if global hotkey is enabled + [self _registerHotKey]; + */ + } + return self; +} + +- (void)didPressHotKey { + // copy items to pasteboard + NSArray *documents = [NSApp orderedDocuments]; + MPDocument *currentDocument = nil; + for(MPDocument *openDocument in documents) { + if(NO == openDocument.encrypted) { + currentDocument = openDocument; + break; + } + } + + /* + Determine the window title of the current front most application + Start searching the db for the best fit (based on title, then on window associations + */ + NSString *windowTitle = [self _frontMostWindowTitle]; + NSLog(@"Looking for entries matching window title:%@", windowTitle); + + return; + + KPKEntry *selectedEntry = currentDocument.selectedEntry; + if(nil == currentDocument || nil == selectedEntry) { + return; // no open documents, no selected entry + } + + /* Perform Autotype instead of dump pasting */ + MPPasteBoardController *controller = [MPPasteBoardController defaultController]; + if(selectedEntry.username) { + [controller copyObjects:@[selectedEntry.username]]; + [self _pressKey:kVK_ANSI_V modifierFlags:kCGEventFlagMaskCommand]; + } + if(selectedEntry.password) { + [self _pressKey:kVK_Tab modifierFlags:0]; + [controller copyObjects:@[selectedEntry.password]]; + [self _pressKey:kVK_ANSI_V modifierFlags:kCGEventFlagMaskCommand]; + } + [self _pressKey:kVK_Return modifierFlags:0]; +} + +- (void)_registerHotKey { + [[DDHotKeyCenter sharedHotKeyCenter] registerHotKeyWithKeyCode:kVK_ANSI_M + modifierFlags:(NSCommandKeyMask | NSAlternateKeyMask ) + target:self + action:@selector(didPressHotKey) + object:nil]; +} + +- (void)_pressKey:(CGKeyCode)keyCode modifierFlags:(CGEventFlags)flags { + CGEventRef pressKey = CGEventCreateKeyboardEvent (NULL, keyCode, YES); + CGEventRef releaseKey = CGEventCreateKeyboardEvent (NULL, keyCode, NO); + + CGEventSetFlags(pressKey,0); + CGEventSetFlags(releaseKey, 0); + CGEventSetFlags(pressKey,flags); + CGEventSetFlags(releaseKey, flags); + + CGEventPost(kCGSessionEventTap, pressKey); + CGEventPost(kCGSessionEventTap, releaseKey); + + CFRelease(pressKey); + CFRelease(releaseKey); +} + +- (NSString *)_frontMostWindowTitle { + NSRunningApplication *frontApplication = [[NSWorkspace sharedWorkspace] frontmostApplication]; + + NSArray *currentWindows = CFBridgingRelease(CGWindowListCopyWindowInfo(kCGWindowListOptionOnScreenOnly | kCGWindowListExcludeDesktopElements, kCGNullWindowID)); + for(NSDictionary *windowDict in currentWindows) { + NSNumber *processId = windowDict[(NSString *)kCGWindowOwnerPID]; + if(processId && [processId isEqualToNumber:@(frontApplication.processIdentifier)]) { + return windowDict[(NSString *)kCGWindowName]; + } + } + return nil; +} + +/* + cody by Joe Turner http://www.cocoabuilder.com/archive/cocoa/242992-detect-keyboard-layout-for-cgkeycodes.html#243168 + */ +- (CGKeyCode)keyCodeForKeyboard:(const UCKeyboardLayout *)uchrHeader character:(NSString *)character { + if ([character isEqualToString:@"RETURN"]) return kVK_Return; + if ([character isEqualToString:@"TAB"]) return kVK_Tab; + if ([character isEqualToString:@"SPACE"]) return kVK_Space; + if ([character isEqualToString:@"DELETE"]) return kVK_Delete; + if ([character isEqualToString:@"ESCAPE"]) return kVK_Escape; + if ([character isEqualToString:@"F5"]) return kVK_F5; + if ([character isEqualToString:@"F6"]) return kVK_F6; + if ([character isEqualToString:@"F7"]) return kVK_F7; + if ([character isEqualToString:@"F3"]) return kVK_F3; + if ([character isEqualToString:@"F8"]) return kVK_F8; + if ([character isEqualToString:@"F9"]) return kVK_F9; + if ([character isEqualToString:@"F11"]) return kVK_F11; + if ([character isEqualToString:@"F13"]) return kVK_F13; + if ([character isEqualToString:@"F16"]) return kVK_F16; + if ([character isEqualToString:@"F14"]) return kVK_F14; + if ([character isEqualToString:@"F10"]) return kVK_F10; + if ([character isEqualToString:@"F12"]) return kVK_F12; + if ([character isEqualToString:@"F15"]) return kVK_F15; + if ([character isEqualToString:@"HELP"]) return kVK_Help; + if ([character isEqualToString:@"HOME"]) return kVK_Home; + if ([character isEqualToString:@"PAGE UP"]) return kVK_PageUp; + if ([character isEqualToString:@"FORWARD DELETE"]) return kVK_ForwardDelete; + if ([character isEqualToString:@"F4"]) return kVK_F4; + if ([character isEqualToString:@"END"]) return kVK_End; + if ([character isEqualToString:@"F2"]) return kVK_F2; + if ([character isEqualToString:@"PAGE DOWN"]) return kVK_PageDown; + if ([character isEqualToString:@"F1"]) return kVK_F1; + if ([character isEqualToString:@"LEFT"]) return kVK_LeftArrow; + if ([character isEqualToString:@"RIGHT"]) return kVK_RightArrow; + if ([character isEqualToString:@"DOWN"]) return kVK_DownArrow; + if ([character isEqualToString:@"UP"]) return kVK_UpArrow; + + UTF16Char theCharacter = [character characterAtIndex:0]; + long i, j, k; + unsigned char *uchrData = (unsigned char *)uchrHeader; + UCKeyboardTypeHeader *uchrTable = uchrHeader->keyboardTypeList; + BOOL found = NO; + UInt16 virtualKeyCode; + + for (i = 0; i < (uchrHeader->keyboardTypeCount) && !found; i++) { + UCKeyToCharTableIndex *uchrKeyIX; + UCKeyStateRecordsIndex *stateRecordsIndex; + + if (uchrTable[i].keyStateRecordsIndexOffset != 0 ) { + stateRecordsIndex = (UCKeyStateRecordsIndex *) (((unsigned char*)uchrData) + (uchrTable[i].keyStateRecordsIndexOffset)); + + if ((stateRecordsIndex->keyStateRecordsIndexFormat) != kUCKeyStateRecordsIndexFormat) { + stateRecordsIndex = NULL; + } + } + else { + stateRecordsIndex = NULL; + } + + uchrKeyIX = (UCKeyToCharTableIndex *)(((unsigned char *)uchrData) + (uchrTable[i].keyToCharTableIndexOffset)); + + if (kUCKeyToCharTableIndexFormat == (uchrKeyIX-> keyToCharTableIndexFormat)) { + for (j = 0; j < (uchrKeyIX->keyToCharTableCount) && !found; j++) { + UCKeyOutput *keyToCharData = (UCKeyOutput *) ( ((unsigned char*)uchrData) + (uchrKeyIX->keyToCharTableOffsets[j]) ); + + for (k = 0; k < (uchrKeyIX->keyToCharTableSize) && !found; k++) { + if (((keyToCharData[k]) & kUCKeyOutputTestForIndexMask) == kUCKeyOutputStateIndexMask) { + long theIndex = (kUCKeyOutputGetIndexMask & keyToCharData[k]); + + if (stateRecordsIndex != NULL && theIndex <= stateRecordsIndex-> keyStateRecordCount) { + UCKeyStateRecord *theStateRecord = (UCKeyStateRecord *) (((unsigned char *) uchrData) + (stateRecordsIndex-> keyStateRecordOffsets[theIndex])); + + if ((theStateRecord->stateZeroCharData) == theCharacter) { + virtualKeyCode = k; + found = YES; + } + } else { + if ((keyToCharData[k]) == theCharacter) { + virtualKeyCode = k; + found = YES; + } + } + } else if (((keyToCharData[k]) & kUCKeyOutputTestForIndexMask) == kUCKeyOutputSequenceIndexMask) { + } else if ( (keyToCharData[k]) == 0xFFFE || (keyToCharData[k]) == 0xFFFF ) { + } else { + if ((keyToCharData[k]) == theCharacter) { + virtualKeyCode = k; + found = YES; + } + } + } + } + } + } + return (CGKeyCode)virtualKeyCode; +} + + +@end diff --git a/MacPass/MPDatabaseSettingsWindowController.m b/MacPass/MPDatabaseSettingsWindowController.m index 5d95d8e8..248d76a5 100644 --- a/MacPass/MPDatabaseSettingsWindowController.m +++ b/MacPass/MPDatabaseSettingsWindowController.m @@ -19,7 +19,7 @@ #import "KPKTree.h" #import "KPKMetaData.h" #import "KPKNode+IconImage.h" -#import "KPKPassword.h" +#import "KPKCompositeKey.h" #import "HNHRoundedTextField.h" #import "HNHRoundedSecureTextField.h" @@ -121,7 +121,7 @@ - (IBAction)benchmarkRounds:(id)sender { [self.benchmarkButton setEnabled:NO]; - [KPKPassword benchmarkTransformationRounds:1 completionHandler:^(NSUInteger rounds) { + [KPKCompositeKey benchmarkTransformationRounds:1 completionHandler:^(NSUInteger rounds) { [self.encryptionRoundsTextField setIntegerValue:rounds]; [self.benchmarkButton setEnabled:YES]; }]; diff --git a/MacPass/MPDocument.h b/MacPass/MPDocument.h index dc4b966d..93aae98b 100644 --- a/MacPass/MPDocument.h +++ b/MacPass/MPDocument.h @@ -23,10 +23,10 @@ APPKIT_EXTERN NSString *const MPDocumentGroupKey; @class KPKTree; @class KPKBinary; @class KPKAttribute; +@class KPKCompositeKey; @interface MPDocument : NSDocument -@property (assign, readonly) BOOL hasPasswordOrKey; @property (nonatomic, readonly, assign) BOOL encrypted; @property (strong, readonly, nonatomic) KPKTree *tree; @@ -34,8 +34,9 @@ APPKIT_EXTERN NSString *const MPDocumentGroupKey; @property (weak, readonly) KPKGroup *trash; @property (weak, readonly) KPKGroup *templates; -@property (nonatomic, copy) NSString *password; -@property (nonatomic, strong) NSURL *key; +@property (nonatomic, strong) KPKCompositeKey *compositeKey; +//@property (nonatomic, copy) NSString *password; +//@property (nonatomic, strong) NSURL *key; @property (assign, readonly, getter = isReadOnly) BOOL readOnly; @property (nonatomic, readonly, assign) KPKVersion versionForFileType; diff --git a/MacPass/MPDocument.m b/MacPass/MPDocument.m index 4817658c..b3008cf3 100644 --- a/MacPass/MPDocument.m +++ b/MacPass/MPDocument.m @@ -22,7 +22,7 @@ #import "KPKGroup.h" #import "KPKTree.h" #import "KPKTree+Serializing.h" -#import "KPKPassword.h" +#import "KPKCompositeKey.h" #import "KPKMetaData.h" #import "KPKAttribute.h" @@ -51,7 +51,6 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { @property (strong, nonatomic) KPKTree *tree; @property (weak, nonatomic) KPKGroup *root; -@property (assign, nonatomic) BOOL hasPasswordOrKey; @property (assign) BOOL readOnly; @property (strong) NSURL *lockFileURL; @@ -97,7 +96,6 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { if(self) { _encryptedData = nil; _didLockFile = NO; - _hasPasswordOrKey = NO; _readOnly = NO; self.tree = [KPKTree templateTree]; } @@ -119,10 +117,9 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { } - (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError { - if(!self.hasPasswordOrKey) { + if(!self.compositeKey.hasPasswordOrKeyFile) { return NO; // No password or key. No save possible } - KPKPassword *password = [[KPKPassword alloc] initWithPassword:self.password key:self.key]; NSString *fileType = [self fileTypeFromLastRunSavePanel]; KPKVersion version = [[self class] versionForFileType:fileType]; if(version == KPKUnknownVersion) { @@ -131,7 +128,7 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { } return NO; } - NSData *treeData = [self.tree encryptWithPassword:password forVersion:version error:outError]; + NSData *treeData = [self.tree encryptWithPassword:self.compositeKey forVersion:version error:outError]; if(![treeData writeToURL:url options:0 error:outError]) { NSLog(@"%@", [*outError localizedDescription]); return NO; @@ -216,25 +213,23 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { #pragma mark Lock/Unlock/Decrypt - (BOOL)unlockWithPassword:(NSString *)password keyFileURL:(NSURL *)keyFileURL error:(NSError *__autoreleasing*)error{ - KPKPassword *passwordData = [[KPKPassword alloc] initWithPassword:password key:keyFileURL]; - - self.key = keyFileURL; - self.password = [password length] > 0 ? password : nil; - - self.tree = [[KPKTree alloc] initWithData:_encryptedData password:passwordData error:error]; + self.compositeKey = [[KPKCompositeKey alloc] initWithPassword:password key:keyFileURL]; + self.tree = [[KPKTree alloc] initWithData:_encryptedData password:self.compositeKey error:error]; BOOL isUnlocked = (nil != self.tree); if(isUnlocked) { [[NSNotificationCenter defaultCenter] postNotificationName:MPDocumentDidUnlockDatabaseNotification object:self]; } + else { + self.compositeKey = nil; // clear the key? + } return isUnlocked; } - (void)lockDatabase:(id)sender { - KPKPassword *password = [[KPKPassword alloc] initWithPassword:self.password key:self.key]; NSError *error; /* Locking needs to be lossless hence just use the XML format */ - _encryptedData = [self.tree encryptWithPassword:password forVersion:KPKXmlVersion error:&error]; + _encryptedData = [self.tree encryptWithPassword:self.compositeKey forVersion:KPKXmlVersion error:&error]; self.tree = nil; } @@ -251,20 +246,6 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { return self.tree.root; } -- (void)setPassword:(NSString *)password { - if(![_password isEqualToString:password]) { - _password = [password copy]; - [self _updateIsSecured]; - } -} - -- (void)setKey:(NSURL *)key { - if(![[_key absoluteString] isEqualToString:[key absoluteString]]) { - _key = key; - [self _updateIsSecured]; - } -} - - (void)setSelectedGroup:(KPKGroup *)selectedGroup { if(_selectedGroup != selectedGroup) { _selectedGroup = selectedGroup; @@ -485,13 +466,6 @@ typedef NS_ENUM(NSUInteger, MPAlertType) { return [super validateUserInterfaceItem:anItem]; } -#pragma mark Private -- (void)_updateIsSecured { - BOOL securePassword = ([self.password length] > 0); - BOOL secureKey = (nil != self.key); - self.hasPasswordOrKey = (secureKey || securePassword); -} - - (void)_cleanupLock { if(_didLockFile) { [[NSFileManager defaultManager] removeItemAtURL:_lockFileURL error:nil]; diff --git a/MacPass/MPDocumentWindowController.m b/MacPass/MPDocumentWindowController.m index dfed6ac5..0041968d 100644 --- a/MacPass/MPDocumentWindowController.m +++ b/MacPass/MPDocumentWindowController.m @@ -23,6 +23,7 @@ #import "MPContextToolbarButton.h" #import "KPKTree.h" +#import "KPKCompositeKey.h" typedef NS_ENUM(NSUInteger, MPAlertContext) { MPAlertLossySaveWarning, @@ -179,7 +180,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { return; } } - else if(!document.hasPasswordOrKey) { + else if(!document.compositeKey) { _saveAfterPasswordChange = YES; [self editPassword:sender]; return; @@ -237,7 +238,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { return valid; } case MPActionLock: - return document.hasPasswordOrKey; + return document.compositeKey.hasPasswordOrKeyFile; case MPActionToggleInspector: return (nil != [_splitView superview]); @@ -266,7 +267,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { return valid; } case MPActionLock: - return document.hasPasswordOrKey; + return document.compositeKey.hasPasswordOrKeyFile; case MPActionToggleInspector: return (nil != [_splitView superview]); @@ -309,7 +310,7 @@ typedef NS_ENUM(NSUInteger, MPAlertContext) { - (IBAction)lock:(id)sender { MPDocument *document = [self document]; - if(!document.hasPasswordOrKey) { + if(!document.compositeKey.hasPasswordOrKeyFile) { return; // Document needs a password/keyfile to be lockable } if(document.encrypted) { diff --git a/MacPass/MPEntryInspectorViewController.m b/MacPass/MPEntryInspectorViewController.m index d8cfd1e5..02693082 100644 --- a/MacPass/MPEntryInspectorViewController.m +++ b/MacPass/MPEntryInspectorViewController.m @@ -79,13 +79,15 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) { [scrollView setTranslatesAutoresizingMaskIntoConstraints:NO]; NSView *clipView = [scrollView contentView]; - NSView *tabView = [[self.tabView tabViewItemAtIndex:MPEntryTabGeneral] view]; + NSTabViewItem *tabViewItem = [self.tabView tabViewItemAtIndex:MPEntryTabGeneral]; + NSView *tabView = [tabViewItem view]; /* DO NEVER SET setTranslatesAutoresizingMaskIntoConstraints on NSTabViewItem's view [tabView setTranslatesAutoresizingMaskIntoConstraints:NO]; */ [scrollView setDocumentView:self.generalView]; [tabView addSubview:scrollView]; + [tabViewItem setInitialFirstResponder:scrollView]; NSDictionary *views = NSDictionaryOfVariableBindings(_generalView, scrollView); [[scrollView superview] addConstraints:[NSLayoutConstraint constraintsWithVisualFormat:@"H:|[scrollView]|" diff --git a/MacPass/MPPasswordEditWindowController.m b/MacPass/MPPasswordEditWindowController.m index 9193d359..c4e2ed26 100644 --- a/MacPass/MPPasswordEditWindowController.m +++ b/MacPass/MPPasswordEditWindowController.m @@ -14,6 +14,7 @@ #import "NSData+Keyfile.h" #import "KPKTree.h" +#import "KPKCompositeKey.h" @interface MPPasswordEditWindowController () @@ -47,9 +48,10 @@ return; } self.showPassword = NO; - [self.passwordTextField setStringValue:_currentDocument.password ? _currentDocument.password : @""]; - [self.passwordRepeatTextField setStringValue:[self.passwordTextField stringValue]]; - self.keyURL = _currentDocument.key; + // TODO: fix initial view for password edit + //[self.passwordTextField setStringValue:_currentDocument.password ? _currentDocument.password : @""]; + //[self.passwordRepeatTextField setStringValue:[self.passwordTextField stringValue]]; + //self.keyURL = _currentDocument.key; NSDictionary *negateOption = @{ NSValueTransformerNameBindingOption : NSNegateBooleanTransformerName }; [self.passwordTextField bind:@"showPassword" toObject:self withKeyPath:@"showPassword" options:nil]; @@ -82,8 +84,7 @@ #pragma mark Actions - (IBAction)save:(id)sender { - _currentDocument.password = [self.passwordTextField stringValue]; - _currentDocument.key = [self.keyfilePathControl URL]; + _currentDocument.compositeKey = [[KPKCompositeKey alloc] initWithPassword:[self.passwordTextField stringValue] key:[self.keyfilePathControl URL]]; [self dismissSheet:NSRunStoppedResponse]; if(self.delegate && [self.delegate respondsToSelector:@selector(didFinishPasswordEditing:)]) { [self.delegate didFinishPasswordEditing:YES]; diff --git a/MacPassTests/KPKTestLegacyLoading.m b/MacPassTests/KPKTestLegacyLoading.m index be547669..972c1803 100644 --- a/MacPassTests/KPKTestLegacyLoading.m +++ b/MacPassTests/KPKTestLegacyLoading.m @@ -9,7 +9,7 @@ #import "KPKTestLegacyLoading.h" #import "KPKTree+Serializing.h" -#import "KPKPassword.h" +#import "KPKCompositeKey.h" #import "KPKMetaData.h" #import "KPKIcon.h" @@ -18,14 +18,14 @@ @implementation KPKTestLegacyLoading - (void)testValidFile { - KPKPassword *password = [[KPKPassword alloc] initWithPassword:@"1234" key:nil]; + KPKCompositeKey *password = [[KPKCompositeKey alloc] initWithPassword:@"1234" key:nil]; NSData *data = [self _loadTestDataBase:@"Test_Password_1234" extension:@"kdb"]; KPKTree *tree = [[KPKTree alloc] initWithData:data password:password error:NULL]; STAssertNotNil(tree, @"Loading should result in a tree object"); } - (void)testWrongPassword { - KPKPassword *password = [[KPKPassword alloc] initWithPassword:@"wrongPassword" key:nil]; + KPKCompositeKey *password = [[KPKCompositeKey alloc] initWithPassword:@"wrongPassword" key:nil]; NSData *data = [self _loadTestDataBase:@"KeePass1_native_test" extension:@"kdb"]; NSError *error; KPKTree *tree = [[KPKTree alloc] initWithData:data password:password error:&error]; @@ -47,7 +47,7 @@ - (void)testMetaParsing { NSData *data = [self _loadTestDataBase:@"KDB1_KeePassX_test" extension:@"kdb"]; - KPKPassword *password = [[KPKPassword alloc] initWithPassword:@"test" key:nil]; + KPKCompositeKey *password = [[KPKCompositeKey alloc] initWithPassword:@"test" key:nil]; KPKTree *tree = [[KPKTree alloc] initWithData:data password:password error:NULL]; STAssertNotNil(tree, @"Tree shoudl be loaded" ); diff --git a/MacPassTests/KPKTestLegacyWriting.h b/MacPassTests/KPKTestLegacyWriting.h index e3d35ae1..c5e60ecf 100644 --- a/MacPassTests/KPKTestLegacyWriting.h +++ b/MacPassTests/KPKTestLegacyWriting.h @@ -7,7 +7,7 @@ // #import -@class KPKPassword; +@class KPKCompositeKey; @interface KPKTestLegacyWriting : SenTestCase diff --git a/MacPassTests/KPKTestLegacyWriting.m b/MacPassTests/KPKTestLegacyWriting.m index f62f04ef..d20f5d1f 100644 --- a/MacPassTests/KPKTestLegacyWriting.m +++ b/MacPassTests/KPKTestLegacyWriting.m @@ -8,7 +8,7 @@ #import "KPKTestLegacyWriting.h" -#import "KPKPassword.h" +#import "KPKCompositeKey.h" #import "KPKTree+Serializing.h" @implementation KPKTestLegacyWriting @@ -16,7 +16,7 @@ - (void)testWriting { NSError __autoreleasing *error = nil; NSURL *dbUrl = [self _urlForFile:@"CustomIcon_Password_1234" extension:@"kdbx"]; - KPKPassword *password = [[KPKPassword alloc] initWithPassword:@"1234" key:nil]; + KPKCompositeKey *password = [[KPKCompositeKey alloc] initWithPassword:@"1234" key:nil]; KPKTree *tree = [[KPKTree alloc] initWithContentsOfUrl:dbUrl password:password error:&error]; STAssertNotNil(tree, @"Tree should be created"); error = nil; diff --git a/MacPassTests/KPKTestXmlLoading.h b/MacPassTests/KPKTestXmlLoading.h index 02a80956..a40789c4 100644 --- a/MacPassTests/KPKTestXmlLoading.h +++ b/MacPassTests/KPKTestXmlLoading.h @@ -7,12 +7,12 @@ // #import -@class KPKPassword; +@class KPKCompositeKey; @interface KPKTestXmlLoading : SenTestCase { @private NSData *_data; - KPKPassword *_password; + KPKCompositeKey *_password; } @end diff --git a/MacPassTests/KPKTestXmlLoading.m b/MacPassTests/KPKTestXmlLoading.m index 6dbc9612..a1682bcf 100644 --- a/MacPassTests/KPKTestXmlLoading.m +++ b/MacPassTests/KPKTestXmlLoading.m @@ -7,7 +7,7 @@ // #import "KPKTestXmlLoading.h" -#import "KPKPassword.h" +#import "KPKCompositeKey.h" #import "KPKTree+Serializing.h" #import "KPKEntry.h" @@ -19,7 +19,7 @@ NSBundle *myBundle = [NSBundle bundleForClass:[self class]]; NSURL *url = [myBundle URLForResource:@"Test_Password_1234" withExtension:@"kdbx"]; _data = [NSData dataWithContentsOfURL:url]; - _password = [[KPKPassword alloc] initWithPassword:@"1234" key:nil]; + _password = [[KPKCompositeKey alloc] initWithPassword:@"1234" key:nil]; } - (void)tearDown { @@ -39,7 +39,7 @@ - (void)testAutotypeLoading { NSBundle *myBundle = [NSBundle bundleForClass:[self class]]; NSURL *url = [myBundle URLForResource:@"Autotype_test" withExtension:@"kdbx"]; - KPKPassword *password = [[KPKPassword alloc] initWithPassword:@"test" key:nil]; + KPKCompositeKey *password = [[KPKCompositeKey alloc] initWithPassword:@"test" key:nil]; NSError *error; KPKTree *tree = [[KPKTree alloc] initWithContentsOfUrl:url password:password error:&error]; STAssertNotNil(tree, @"Tree shoud be loaded"); diff --git a/MacPassTests/KPKTestXmlWriting.m b/MacPassTests/KPKTestXmlWriting.m index 3d844d7a..d0146c8e 100644 --- a/MacPassTests/KPKTestXmlWriting.m +++ b/MacPassTests/KPKTestXmlWriting.m @@ -7,7 +7,7 @@ // #import "KPKTestXmlWriting.h" -#import "KPKPassword.h" +#import "KPKCompositeKey.h" #import "KPKTree+Serializing.h" @implementation KPKTestXmlWriting @@ -15,7 +15,7 @@ - (void)testXmlWriting { NSData *data = [self _loadTestDataBase:@"CustomIcon_Password_1234" extension:@"kdbx"]; NSError *error; - KPKPassword *password = [[KPKPassword alloc] initWithPassword:@"1234" key:nil]; + KPKCompositeKey *password = [[KPKCompositeKey alloc] initWithPassword:@"1234" key:nil]; KPKTree *tree = [[KPKTree alloc] initWithData:data password:password error:&error]; error = nil; NSData *saveData = [tree encryptWithPassword:password forVersion:KPKXmlVersion error:&error];