diff --git a/KeePassKit b/KeePassKit index 6c63ae18..9adeb5a5 160000 --- a/KeePassKit +++ b/KeePassKit @@ -1 +1 @@ -Subproject commit 6c63ae1896ce96ff31bd33781028f4c17880f998 +Subproject commit 9adeb5a5d42f0f19425b8d8e5c6854ea806364c3 diff --git a/MacPass/AutotypeCandidateSelectionWindow.xib b/MacPass/AutotypeCandidateSelectionWindow.xib index d845b10d..bc6b74a8 100644 --- a/MacPass/AutotypeCandidateSelectionWindow.xib +++ b/MacPass/AutotypeCandidateSelectionWindow.xib @@ -65,7 +65,7 @@ DQ - + diff --git a/MacPass/Base.lproj/EntryInspectorView.xib b/MacPass/Base.lproj/EntryInspectorView.xib index d055fd41..bd111c28 100644 --- a/MacPass/Base.lproj/EntryInspectorView.xib +++ b/MacPass/Base.lproj/EntryInspectorView.xib @@ -1,8 +1,8 @@ - + - + @@ -19,6 +19,7 @@ + @@ -673,31 +674,33 @@ - + - - + - + @@ -705,15 +708,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + - + - + - + @@ -758,10 +785,10 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/MacPass/MPAutotypeCommand.m b/MacPass/MPAutotypeCommand.m index 64f73d10..d293a8bd 100644 --- a/MacPass/MPAutotypeCommand.m +++ b/MacPass/MPAutotypeCommand.m @@ -16,12 +16,16 @@ #import "MPAutotypeContext.h" #import "MPKeyMapper.h" +#import "KPKEntry.h" +#import "KPKAutotype.h" #import "KPKAutotypeCommands.h" #import "NSString+Commands.h" #import +#import + @interface NSNumber (AutotypeCommand) - (CGEventFlags)eventFlagsValue; @@ -127,14 +131,14 @@ NSRange pasteRange = NSMakeRange(lastLocation, commandRange.location - lastLocation); if(pasteRange.length > 0) { NSString *pasteValue = [context.evaluatedCommand substringWithRange:pasteRange]; - [self appendPasteCommandForContent:pasteValue toCommands:commands]; + [self appendAppropriatePasteCommandForEntry:context.entry withContent:pasteValue toCommands:commands]; } } /* Test for modifer Key */ NSString *commandString = [context.evaluatedCommand substringWithRange:commandRange]; /* append commands for non-modifer keys */ if(![self updateModifierMask:&collectedModifers forCommand:commandString]) { - [self appendCommandForString:commandString toCommands:commands activeModifer:collectedModifers]; + [self appendCommandForEntry:context.entry withString:commandString toCommands:commands activeModifer:collectedModifers]; collectedModifers = 0; // Reset the modifers; } lastLocation = commandRange.location + commandRange.length; @@ -144,27 +148,102 @@ NSRange pasteRange = NSMakeRange(lastLocation, [context.evaluatedCommand length] - lastLocation); if(pasteRange.length > 0) { NSString *pasteValue = [context.evaluatedCommand substringWithRange:pasteRange]; - [self appendPasteCommandForContent:pasteValue toCommands:commands]; + [self appendAppropriatePasteCommandForEntry:context.entry withContent:pasteValue toCommands:commands]; } } return commands; } ++ (void)appendAppropriatePasteCommandForEntry:(KPKEntry *)entry withContent:(NSString *)pasteContent toCommands:(NSMutableArray *)commands +{ + if (entry.autotype.obfuscateDataTransfer) + [self appendObfuscatedPasteCommandForContent:pasteContent toCommands:commands]; + else + [self appendPasteCommandForContent:pasteContent toCommands:commands]; +} + + (void)appendPasteCommandForContent:(NSString *)pasteContent toCommands:(NSMutableArray *)commands { + /* Update an already inserted paste command with the new conents */ + if([[commands lastObject] isKindOfClass:[MPAutotypePaste class]]) { + [[commands lastObject] appendString:pasteContent]; + } + else { + MPAutotypePaste *pasteCommand = [[MPAutotypePaste alloc] initWithString:pasteContent]; + [commands addObject:pasteCommand]; + } +} + ++ (void)appendObfuscatedPasteCommandForContent:(NSString *)pasteContent toCommands:(NSMutableArray *)commands { if(pasteContent) { - /* Update an already inserted paste command with the new conents */ - if([[commands lastObject] isKindOfClass:[MPAutotypePaste class]]) { - [[commands lastObject] appendString:pasteContent]; + + /* + * obfuscate entered data using Two-Channel Auto-Type Obfuscation + * refer to KeePass documentation for more information + * http://keepass.info/help/v2/autotype_obfuscation.html + */ + + NSString *paste = @""; + NSMutableArray *typeKeys = [NSMutableArray array]; + NSMutableArray *modifiers = [NSMutableArray array]; + + /* + * seed the random number generator using the first 4 bytes of the string's SHA1 + * this ensures that you get the same string split every time for a given string + */ + const char *cstr = [pasteContent cStringUsingEncoding:NSUTF8StringEncoding]; + NSData *data = [NSData dataWithBytes:cstr length:pasteContent.length]; + uint8_t digest[CC_SHA1_DIGEST_LENGTH]; + CC_SHA1(data.bytes, (unsigned int)data.length, digest); + srandom(*((unsigned int*)digest)); + + for (NSUInteger i = 0; i < pasteContent.length; i++) { + NSUInteger part = random() % 2; + + unichar key = [pasteContent characterAtIndex:i]; + CGKeyCode keyCode = [MPKeyMapper keyCodeForCharacter:[NSString stringWithFormat:@"%c", key]]; + + /* append unknown keycodes to the paste since we can't type them */ + if (part == 0 || keyCode == kMPUnknownKeyCode) { + paste = [paste stringByAppendingFormat:@"%c", key]; + + [typeKeys addObject:@(kVK_RightArrow)]; + [modifiers addObject:@0]; + } + else { + [typeKeys addObject:@(keyCode)]; + + if ([[NSCharacterSet uppercaseLetterCharacterSet] characterIsMember:key]) + [modifiers addObject:@(kCGEventFlagMaskShift)]; + else + [modifiers addObject:@0]; + } } - else { - MPAutotypePaste *pasteCommand = [[MPAutotypePaste alloc] initWithString:pasteContent]; - [commands addObject:pasteCommand]; + + /* move to the end of the content */ + for (NSUInteger i = typeKeys.count; i < pasteContent.length; i++) { + [typeKeys addObject:@(kVK_RightArrow)]; + [modifiers addObject:@0]; + } + + /* add paste command */ + MPAutotypePaste *pasteCommand = [[MPAutotypePaste alloc] initWithString:paste]; + [commands addObject:pasteCommand]; + + /* add keypress commands */ + if (typeKeys.count > 0) { + for (NSUInteger i = 0; i < paste.length; i++) { + [commands addObject:[[MPAutotypeKeyPress alloc] initWithModifierMask:0 keyCode:kVK_LeftArrow]]; + } + + for (NSUInteger i = 0; i < typeKeys.count; i++) { + [commands addObject:[[MPAutotypeKeyPress alloc] initWithModifierMask:[modifiers[i] longLongValue] keyCode:[typeKeys[i] unsignedShortValue]]]; + } } } } -+ (void)appendCommandForString:(NSString *)commandString toCommands:(NSMutableArray *)commands activeModifer:(CGEventFlags)flags { ++ (void)appendCommandForEntry:(KPKEntry *)entry withString:(NSString *)commandString toCommands:(NSMutableArray *)commands activeModifer:(CGEventFlags)flags { if(nil == commandString) { return; // Nothing to parse } @@ -209,7 +288,7 @@ } } else { - [self appendPasteCommandForContent:commandString toCommands:commands]; + [self appendAppropriatePasteCommandForEntry:entry withContent:commandString toCommands:commands]; } } @@ -240,7 +319,7 @@ /* Send the event */ CGEventPost(kCGSessionEventTap, pressKey); - usleep(100000); + usleep(0.05 * NSEC_PER_MSEC); CGEventPost(kCGSessionEventTap, releaseKey); CFRelease(pressKey); diff --git a/MacPass/MPAutotypeDaemon.m b/MacPass/MPAutotypeDaemon.m index 299befb5..c4eb1c74 100644 --- a/MacPass/MPAutotypeDaemon.m +++ b/MacPass/MPAutotypeDaemon.m @@ -178,17 +178,12 @@ NSString *const kMPProcessIdentifierKey = @"kMPProcessIdentifierKey"; NSArray *commands = [MPAutotypeCommand commandsForContext:context]; if([MPAutotypeDaemon _orderApplicationToFront:self.targetPID]) { /* Sleep a bit after the app was activated */ - NSLog(@"App wasn frontmost, did order it there. Wating a bit."); - usleep(1000*500); + NSLog(@"App wasn't frontmost, did order it there. Waiting a bit."); + usleep(0.5 * NSEC_PER_MSEC); NSLog(@"Done waiting."); } - BOOL lastCommandWasPaste = NO; for(MPAutotypeCommand *command in commands) { - if(lastCommandWasPaste) { - usleep(1000*1000); - } [command execute]; - lastCommandWasPaste = [command isKindOfClass:[MPAutotypePaste class]]; } }); } diff --git a/MacPass/MPAutotypePaste.m b/MacPass/MPAutotypePaste.m index ac376b46..dc5578f2 100644 --- a/MacPass/MPAutotypePaste.m +++ b/MacPass/MPAutotypePaste.m @@ -35,8 +35,10 @@ - (void)execute { if([self.pasteData length] > 0) { MPPasteBoardController *controller = [MPPasteBoardController defaultController]; - [controller copyObjects:@[self.pasteData]]; + [controller stashObjects]; + [controller copyObjectsWithoutTimeout:@[self.pasteData]]; [self sendPasteKeyCode]; + [controller restoreObjects]; } } diff --git a/MacPass/MPEntryInspectorViewController.h b/MacPass/MPEntryInspectorViewController.h index 1485b446..56b67fd2 100644 --- a/MacPass/MPEntryInspectorViewController.h +++ b/MacPass/MPEntryInspectorViewController.h @@ -38,6 +38,7 @@ /* Autotype */ @property (weak) IBOutlet NSButton *enableAutotypeCheckButton; +@property (weak) IBOutlet NSButton *obfuscateAutotypeCheckButton; @property (weak) IBOutlet NSTableView *windowAssociationsTableView; @property (weak) IBOutlet NSTextField *customEntrySequenceTextField; @property (weak) IBOutlet NSComboBox *windowTitleComboBox; diff --git a/MacPass/MPEntryInspectorViewController.m b/MacPass/MPEntryInspectorViewController.m index a5f25b69..fcf33d53 100644 --- a/MacPass/MPEntryInspectorViewController.m +++ b/MacPass/MPEntryInspectorViewController.m @@ -415,6 +415,7 @@ typedef NS_ENUM(NSUInteger, MPEntryTab) { - (void)_bindAutotype { if(self.entry) { [self.enableAutotypeCheckButton bind:NSValueBinding toObject:self.entry.autotype withKeyPath:NSStringFromSelector(@selector(isEnabled)) options:nil]; + [self.obfuscateAutotypeCheckButton bind:NSValueBinding toObject:self.entry.autotype withKeyPath:NSStringFromSelector(@selector(obfuscateDataTransfer)) options:nil]; [self.customEntrySequenceTextField bind:NSEnabledBinding toObject:self.entry.autotype withKeyPath:NSStringFromSelector(@selector(isEnabled)) options:nil]; [self.customEntrySequenceTextField bind:NSValueBinding toObject:self.entry.autotype withKeyPath:NSStringFromSelector(@selector(defaultKeystrokeSequence)) options:nil]; [_windowAssociationsController bind:NSContentArrayBinding toObject:self.entry.autotype withKeyPath:NSStringFromSelector(@selector(associations)) options:nil]; diff --git a/MacPass/MPPasteBoardController.h b/MacPass/MPPasteBoardController.h index 66ab6740..dfadbcaf 100644 --- a/MacPass/MPPasteBoardController.h +++ b/MacPass/MPPasteBoardController.h @@ -29,6 +29,9 @@ FOUNDATION_EXPORT NSString *const MPPasteBoardControllerDidClearClipboard; + (MPPasteBoardController *)defaultController; +- (void)stashObjects; +- (void)restoreObjects; - (void)copyObjects:(NSArray *)objects; +- (void)copyObjectsWithoutTimeout:(NSArray *)objects; @end diff --git a/MacPass/MPPasteBoardController.m b/MacPass/MPPasteBoardController.m index 6362776c..abb77a20 100644 --- a/MacPass/MPPasteBoardController.m +++ b/MacPass/MPPasteBoardController.m @@ -16,6 +16,7 @@ NSString *const MPPasteBoardControllerDidClearClipboard = @"com.hicknhack.macpas @interface MPPasteBoardController () @property (assign) BOOL isEmpty; +@property (nonatomic, strong) NSMutableArray *stashedObjects; @end @@ -56,15 +57,6 @@ NSString *const MPPasteBoardControllerDidClearClipboard = @"com.hicknhack.macpas } } -- (void)setClearTimeout:(NSTimeInterval)clearTimeout { - if(_clearTimeout != clearTimeout) { - if(clearTimeout > 0) { - [self _clearPasteboardContents]; - } - _clearTimeout = clearTimeout; - } -} - - (void)setClearPasteboardOnShutdown:(BOOL)clearPasteboardOnShutdown { if(_clearPasteboardOnShutdown != clearPasteboardOnShutdown ) { _clearPasteboardOnShutdown = !_clearPasteboardOnShutdown; @@ -72,17 +64,47 @@ NSString *const MPPasteBoardControllerDidClearClipboard = @"com.hicknhack.macpas } } +- (void)stashObjects +{ + self.stashedObjects = [NSMutableArray array]; + for (NSPasteboardItem *item in [[NSPasteboard generalPasteboard] pasteboardItems]) { + NSPasteboardItem *newItem = [[NSPasteboardItem alloc] init]; + for (NSString *type in [item types]) { + NSData *data = [[item dataForType:type] mutableCopy]; + if (data) { + [newItem setData:data forType:type]; + } + } + [self.stashedObjects addObject:newItem]; + } +} + +- (void)restoreObjects +{ + if (self.stashedObjects) + { + [[NSPasteboard generalPasteboard] clearContents]; + [[NSPasteboard generalPasteboard] writeObjects:self.stashedObjects]; + self.stashedObjects = nil; + self.isEmpty = YES; + } +} + - (void)copyObjects:(NSArray *)objects { - /* Should we save the old content ?*/ - [[NSPasteboard generalPasteboard] clearContents]; - [[NSPasteboard generalPasteboard] writeObjects:objects]; - self.isEmpty = NO; + [self copyObjectsWithoutTimeout:objects]; if(self.clearTimeout != 0) { [[NSNotificationCenter defaultCenter] postNotificationName:MPPasteBoardControllerDidCopyObjects object:self]; [self performSelector:@selector(_clearPasteboardContents) withObject:nil afterDelay:self.clearTimeout]; } } +- (void)copyObjectsWithoutTimeout:(NSArray *)objects +{ + [[NSPasteboard generalPasteboard] clearContents]; + [[NSPasteboard generalPasteboard] writeObjects:objects]; + self.isEmpty = NO; +} + - (void)_clearPasteboardContents { /* Only clear stuff we might have put there */ if(!self.isEmpty) {