├── .gitignore ├── Blockpass.xcodeproj ├── project.pbxproj └── project.xcworkspace │ └── contents.xcworkspacedata ├── Blockpass ├── AppDelegate.swift ├── Base.lproj │ └── MainMenu.xib ├── Blockpass.entitlements ├── CGEvent+RSUtilities.swift ├── Images.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Info.plist ├── PasswordPromptPanel.xib ├── RSKeychain.swift ├── RSStringMatchingKeyboardTap.swift └── en.lproj │ └── MainMenu.strings ├── BlockpassTests ├── CGEvent+RSUtilities-Tests.swift └── Info.plist ├── LICENSE └── README.markdown /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | xcuserdata 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | # CocoaPods 21 | # 22 | # We recommend against adding the Pods directory to your .gitignore. However 23 | # you should judge for yourself, the pros and cons are mentioned at: 24 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 25 | # 26 | # Pods/ 27 | -------------------------------------------------------------------------------- /Blockpass.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 65216F1B2139AA5200E39EA5 /* CGEvent+RSUtilities-Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65216F1A2139AA5200E39EA5 /* CGEvent+RSUtilities-Tests.swift */; }; 11 | 65216F1C2139AB6A00E39EA5 /* CGEvent+RSUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65D8FBBE213995DC001E9F0F /* CGEvent+RSUtilities.swift */; }; 12 | 6549C3AC1A1F980E00C62013 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6549C3AB1A1F980E00C62013 /* AppDelegate.swift */; }; 13 | 6549C3AE1A1F980E00C62013 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6549C3AD1A1F980E00C62013 /* Images.xcassets */; }; 14 | 6549C3CB1A1F9BC700C62013 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6549C3CA1A1F9BC700C62013 /* CoreGraphics.framework */; }; 15 | 65AE38281A39F19600E35656 /* PasswordPromptPanel.xib in Resources */ = {isa = PBXBuildFile; fileRef = 65AE38271A39F19600E35656 /* PasswordPromptPanel.xib */; }; 16 | 65B9C0091A1FC0C20033DA8E /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 6549C3AF1A1F980E00C62013 /* MainMenu.xib */; }; 17 | 65D8FBBF213995DC001E9F0F /* CGEvent+RSUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65D8FBBE213995DC001E9F0F /* CGEvent+RSUtilities.swift */; }; 18 | 65E23B7F2139BF9800BA2FA0 /* RSStringMatchingKeyboardTap.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65D8FBBC2139888B001E9F0F /* RSStringMatchingKeyboardTap.swift */; }; 19 | 65E23B812139C34B00BA2FA0 /* RSKeychain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 65E23B802139C34B00BA2FA0 /* RSKeychain.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 65216F152139AA2600E39EA5 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 6549C39E1A1F980E00C62013 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 6549C3A51A1F980E00C62013; 28 | remoteInfo = Blockpass; 29 | }; 30 | /* End PBXContainerItemProxy section */ 31 | 32 | /* Begin PBXCopyFilesBuildPhase section */ 33 | 65B9C0021A1FBCC70033DA8E /* Copy Bundled Frameworks */ = { 34 | isa = PBXCopyFilesBuildPhase; 35 | buildActionMask = 2147483647; 36 | dstPath = ""; 37 | dstSubfolderSpec = 10; 38 | files = ( 39 | ); 40 | name = "Copy Bundled Frameworks"; 41 | runOnlyForDeploymentPostprocessing = 0; 42 | }; 43 | /* End PBXCopyFilesBuildPhase section */ 44 | 45 | /* Begin PBXFileReference section */ 46 | 65216F102139AA2600E39EA5 /* BlockpassTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = BlockpassTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 47 | 65216F142139AA2600E39EA5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 48 | 65216F1A2139AA5200E39EA5 /* CGEvent+RSUtilities-Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CGEvent+RSUtilities-Tests.swift"; sourceTree = ""; }; 49 | 6549C3A61A1F980E00C62013 /* Blockpass.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Blockpass.app; sourceTree = BUILT_PRODUCTS_DIR; }; 50 | 6549C3AA1A1F980E00C62013 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 51 | 6549C3AB1A1F980E00C62013 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 52 | 6549C3AD1A1F980E00C62013 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 53 | 6549C3B01A1F980E00C62013 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; 54 | 6549C3CA1A1F9BC700C62013 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = System/Library/Frameworks/CoreGraphics.framework; sourceTree = SDKROOT; }; 55 | 65AE380B1A39E9C800E35656 /* README.markdown */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.markdown; sourceTree = ""; }; 56 | 65AE38271A39F19600E35656 /* PasswordPromptPanel.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = PasswordPromptPanel.xib; sourceTree = ""; }; 57 | 65B9C0071A1FC0060033DA8E /* Blockpass.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = Blockpass.entitlements; sourceTree = ""; }; 58 | 65D8FBBC2139888B001E9F0F /* RSStringMatchingKeyboardTap.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSStringMatchingKeyboardTap.swift; sourceTree = ""; }; 59 | 65D8FBBE213995DC001E9F0F /* CGEvent+RSUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGEvent+RSUtilities.swift"; sourceTree = ""; }; 60 | 65E23B802139C34B00BA2FA0 /* RSKeychain.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RSKeychain.swift; sourceTree = ""; }; 61 | /* End PBXFileReference section */ 62 | 63 | /* Begin PBXFrameworksBuildPhase section */ 64 | 65216F0D2139AA2600E39EA5 /* Frameworks */ = { 65 | isa = PBXFrameworksBuildPhase; 66 | buildActionMask = 2147483647; 67 | files = ( 68 | ); 69 | runOnlyForDeploymentPostprocessing = 0; 70 | }; 71 | 6549C3A31A1F980E00C62013 /* Frameworks */ = { 72 | isa = PBXFrameworksBuildPhase; 73 | buildActionMask = 2147483647; 74 | files = ( 75 | 6549C3CB1A1F9BC700C62013 /* CoreGraphics.framework in Frameworks */, 76 | ); 77 | runOnlyForDeploymentPostprocessing = 0; 78 | }; 79 | /* End PBXFrameworksBuildPhase section */ 80 | 81 | /* Begin PBXGroup section */ 82 | 65216F112139AA2600E39EA5 /* BlockpassTests */ = { 83 | isa = PBXGroup; 84 | children = ( 85 | 65216F1A2139AA5200E39EA5 /* CGEvent+RSUtilities-Tests.swift */, 86 | 65216F142139AA2600E39EA5 /* Info.plist */, 87 | ); 88 | path = BlockpassTests; 89 | sourceTree = ""; 90 | }; 91 | 6549C39D1A1F980E00C62013 = { 92 | isa = PBXGroup; 93 | children = ( 94 | 65AE380B1A39E9C800E35656 /* README.markdown */, 95 | 6549C3A81A1F980E00C62013 /* Blockpass */, 96 | 65216F112139AA2600E39EA5 /* BlockpassTests */, 97 | 6549C3A71A1F980E00C62013 /* Products */, 98 | 6549C3CA1A1F9BC700C62013 /* CoreGraphics.framework */, 99 | ); 100 | sourceTree = ""; 101 | }; 102 | 6549C3A71A1F980E00C62013 /* Products */ = { 103 | isa = PBXGroup; 104 | children = ( 105 | 6549C3A61A1F980E00C62013 /* Blockpass.app */, 106 | 65216F102139AA2600E39EA5 /* BlockpassTests.xctest */, 107 | ); 108 | name = Products; 109 | sourceTree = ""; 110 | }; 111 | 6549C3A81A1F980E00C62013 /* Blockpass */ = { 112 | isa = PBXGroup; 113 | children = ( 114 | 65B9C0071A1FC0060033DA8E /* Blockpass.entitlements */, 115 | 6549C3AB1A1F980E00C62013 /* AppDelegate.swift */, 116 | 65D8FBBC2139888B001E9F0F /* RSStringMatchingKeyboardTap.swift */, 117 | 65D8FBBE213995DC001E9F0F /* CGEvent+RSUtilities.swift */, 118 | 65E23B802139C34B00BA2FA0 /* RSKeychain.swift */, 119 | 65AE38271A39F19600E35656 /* PasswordPromptPanel.xib */, 120 | 6549C3AF1A1F980E00C62013 /* MainMenu.xib */, 121 | 6549C3AD1A1F980E00C62013 /* Images.xcassets */, 122 | 6549C3A91A1F980E00C62013 /* Supporting Files */, 123 | ); 124 | path = Blockpass; 125 | sourceTree = ""; 126 | }; 127 | 6549C3A91A1F980E00C62013 /* Supporting Files */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 6549C3AA1A1F980E00C62013 /* Info.plist */, 131 | ); 132 | name = "Supporting Files"; 133 | sourceTree = ""; 134 | }; 135 | /* End PBXGroup section */ 136 | 137 | /* Begin PBXNativeTarget section */ 138 | 65216F0F2139AA2600E39EA5 /* BlockpassTests */ = { 139 | isa = PBXNativeTarget; 140 | buildConfigurationList = 65216F172139AA2600E39EA5 /* Build configuration list for PBXNativeTarget "BlockpassTests" */; 141 | buildPhases = ( 142 | 65216F0C2139AA2600E39EA5 /* Sources */, 143 | 65216F0D2139AA2600E39EA5 /* Frameworks */, 144 | 65216F0E2139AA2600E39EA5 /* Resources */, 145 | ); 146 | buildRules = ( 147 | ); 148 | dependencies = ( 149 | 65216F162139AA2600E39EA5 /* PBXTargetDependency */, 150 | ); 151 | name = BlockpassTests; 152 | productName = BlockpassTests; 153 | productReference = 65216F102139AA2600E39EA5 /* BlockpassTests.xctest */; 154 | productType = "com.apple.product-type.bundle.unit-test"; 155 | }; 156 | 6549C3A51A1F980E00C62013 /* Blockpass */ = { 157 | isa = PBXNativeTarget; 158 | buildConfigurationList = 6549C3C01A1F980E00C62013 /* Build configuration list for PBXNativeTarget "Blockpass" */; 159 | buildPhases = ( 160 | 6549C3A21A1F980E00C62013 /* Sources */, 161 | 6549C3A31A1F980E00C62013 /* Frameworks */, 162 | 6549C3A41A1F980E00C62013 /* Resources */, 163 | 65B9C0021A1FBCC70033DA8E /* Copy Bundled Frameworks */, 164 | ); 165 | buildRules = ( 166 | ); 167 | dependencies = ( 168 | ); 169 | name = Blockpass; 170 | productName = Blockpass; 171 | productReference = 6549C3A61A1F980E00C62013 /* Blockpass.app */; 172 | productType = "com.apple.product-type.application"; 173 | }; 174 | /* End PBXNativeTarget section */ 175 | 176 | /* Begin PBXProject section */ 177 | 6549C39E1A1F980E00C62013 /* Project object */ = { 178 | isa = PBXProject; 179 | attributes = { 180 | LastSwiftUpdateCheck = 1000; 181 | LastUpgradeCheck = 1000; 182 | ORGANIZATIONNAME = "Daniel Jalkut"; 183 | TargetAttributes = { 184 | 65216F0F2139AA2600E39EA5 = { 185 | CreatedOnToolsVersion = 10.0; 186 | DevelopmentTeam = J79Y4KWZQ2; 187 | ProvisioningStyle = Automatic; 188 | TestTargetID = 6549C3A51A1F980E00C62013; 189 | }; 190 | 6549C3A51A1F980E00C62013 = { 191 | CreatedOnToolsVersion = 6.2; 192 | DevelopmentTeam = 493CVA9A35; 193 | SystemCapabilities = { 194 | com.apple.Sandbox = { 195 | enabled = 1; 196 | }; 197 | }; 198 | }; 199 | }; 200 | }; 201 | buildConfigurationList = 6549C3A11A1F980E00C62013 /* Build configuration list for PBXProject "Blockpass" */; 202 | compatibilityVersion = "Xcode 3.2"; 203 | developmentRegion = English; 204 | hasScannedForEncodings = 0; 205 | knownRegions = ( 206 | en, 207 | Base, 208 | ); 209 | mainGroup = 6549C39D1A1F980E00C62013; 210 | productRefGroup = 6549C3A71A1F980E00C62013 /* Products */; 211 | projectDirPath = ""; 212 | projectRoot = ""; 213 | targets = ( 214 | 6549C3A51A1F980E00C62013 /* Blockpass */, 215 | 65216F0F2139AA2600E39EA5 /* BlockpassTests */, 216 | ); 217 | }; 218 | /* End PBXProject section */ 219 | 220 | /* Begin PBXResourcesBuildPhase section */ 221 | 65216F0E2139AA2600E39EA5 /* Resources */ = { 222 | isa = PBXResourcesBuildPhase; 223 | buildActionMask = 2147483647; 224 | files = ( 225 | ); 226 | runOnlyForDeploymentPostprocessing = 0; 227 | }; 228 | 6549C3A41A1F980E00C62013 /* Resources */ = { 229 | isa = PBXResourcesBuildPhase; 230 | buildActionMask = 2147483647; 231 | files = ( 232 | 6549C3AE1A1F980E00C62013 /* Images.xcassets in Resources */, 233 | 65B9C0091A1FC0C20033DA8E /* MainMenu.xib in Resources */, 234 | 65AE38281A39F19600E35656 /* PasswordPromptPanel.xib in Resources */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXResourcesBuildPhase section */ 239 | 240 | /* Begin PBXSourcesBuildPhase section */ 241 | 65216F0C2139AA2600E39EA5 /* Sources */ = { 242 | isa = PBXSourcesBuildPhase; 243 | buildActionMask = 2147483647; 244 | files = ( 245 | 65216F1C2139AB6A00E39EA5 /* CGEvent+RSUtilities.swift in Sources */, 246 | 65216F1B2139AA5200E39EA5 /* CGEvent+RSUtilities-Tests.swift in Sources */, 247 | ); 248 | runOnlyForDeploymentPostprocessing = 0; 249 | }; 250 | 6549C3A21A1F980E00C62013 /* Sources */ = { 251 | isa = PBXSourcesBuildPhase; 252 | buildActionMask = 2147483647; 253 | files = ( 254 | 65D8FBBF213995DC001E9F0F /* CGEvent+RSUtilities.swift in Sources */, 255 | 65E23B812139C34B00BA2FA0 /* RSKeychain.swift in Sources */, 256 | 6549C3AC1A1F980E00C62013 /* AppDelegate.swift in Sources */, 257 | 65E23B7F2139BF9800BA2FA0 /* RSStringMatchingKeyboardTap.swift in Sources */, 258 | ); 259 | runOnlyForDeploymentPostprocessing = 0; 260 | }; 261 | /* End PBXSourcesBuildPhase section */ 262 | 263 | /* Begin PBXTargetDependency section */ 264 | 65216F162139AA2600E39EA5 /* PBXTargetDependency */ = { 265 | isa = PBXTargetDependency; 266 | target = 6549C3A51A1F980E00C62013 /* Blockpass */; 267 | targetProxy = 65216F152139AA2600E39EA5 /* PBXContainerItemProxy */; 268 | }; 269 | /* End PBXTargetDependency section */ 270 | 271 | /* Begin PBXVariantGroup section */ 272 | 6549C3AF1A1F980E00C62013 /* MainMenu.xib */ = { 273 | isa = PBXVariantGroup; 274 | children = ( 275 | 6549C3B01A1F980E00C62013 /* Base */, 276 | ); 277 | name = MainMenu.xib; 278 | sourceTree = ""; 279 | }; 280 | /* End PBXVariantGroup section */ 281 | 282 | /* Begin XCBuildConfiguration section */ 283 | 65216F182139AA2600E39EA5 /* Debug */ = { 284 | isa = XCBuildConfiguration; 285 | buildSettings = { 286 | BUNDLE_LOADER = "$(TEST_HOST)"; 287 | CLANG_ANALYZER_NONNULL = YES; 288 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 289 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 290 | CLANG_ENABLE_OBJC_WEAK = YES; 291 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 292 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 293 | CODE_SIGN_STYLE = Automatic; 294 | COMBINE_HIDPI_IMAGES = YES; 295 | DEBUG_INFORMATION_FORMAT = dwarf; 296 | DEVELOPMENT_TEAM = J79Y4KWZQ2; 297 | GCC_C_LANGUAGE_STANDARD = gnu11; 298 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 299 | INFOPLIST_FILE = BlockpassTests/Info.plist; 300 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 301 | MACOSX_DEPLOYMENT_TARGET = 10.14; 302 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 303 | MTL_FAST_MATH = YES; 304 | PRODUCT_BUNDLE_IDENTIFIER = "com.red-sweater.bugreports.BlockpassTests"; 305 | PRODUCT_NAME = "$(TARGET_NAME)"; 306 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 307 | SWIFT_VERSION = 4.2; 308 | }; 309 | name = Debug; 310 | }; 311 | 65216F192139AA2600E39EA5 /* Release */ = { 312 | isa = XCBuildConfiguration; 313 | buildSettings = { 314 | BUNDLE_LOADER = "$(TEST_HOST)"; 315 | CLANG_ANALYZER_NONNULL = YES; 316 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 317 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 318 | CLANG_ENABLE_OBJC_WEAK = YES; 319 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 320 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 321 | CODE_SIGN_STYLE = Automatic; 322 | COMBINE_HIDPI_IMAGES = YES; 323 | COPY_PHASE_STRIP = NO; 324 | DEVELOPMENT_TEAM = J79Y4KWZQ2; 325 | GCC_C_LANGUAGE_STANDARD = gnu11; 326 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 327 | INFOPLIST_FILE = BlockpassTests/Info.plist; 328 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 329 | MACOSX_DEPLOYMENT_TARGET = 10.14; 330 | MTL_FAST_MATH = YES; 331 | PRODUCT_BUNDLE_IDENTIFIER = "com.red-sweater.bugreports.BlockpassTests"; 332 | PRODUCT_NAME = "$(TARGET_NAME)"; 333 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 334 | SWIFT_VERSION = 4.2; 335 | }; 336 | name = Release; 337 | }; 338 | 6549C3BE1A1F980E00C62013 /* Debug */ = { 339 | isa = XCBuildConfiguration; 340 | buildSettings = { 341 | ALWAYS_SEARCH_USER_PATHS = NO; 342 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 343 | CLANG_CXX_LIBRARY = "libc++"; 344 | CLANG_ENABLE_MODULES = YES; 345 | CLANG_ENABLE_OBJC_ARC = YES; 346 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 347 | CLANG_WARN_BOOL_CONVERSION = YES; 348 | CLANG_WARN_COMMA = YES; 349 | CLANG_WARN_CONSTANT_CONVERSION = YES; 350 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 351 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 352 | CLANG_WARN_EMPTY_BODY = YES; 353 | CLANG_WARN_ENUM_CONVERSION = YES; 354 | CLANG_WARN_INFINITE_RECURSION = YES; 355 | CLANG_WARN_INT_CONVERSION = YES; 356 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 357 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 358 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 359 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 360 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 361 | CLANG_WARN_STRICT_PROTOTYPES = YES; 362 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 363 | CLANG_WARN_UNREACHABLE_CODE = YES; 364 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 365 | CODE_SIGN_IDENTITY = "Mac Developer"; 366 | COPY_PHASE_STRIP = NO; 367 | ENABLE_STRICT_OBJC_MSGSEND = YES; 368 | ENABLE_TESTABILITY = YES; 369 | GCC_C_LANGUAGE_STANDARD = gnu99; 370 | GCC_DYNAMIC_NO_PIC = NO; 371 | GCC_NO_COMMON_BLOCKS = YES; 372 | GCC_OPTIMIZATION_LEVEL = 0; 373 | GCC_PREPROCESSOR_DEFINITIONS = ( 374 | "DEBUG=1", 375 | "$(inherited)", 376 | ); 377 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 378 | GCC_WARN_64_TO_32_BIT_CONVERSION = NO; 379 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 380 | GCC_WARN_UNDECLARED_SELECTOR = YES; 381 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 382 | GCC_WARN_UNUSED_FUNCTION = YES; 383 | GCC_WARN_UNUSED_VARIABLE = YES; 384 | MACOSX_DEPLOYMENT_TARGET = 10.10; 385 | MTL_ENABLE_DEBUG_INFO = YES; 386 | ONLY_ACTIVE_ARCH = YES; 387 | SDKROOT = macosx; 388 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 389 | }; 390 | name = Debug; 391 | }; 392 | 6549C3BF1A1F980E00C62013 /* Release */ = { 393 | isa = XCBuildConfiguration; 394 | buildSettings = { 395 | ALWAYS_SEARCH_USER_PATHS = NO; 396 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 397 | CLANG_CXX_LIBRARY = "libc++"; 398 | CLANG_ENABLE_MODULES = YES; 399 | CLANG_ENABLE_OBJC_ARC = YES; 400 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 401 | CLANG_WARN_BOOL_CONVERSION = YES; 402 | CLANG_WARN_COMMA = YES; 403 | CLANG_WARN_CONSTANT_CONVERSION = YES; 404 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 405 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 406 | CLANG_WARN_EMPTY_BODY = YES; 407 | CLANG_WARN_ENUM_CONVERSION = YES; 408 | CLANG_WARN_INFINITE_RECURSION = YES; 409 | CLANG_WARN_INT_CONVERSION = YES; 410 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 411 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 412 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 413 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 414 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 415 | CLANG_WARN_STRICT_PROTOTYPES = YES; 416 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 417 | CLANG_WARN_UNREACHABLE_CODE = YES; 418 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 419 | CODE_SIGN_IDENTITY = "Mac Developer"; 420 | COPY_PHASE_STRIP = YES; 421 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 422 | ENABLE_NS_ASSERTIONS = NO; 423 | ENABLE_STRICT_OBJC_MSGSEND = YES; 424 | GCC_C_LANGUAGE_STANDARD = gnu99; 425 | GCC_NO_COMMON_BLOCKS = YES; 426 | GCC_WARN_64_TO_32_BIT_CONVERSION = NO; 427 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 428 | GCC_WARN_UNDECLARED_SELECTOR = YES; 429 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 430 | GCC_WARN_UNUSED_FUNCTION = YES; 431 | GCC_WARN_UNUSED_VARIABLE = YES; 432 | MACOSX_DEPLOYMENT_TARGET = 10.10; 433 | MTL_ENABLE_DEBUG_INFO = NO; 434 | SDKROOT = macosx; 435 | }; 436 | name = Release; 437 | }; 438 | 6549C3C11A1F980E00C62013 /* Debug */ = { 439 | isa = XCBuildConfiguration; 440 | buildSettings = { 441 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 442 | CLANG_ENABLE_MODULES = YES; 443 | CODE_SIGN_ENTITLEMENTS = Blockpass/Blockpass.entitlements; 444 | COMBINE_HIDPI_IMAGES = YES; 445 | INFOPLIST_FILE = Blockpass/Info.plist; 446 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 447 | PRODUCT_BUNDLE_IDENTIFIER = "com.punkitup.$(PRODUCT_NAME:rfc1034identifier)"; 448 | PRODUCT_NAME = "$(TARGET_NAME)"; 449 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 450 | SWIFT_VERSION = 4.2; 451 | }; 452 | name = Debug; 453 | }; 454 | 6549C3C21A1F980E00C62013 /* Release */ = { 455 | isa = XCBuildConfiguration; 456 | buildSettings = { 457 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 458 | CLANG_ENABLE_MODULES = YES; 459 | CODE_SIGN_ENTITLEMENTS = Blockpass/Blockpass.entitlements; 460 | COMBINE_HIDPI_IMAGES = YES; 461 | INFOPLIST_FILE = Blockpass/Info.plist; 462 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks"; 463 | PRODUCT_BUNDLE_IDENTIFIER = "com.punkitup.$(PRODUCT_NAME:rfc1034identifier)"; 464 | PRODUCT_NAME = "$(TARGET_NAME)"; 465 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 466 | SWIFT_VERSION = 4.2; 467 | }; 468 | name = Release; 469 | }; 470 | /* End XCBuildConfiguration section */ 471 | 472 | /* Begin XCConfigurationList section */ 473 | 65216F172139AA2600E39EA5 /* Build configuration list for PBXNativeTarget "BlockpassTests" */ = { 474 | isa = XCConfigurationList; 475 | buildConfigurations = ( 476 | 65216F182139AA2600E39EA5 /* Debug */, 477 | 65216F192139AA2600E39EA5 /* Release */, 478 | ); 479 | defaultConfigurationIsVisible = 0; 480 | defaultConfigurationName = Release; 481 | }; 482 | 6549C3A11A1F980E00C62013 /* Build configuration list for PBXProject "Blockpass" */ = { 483 | isa = XCConfigurationList; 484 | buildConfigurations = ( 485 | 6549C3BE1A1F980E00C62013 /* Debug */, 486 | 6549C3BF1A1F980E00C62013 /* Release */, 487 | ); 488 | defaultConfigurationIsVisible = 0; 489 | defaultConfigurationName = Release; 490 | }; 491 | 6549C3C01A1F980E00C62013 /* Build configuration list for PBXNativeTarget "Blockpass" */ = { 492 | isa = XCConfigurationList; 493 | buildConfigurations = ( 494 | 6549C3C11A1F980E00C62013 /* Debug */, 495 | 6549C3C21A1F980E00C62013 /* Release */, 496 | ); 497 | defaultConfigurationIsVisible = 0; 498 | defaultConfigurationName = Release; 499 | }; 500 | /* End XCConfigurationList section */ 501 | }; 502 | rootObject = 6549C39E1A1F980E00C62013 /* Project object */; 503 | } 504 | -------------------------------------------------------------------------------- /Blockpass.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Blockpass/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Blockpass 4 | // 5 | // Created by Daniel Jalkut on 11/21/14. 6 | // Copyright (c) 2014 Daniel Jalkut. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate, RSStringMatchingKeyboardTapDelegate 13 | { 14 | @IBOutlet weak var passwordPrompt : NSPanel? 15 | @IBOutlet weak var secureStringTextField : NSSecureTextField? 16 | 17 | var myPasswordBlocker : RSStringMatchingKeyboardTap? = nil 18 | 19 | var passwordPromptNibObjects : NSArray? = nil 20 | 21 | let keychainItemName = "Blockpass" 22 | let keychainAccountName = "main password" 23 | 24 | func saveSecretTextToKeychain(_ secretText : String) -> Void 25 | { 26 | RSKeychain.setSecureString(secretText, forItemName:keychainItemName, accountName:keychainAccountName) 27 | } 28 | 29 | func getSecretTextFromKeychain() -> String? 30 | { 31 | return RSKeychain.secureString(forItemName: keychainItemName, accountName:keychainAccountName) 32 | } 33 | 34 | @IBAction func dismissPasswordPrompt(_ sender: AnyObject) 35 | { 36 | let response = NSApplication.ModalResponse(sender.tag) 37 | NSApp.stopModal(withCode: response) 38 | } 39 | 40 | func passwordByPromptingUser() -> String? 41 | { 42 | var userPassword : String? = nil 43 | 44 | if (self.passwordPrompt == nil) 45 | { 46 | let panelNib : NSNib = NSNib(nibNamed:"PasswordPromptPanel", bundle:nil)! 47 | panelNib.instantiate(withOwner: self, topLevelObjects: &passwordPromptNibObjects) 48 | } 49 | 50 | let thePrompt = self.passwordPrompt! 51 | // Present the panel modally and at high layer since we're a faceless background app 52 | thePrompt.center() 53 | 54 | thePrompt.level = .statusBar 55 | 56 | thePrompt.makeKeyAndOrderFront(nil) 57 | 58 | let promptStatus = NSApp.runModal(for: thePrompt) 59 | if (promptStatus == .OK) 60 | { 61 | userPassword = self.secureStringTextField!.stringValue 62 | } 63 | 64 | thePrompt.orderOut(nil) 65 | 66 | // Prompt here and if it's "" then change to nil 67 | return userPassword 68 | } 69 | 70 | func applicationDidFinishLaunching(_ aNotification: Notification) 71 | { 72 | // Make sure we have ability to intercept keys 73 | let promptFlag = kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString 74 | let myDict: CFDictionary = NSDictionary(dictionary: [promptFlag: true]) 75 | AXIsProcessTrustedWithOptions(myDict) 76 | 77 | // Unfortunately the prompting mechanism doesn't seem to work from an LSUIElement app 78 | if (!AXIsProcessTrustedWithOptions(myDict)) 79 | { 80 | NSLog("Don't have accessibility access, so we're prompting...") 81 | let warnAlert = NSAlert() 82 | warnAlert.messageText = "Blockpass relies upon having permission to 'control your computer'. If the permission prompt did not appear automatically, go to System Preferences, Security & Privacy, Privacy, Accessibility, and add Blockpass to the list of allowed apps. Then relaunch Blockpass."; 83 | warnAlert.layout() 84 | let warnPanel = warnAlert.window as! NSPanel 85 | warnPanel.level = .statusBar 86 | warnAlert.runModal(); 87 | NSApp.terminate(nil) 88 | } 89 | else 90 | { 91 | var matchedString : String? = getSecretTextFromKeychain() 92 | 93 | // Override keychain stored value if option key is held down 94 | let keyFlags = NSEvent.modifierFlags 95 | 96 | let overrideKeychain = keyFlags.contains(.option) 97 | 98 | if ((matchedString == nil) || overrideKeychain) 99 | { 100 | let newMatchedString = passwordByPromptingUser() 101 | if (newMatchedString == nil) 102 | { 103 | NSLog("User declined to supply a new password string...") 104 | } 105 | else 106 | { 107 | matchedString = newMatchedString 108 | saveSecretTextToKeychain(matchedString!) 109 | } 110 | } 111 | 112 | // If we still don't have a string to match on, just quit 113 | guard let actualMatchedString = matchedString else { 114 | NSLog("No stored password and no user-supplied password. Quitting…") 115 | NSApp.terminate(nil) 116 | return 117 | } 118 | 119 | myPasswordBlocker = RSStringMatchingKeyboardTap(stringToMatch: actualMatchedString, delegate:self) 120 | } 121 | 122 | // Register our default ignored app identifiers 123 | let defaultIgnoredApps : [String] = ["com.apple.ScreenSharing"] 124 | UserDefaults.standard.register(defaults: ["IgnoredAppIdentifiers": defaultIgnoredApps]) 125 | } 126 | 127 | func applicationWillTerminate(aNotification: NSNotification) 128 | { 129 | // Insert code here to tear down your application 130 | } 131 | 132 | func shouldIgnoreMatches() -> Bool 133 | { 134 | // Work around problems where Terminal password entry (e.g. sudo prompt) and Screen Sharing 135 | // (e.g. legitimate secure entry into Screen Sharing app) cause false alarms because secure 136 | // keyboard entry is not enabled while typing passwords. 137 | 138 | var shouldIgnore : Bool = false 139 | 140 | func ignoredAppIDs() -> [String] 141 | { 142 | return UserDefaults.standard.stringArray(forKey: "IgnoredAppIdentifiers") ?? [] 143 | } 144 | 145 | guard let frontAppID = NSWorkspace.shared.frontmostApplication?.bundleIdentifier else { return false } 146 | 147 | if (ignoredAppIDs().contains(frontAppID)) 148 | { 149 | NSLog("Allowing blocked password typing in whitelisted app: %@.", frontAppID) 150 | shouldIgnore = true 151 | } 152 | 153 | return shouldIgnore 154 | } 155 | 156 | func keyboardTap(_ theTap: RSStringMatchingKeyboardTap, didMatchString theString: String) 157 | { 158 | if (!shouldIgnoreMatches()) 159 | { 160 | // Just put up a dialog to block the remaining keys of the password 161 | NSApp.activate(ignoringOtherApps: true) 162 | let dummyAlert = NSAlert() 163 | dummyAlert.messageText = "Hey dummy, don't type your password where everybody can see it!" 164 | dummyAlert.icon = NSImage(named: NSImage.cautionName) 165 | dummyAlert.runModal() 166 | } 167 | } 168 | } 169 | 170 | -------------------------------------------------------------------------------- /Blockpass/Base.lproj/MainMenu.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | Default 535 | 536 | 537 | 538 | 539 | 540 | 541 | Left to Right 542 | 543 | 544 | 545 | 546 | 547 | 548 | Right to Left 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | Default 560 | 561 | 562 | 563 | 564 | 565 | 566 | Left to Right 567 | 568 | 569 | 570 | 571 | 572 | 573 | Right to Left 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | -------------------------------------------------------------------------------- /Blockpass/Blockpass.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Blockpass/CGEvent+RSUtilities.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGEvent+RSUtilities.swift 3 | // Blockpass 4 | // 5 | // Created by Daniel Jalkut on 8/31/18. 6 | // Copyright © 2018 Daniel Jalkut. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import Carbon 11 | 12 | // Based on http://stackoverflow.com/questions/9458017/convert-cgkeycode-to-character 13 | extension CGEvent { 14 | var eventString: String { 15 | // If control or command is down, don't try to interpret the keystroke as typing 16 | let ignoredModifiers: CGEventFlags = [.maskControl, .maskCommand] 17 | guard self.flags.intersection(ignoredModifiers).isEmpty else { 18 | return "" 19 | } 20 | 21 | guard 22 | let currentKeyboard = TISCopyCurrentKeyboardInputSource()?.takeRetainedValue(), 23 | let layoutDataPtr = TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData) 24 | else { 25 | return "" 26 | } 27 | 28 | // Unicode 4 character code should be the maximum case 29 | let maxCharacterLength = 4 30 | var keysDown: UInt32 = 0 31 | var eventCharacters = [UniChar](repeating: 0, count: maxCharacterLength) 32 | var actualCharacterLength: Int = 0 33 | 34 | let layoutData = Unmanaged.fromOpaque(layoutDataPtr).takeUnretainedValue() as Data 35 | let keyTranslationErr: OSStatus = layoutData.withUnsafeBytes { (unsafeLayoutData: UnsafePointer) -> OSStatus in 36 | let keyCode = self.getIntegerValueField(.keyboardEventKeycode) 37 | 38 | let modifierKeyState = UInt32(((self.flags.rawValue) >> 16) & 0xFF) 39 | return UCKeyTranslate(unsafeLayoutData, UInt16(keyCode), UInt16(kUCKeyActionDisplay), modifierKeyState, UInt32(LMGetKbdType()), OptionBits(kUCKeyTranslateNoDeadKeysBit), &keysDown, maxCharacterLength, &actualCharacterLength, &eventCharacters) 40 | } 41 | 42 | guard keyTranslationErr == noErr else { 43 | return "" 44 | } 45 | 46 | return CFStringCreateWithCharacters(kCFAllocatorDefault, eventCharacters, 1) as String 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Blockpass/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Blockpass/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2014 Daniel Jalkut. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /Blockpass/PasswordPromptPanel.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 74 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /Blockpass/RSKeychain.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RSKeychain.swift 3 | // Blockpass 4 | // 5 | // Created by Daniel Jalkut on 8/31/18. 6 | // Copyright © 2018 Daniel Jalkut. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | struct RSKeychain { 12 | internal static func setSecureBytes(_ bytePointer: UnsafeRawPointer!, length byteLength: UInt, forItemName keychainItemName: String, accountName: String) { 13 | 14 | var defaultKeychain: SecKeychain! = nil 15 | if SecKeychainCopyDefault(&defaultKeychain) == noErr { 16 | var shouldAddNewItem = true 17 | 18 | let keychainItemNameCString = (keychainItemName as NSString).utf8String 19 | let keychainItemNameLength = UInt32(strlen(keychainItemNameCString)) 20 | 21 | let accountNameCString = (accountName as NSString).utf8String 22 | let accountNameLength = UInt32(strlen(accountNameCString)) 23 | 24 | // Update an existing item if we have one 25 | var keychainItemRef: SecKeychainItem? = nil 26 | if (SecKeychainFindGenericPassword(nil, keychainItemNameLength, keychainItemNameCString, accountNameLength, accountNameCString, nil, nil, &keychainItemRef) == noErr), 27 | let keychainItemRef = keychainItemRef { 28 | if SecKeychainItemModifyContent(keychainItemRef, nil, UInt32(byteLength), bytePointer) == noErr { 29 | shouldAddNewItem = false 30 | } 31 | } 32 | 33 | if shouldAddNewItem { 34 | SecKeychainAddGenericPassword(defaultKeychain, keychainItemNameLength, keychainItemNameCString, accountNameLength, accountNameCString, UInt32(byteLength), bytePointer, nil) 35 | } 36 | } 37 | } 38 | 39 | public static func secureData(forItemName keychainItemName: String, accountName: String) -> Data? { 40 | var foundData: Data? = nil 41 | 42 | let keychainItemNameCString = (keychainItemName as NSString).utf8String 43 | let keychainItemNameLength = UInt32(strlen(keychainItemNameCString)) 44 | 45 | let accountNameCString = (accountName as NSString).utf8String 46 | let accountNameLength = UInt32(strlen(accountNameCString)) 47 | 48 | var secureDataLength: UInt32 = 0 49 | var secureData: UnsafeMutableRawPointer? = nil 50 | let secureErr = SecKeychainFindGenericPassword(nil, keychainItemNameLength, keychainItemNameCString, accountNameLength, accountNameCString, &secureDataLength, &secureData, nil) 51 | if secureErr == noErr, 52 | let secureData = secureData 53 | { 54 | foundData = Data(bytes: secureData, count: Int(secureDataLength)) 55 | } 56 | else { 57 | if secureErr != errSecItemNotFound { 58 | NSLog("Got error %d trying to access password in keychain for %@/%@", secureErr, keychainItemName, accountName); 59 | } 60 | } 61 | return foundData 62 | } 63 | 64 | public static func setSecureString(_ newString: String, forItemName keychainItemName: String, accountName: String) { 65 | let secureString = (newString as NSString).utf8String 66 | self.setSecureBytes(secureString, length: UInt(strlen(secureString)), forItemName: keychainItemName, accountName: accountName) 67 | } 68 | 69 | public static func secureString(forItemName keychainItemName: String, accountName: String) -> String? { 70 | guard 71 | let secureData = self.secureData(forItemName: keychainItemName, accountName: accountName), 72 | let secureString = String(data: secureData, encoding: .utf8) 73 | else { 74 | return nil 75 | } 76 | 77 | return secureString 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /Blockpass/RSStringMatchingKeyboardTap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RSStringMatchingKeyboardTap.swift 3 | // Blockpass 4 | // 5 | // Created by Daniel Jalkut on 8/31/18. 6 | // Copyright © 2018 Daniel Jalkut. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @objc protocol RSStringMatchingKeyboardTapDelegate { 12 | func keyboardTap(_ theTap: RSStringMatchingKeyboardTap, didMatchString matchingString: String) -> Void 13 | } 14 | 15 | class RSStringMatchingKeyboardTap: NSObject { 16 | 17 | weak var delegate: RSStringMatchingKeyboardTapDelegate? 18 | 19 | var eventTap: CFMachPort! = nil 20 | var stringToMatch: String 21 | var matchedSubstring: String 22 | 23 | init?(stringToMatch: String, delegate: RSStringMatchingKeyboardTapDelegate) { 24 | self.delegate = delegate 25 | self.stringToMatch = stringToMatch 26 | self.matchedSubstring = "" 27 | 28 | // Have to super.init before we can reference self for our callback 29 | super.init() 30 | 31 | let eventMask = CGEventMask(1 << CGEventType.keyDown.rawValue) 32 | let unsafeSelf: UnsafeMutableRawPointer? = Unmanaged.passUnretained(self).toOpaque() 33 | 34 | // We need to use this trampoline to cast the unsafe userInfo back to self, so we 35 | // can invoke the actual instance method. 36 | let myCallbackTrampoline: CGEventTapCallBack = {(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent, userInfo: UnsafeMutableRawPointer?) in 37 | guard let userInfo = userInfo else { 38 | return Unmanaged.passRetained(event) 39 | } 40 | let unsafeSelf = Unmanaged.fromOpaque(userInfo).takeUnretainedValue() 41 | return unsafeSelf.myKeyEventCallback(proxy: proxy, type: type, event: event) 42 | } 43 | 44 | guard let eventTap = CGEvent.tapCreate(tap: .cgSessionEventTap, place: .tailAppendEventTap, options: .listenOnly, eventsOfInterest: eventMask, callback: myCallbackTrampoline, userInfo: unsafeSelf) else { 45 | NSLog("Failed to initialize. Guess we're done here!") 46 | return nil 47 | } 48 | 49 | // Save it 50 | self.eventTap = eventTap 51 | 52 | // Schedule it 53 | let runLoopSource = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, eventTap, 0) 54 | CFRunLoopAddSource(CFRunLoopGetCurrent(), runLoopSource, .commonModes) 55 | } 56 | 57 | func myKeyEventCallback(proxy: CGEventTapProxy, type: CGEventType, event: CGEvent) -> Unmanaged? { 58 | // Get the newly typed key character 59 | let newKeyString = event.eventString 60 | 61 | // Test for accumulated match with blocked substring 62 | let blockedString = self.stringToMatch 63 | print("Got typed string \(newKeyString)") 64 | var runningSubstringMatch = self.matchedSubstring + newKeyString 65 | 66 | // Accumulate while possibly matching 67 | if blockedString.hasPrefix(runningSubstringMatch) { 68 | // Complete match? Burn everything! 69 | if blockedString == runningSubstringMatch { 70 | // Immediately reset the running substring match because the delegate might 71 | // actually put up a modal dialog that e.g. then fires us and we re-enter, 72 | // generating a false match. 73 | self.matchedSubstring = "" 74 | 75 | self.delegate?.keyboardTap(self, didMatchString: runningSubstringMatch) 76 | 77 | runningSubstringMatch = "" 78 | } 79 | } 80 | else { 81 | // No match, start over 82 | runningSubstringMatch = "" 83 | } 84 | 85 | self.matchedSubstring = runningSubstringMatch 86 | 87 | return Unmanaged.passRetained(event) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Blockpass/en.lproj/MainMenu.strings: -------------------------------------------------------------------------------- 1 | 2 | /* Class = "NSMenuItem"; title = "Customize Toolbar…"; ObjectID = "1UK-8n-QPP"; */ 3 | "1UK-8n-QPP.title" = "Customize Toolbar…"; 4 | 5 | /* Class = "NSMenuItem"; title = "Blockpass"; ObjectID = "1Xt-HY-uBw"; */ 6 | "1Xt-HY-uBw.title" = "Blockpass"; 7 | 8 | /* Class = "NSMenu"; title = "Find"; ObjectID = "1b7-l0-nxx"; */ 9 | "1b7-l0-nxx.title" = "Find"; 10 | 11 | /* Class = "NSMenuItem"; title = "Lower"; ObjectID = "1tx-W0-xDw"; */ 12 | "1tx-W0-xDw.title" = "Lower"; 13 | 14 | /* Class = "NSMenuItem"; title = "Raise"; ObjectID = "2h7-ER-AoG"; */ 15 | "2h7-ER-AoG.title" = "Raise"; 16 | 17 | /* Class = "NSMenuItem"; title = "Transformations"; ObjectID = "2oI-Rn-ZJC"; */ 18 | "2oI-Rn-ZJC.title" = "Transformations"; 19 | 20 | /* Class = "NSMenu"; title = "Spelling"; ObjectID = "3IN-sU-3Bg"; */ 21 | "3IN-sU-3Bg.title" = "Spelling"; 22 | 23 | /* Class = "NSMenuItem"; title = "Use Default"; ObjectID = "3Om-Ey-2VK"; */ 24 | "3Om-Ey-2VK.title" = "Use Default"; 25 | 26 | /* Class = "NSMenu"; title = "Speech"; ObjectID = "3rS-ZA-NoH"; */ 27 | "3rS-ZA-NoH.title" = "Speech"; 28 | 29 | /* Class = "NSMenuItem"; title = "Tighten"; ObjectID = "46P-cB-AYj"; */ 30 | "46P-cB-AYj.title" = "Tighten"; 31 | 32 | /* Class = "NSMenuItem"; title = "Find"; ObjectID = "4EN-yA-p0u"; */ 33 | "4EN-yA-p0u.title" = "Find"; 34 | 35 | /* Class = "NSMenuItem"; title = "Quit Blockpass"; ObjectID = "4sb-4s-VLi"; */ 36 | "4sb-4s-VLi.title" = "Quit Blockpass"; 37 | 38 | /* Class = "NSMenuItem"; title = "Edit"; ObjectID = "5QF-Oa-p0T"; */ 39 | "5QF-Oa-p0T.title" = "Edit"; 40 | 41 | /* Class = "NSMenuItem"; title = "Copy Style"; ObjectID = "5Vv-lz-BsD"; */ 42 | "5Vv-lz-BsD.title" = "Copy Style"; 43 | 44 | /* Class = "NSMenuItem"; title = "About Blockpass"; ObjectID = "5kV-Vb-QxS"; */ 45 | "5kV-Vb-QxS.title" = "About Blockpass"; 46 | 47 | /* Class = "NSMenuItem"; title = "Redo"; ObjectID = "6dh-zS-Vam"; */ 48 | "6dh-zS-Vam.title" = "Redo"; 49 | 50 | /* Class = "NSMenuItem"; title = "Correct Spelling Automatically"; ObjectID = "78Y-hA-62v"; */ 51 | "78Y-hA-62v.title" = "Correct Spelling Automatically"; 52 | 53 | /* Class = "NSMenu"; title = "Writing Direction"; ObjectID = "8mr-sm-Yjd"; */ 54 | "8mr-sm-Yjd.title" = "Writing Direction"; 55 | 56 | /* Class = "NSMenuItem"; title = "Substitutions"; ObjectID = "9ic-FL-obx"; */ 57 | "9ic-FL-obx.title" = "Substitutions"; 58 | 59 | /* Class = "NSMenuItem"; title = "Smart Copy/Paste"; ObjectID = "9yt-4B-nSM"; */ 60 | "9yt-4B-nSM.title" = "Smart Copy/Paste"; 61 | 62 | /* Class = "NSMenu"; title = "Main Menu"; ObjectID = "AYu-sK-qS6"; */ 63 | "AYu-sK-qS6.title" = "Main Menu"; 64 | 65 | /* Class = "NSMenuItem"; title = "Preferences…"; ObjectID = "BOF-NM-1cW"; */ 66 | "BOF-NM-1cW.title" = "Preferences…"; 67 | 68 | /* Class = "NSMenuItem"; title = "\tLeft to Right"; ObjectID = "BgM-ve-c93"; */ 69 | "BgM-ve-c93.title" = "\tLeft to Right"; 70 | 71 | /* Class = "NSMenuItem"; title = "Save As…"; ObjectID = "Bw7-FT-i3A"; */ 72 | "Bw7-FT-i3A.title" = "Save As…"; 73 | 74 | /* Class = "NSMenuItem"; title = "Close"; ObjectID = "DVo-aG-piG"; */ 75 | "DVo-aG-piG.title" = "Close"; 76 | 77 | /* Class = "NSMenuItem"; title = "Spelling and Grammar"; ObjectID = "Dv1-io-Yv7"; */ 78 | "Dv1-io-Yv7.title" = "Spelling and Grammar"; 79 | 80 | /* Class = "NSMenu"; title = "Help"; ObjectID = "F2S-fz-NVQ"; */ 81 | "F2S-fz-NVQ.title" = "Help"; 82 | 83 | /* Class = "NSMenuItem"; title = "Blockpass Help"; ObjectID = "FKE-Sm-Kum"; */ 84 | "FKE-Sm-Kum.title" = "Blockpass Help"; 85 | 86 | /* Class = "NSMenuItem"; title = "Text"; ObjectID = "Fal-I4-PZk"; */ 87 | "Fal-I4-PZk.title" = "Text"; 88 | 89 | /* Class = "NSMenu"; title = "Substitutions"; ObjectID = "FeM-D8-WVr"; */ 90 | "FeM-D8-WVr.title" = "Substitutions"; 91 | 92 | /* Class = "NSMenuItem"; title = "Bold"; ObjectID = "GB9-OM-e27"; */ 93 | "GB9-OM-e27.title" = "Bold"; 94 | 95 | /* Class = "NSMenu"; title = "Format"; ObjectID = "GEO-Iw-cKr"; */ 96 | "GEO-Iw-cKr.title" = "Format"; 97 | 98 | /* Class = "NSMenuItem"; title = "Use Default"; ObjectID = "GUa-eO-cwY"; */ 99 | "GUa-eO-cwY.title" = "Use Default"; 100 | 101 | /* Class = "NSMenuItem"; title = "Font"; ObjectID = "Gi5-1S-RQB"; */ 102 | "Gi5-1S-RQB.title" = "Font"; 103 | 104 | /* Class = "NSMenuItem"; title = "Writing Direction"; ObjectID = "H1b-Si-o9J"; */ 105 | "H1b-Si-o9J.title" = "Writing Direction"; 106 | 107 | /* Class = "NSMenuItem"; title = "View"; ObjectID = "H8h-7b-M4v"; */ 108 | "H8h-7b-M4v.title" = "View"; 109 | 110 | /* Class = "NSMenuItem"; title = "Text Replacement"; ObjectID = "HFQ-gK-NFA"; */ 111 | "HFQ-gK-NFA.title" = "Text Replacement"; 112 | 113 | /* Class = "NSMenuItem"; title = "Show Spelling and Grammar"; ObjectID = "HFo-cy-zxI"; */ 114 | "HFo-cy-zxI.title" = "Show Spelling and Grammar"; 115 | 116 | /* Class = "NSMenu"; title = "View"; ObjectID = "HyV-fh-RgO"; */ 117 | "HyV-fh-RgO.title" = "View"; 118 | 119 | /* Class = "NSMenuItem"; title = "Subscript"; ObjectID = "I0S-gh-46l"; */ 120 | "I0S-gh-46l.title" = "Subscript"; 121 | 122 | /* Class = "NSMenuItem"; title = "Open…"; ObjectID = "IAo-SY-fd9"; */ 123 | "IAo-SY-fd9.title" = "Open…"; 124 | 125 | /* Class = "NSMenuItem"; title = "Justify"; ObjectID = "J5U-5w-g23"; */ 126 | "J5U-5w-g23.title" = "Justify"; 127 | 128 | /* Class = "NSMenuItem"; title = "Use None"; ObjectID = "J7y-lM-qPV"; */ 129 | "J7y-lM-qPV.title" = "Use None"; 130 | 131 | /* Class = "NSMenuItem"; title = "Revert to Saved"; ObjectID = "KaW-ft-85H"; */ 132 | "KaW-ft-85H.title" = "Revert to Saved"; 133 | 134 | /* Class = "NSMenuItem"; title = "Show All"; ObjectID = "Kd2-mp-pUS"; */ 135 | "Kd2-mp-pUS.title" = "Show All"; 136 | 137 | /* Class = "NSMenuItem"; title = "Bring All to Front"; ObjectID = "LE2-aR-0XJ"; */ 138 | "LE2-aR-0XJ.title" = "Bring All to Front"; 139 | 140 | /* Class = "NSMenuItem"; title = "Paste Ruler"; ObjectID = "LVM-kO-fVI"; */ 141 | "LVM-kO-fVI.title" = "Paste Ruler"; 142 | 143 | /* Class = "NSMenuItem"; title = "\tLeft to Right"; ObjectID = "Lbh-J2-qVU"; */ 144 | "Lbh-J2-qVU.title" = "\tLeft to Right"; 145 | 146 | /* Class = "NSMenuItem"; title = "Copy Ruler"; ObjectID = "MkV-Pr-PK5"; */ 147 | "MkV-Pr-PK5.title" = "Copy Ruler"; 148 | 149 | /* Class = "NSMenuItem"; title = "Services"; ObjectID = "NMo-om-nkz"; */ 150 | "NMo-om-nkz.title" = "Services"; 151 | 152 | /* Class = "NSMenuItem"; title = "\tDefault"; ObjectID = "Nop-cj-93Q"; */ 153 | "Nop-cj-93Q.title" = "\tDefault"; 154 | 155 | /* Class = "NSMenuItem"; title = "Minimize"; ObjectID = "OY7-WF-poV"; */ 156 | "OY7-WF-poV.title" = "Minimize"; 157 | 158 | /* Class = "NSMenuItem"; title = "Baseline"; ObjectID = "OaQ-X3-Vso"; */ 159 | "OaQ-X3-Vso.title" = "Baseline"; 160 | 161 | /* Class = "NSMenuItem"; title = "Hide Blockpass"; ObjectID = "Olw-nP-bQN"; */ 162 | "Olw-nP-bQN.title" = "Hide Blockpass"; 163 | 164 | /* Class = "NSMenuItem"; title = "Find Previous"; ObjectID = "OwM-mh-QMV"; */ 165 | "OwM-mh-QMV.title" = "Find Previous"; 166 | 167 | /* Class = "NSMenuItem"; title = "Stop Speaking"; ObjectID = "Oyz-dy-DGm"; */ 168 | "Oyz-dy-DGm.title" = "Stop Speaking"; 169 | 170 | /* Class = "NSMenuItem"; title = "Bigger"; ObjectID = "Ptp-SP-VEL"; */ 171 | "Ptp-SP-VEL.title" = "Bigger"; 172 | 173 | /* Class = "NSMenuItem"; title = "Show Fonts"; ObjectID = "Q5e-8K-NDq"; */ 174 | "Q5e-8K-NDq.title" = "Show Fonts"; 175 | 176 | /* Class = "NSMenuItem"; title = "Zoom"; ObjectID = "R4o-n2-Eq4"; */ 177 | "R4o-n2-Eq4.title" = "Zoom"; 178 | 179 | /* Class = "NSMenuItem"; title = "\tRight to Left"; ObjectID = "RB4-Sm-HuC"; */ 180 | "RB4-Sm-HuC.title" = "\tRight to Left"; 181 | 182 | /* Class = "NSMenuItem"; title = "Superscript"; ObjectID = "Rqc-34-cIF"; */ 183 | "Rqc-34-cIF.title" = "Superscript"; 184 | 185 | /* Class = "NSMenuItem"; title = "Select All"; ObjectID = "Ruw-6m-B2m"; */ 186 | "Ruw-6m-B2m.title" = "Select All"; 187 | 188 | /* Class = "NSMenuItem"; title = "Jump to Selection"; ObjectID = "S0p-oC-mLd"; */ 189 | "S0p-oC-mLd.title" = "Jump to Selection"; 190 | 191 | /* Class = "NSMenu"; title = "Window"; ObjectID = "Td7-aD-5lo"; */ 192 | "Td7-aD-5lo.title" = "Window"; 193 | 194 | /* Class = "NSMenuItem"; title = "Capitalize"; ObjectID = "UEZ-Bs-lqG"; */ 195 | "UEZ-Bs-lqG.title" = "Capitalize"; 196 | 197 | /* Class = "NSMenuItem"; title = "Center"; ObjectID = "VIY-Ag-zcb"; */ 198 | "VIY-Ag-zcb.title" = "Center"; 199 | 200 | /* Class = "NSMenuItem"; title = "Hide Others"; ObjectID = "Vdr-fp-XzO"; */ 201 | "Vdr-fp-XzO.title" = "Hide Others"; 202 | 203 | /* Class = "NSMenuItem"; title = "Italic"; ObjectID = "Vjx-xi-njq"; */ 204 | "Vjx-xi-njq.title" = "Italic"; 205 | 206 | /* Class = "NSMenu"; title = "Edit"; ObjectID = "W48-6f-4Dl"; */ 207 | "W48-6f-4Dl.title" = "Edit"; 208 | 209 | /* Class = "NSMenuItem"; title = "Underline"; ObjectID = "WRG-CD-K1S"; */ 210 | "WRG-CD-K1S.title" = "Underline"; 211 | 212 | /* Class = "NSMenuItem"; title = "New"; ObjectID = "Was-JA-tGl"; */ 213 | "Was-JA-tGl.title" = "New"; 214 | 215 | /* Class = "NSMenuItem"; title = "Paste and Match Style"; ObjectID = "WeT-3V-zwk"; */ 216 | "WeT-3V-zwk.title" = "Paste and Match Style"; 217 | 218 | /* Class = "NSMenuItem"; title = "Find…"; ObjectID = "Xz5-n4-O0W"; */ 219 | "Xz5-n4-O0W.title" = "Find…"; 220 | 221 | /* Class = "NSMenuItem"; title = "Find and Replace…"; ObjectID = "YEy-JH-Tfz"; */ 222 | "YEy-JH-Tfz.title" = "Find and Replace…"; 223 | 224 | /* Class = "NSMenuItem"; title = "\tDefault"; ObjectID = "YGs-j5-SAR"; */ 225 | "YGs-j5-SAR.title" = "\tDefault"; 226 | 227 | /* Class = "NSMenuItem"; title = "Start Speaking"; ObjectID = "Ynk-f8-cLZ"; */ 228 | "Ynk-f8-cLZ.title" = "Start Speaking"; 229 | 230 | /* Class = "NSMenuItem"; title = "Align Left"; ObjectID = "ZM1-6Q-yy1"; */ 231 | "ZM1-6Q-yy1.title" = "Align Left"; 232 | 233 | /* Class = "NSMenuItem"; title = "Paragraph"; ObjectID = "ZvO-Gk-QUH"; */ 234 | "ZvO-Gk-QUH.title" = "Paragraph"; 235 | 236 | /* Class = "NSMenuItem"; title = "Print…"; ObjectID = "aTl-1u-JFS"; */ 237 | "aTl-1u-JFS.title" = "Print…"; 238 | 239 | /* Class = "NSMenuItem"; title = "Window"; ObjectID = "aUF-d1-5bR"; */ 240 | "aUF-d1-5bR.title" = "Window"; 241 | 242 | /* Class = "NSMenu"; title = "Font"; ObjectID = "aXa-aM-Jaq"; */ 243 | "aXa-aM-Jaq.title" = "Font"; 244 | 245 | /* Class = "NSMenuItem"; title = "Use Default"; ObjectID = "agt-UL-0e3"; */ 246 | "agt-UL-0e3.title" = "Use Default"; 247 | 248 | /* Class = "NSMenuItem"; title = "Show Colors"; ObjectID = "bgn-CT-cEk"; */ 249 | "bgn-CT-cEk.title" = "Show Colors"; 250 | 251 | /* Class = "NSMenu"; title = "File"; ObjectID = "bib-Uj-vzu"; */ 252 | "bib-Uj-vzu.title" = "File"; 253 | 254 | /* Class = "NSMenuItem"; title = "Use Selection for Find"; ObjectID = "buJ-ug-pKt"; */ 255 | "buJ-ug-pKt.title" = "Use Selection for Find"; 256 | 257 | /* Class = "NSMenu"; title = "Transformations"; ObjectID = "c8a-y6-VQd"; */ 258 | "c8a-y6-VQd.title" = "Transformations"; 259 | 260 | /* Class = "NSMenuItem"; title = "Use None"; ObjectID = "cDB-IK-hbR"; */ 261 | "cDB-IK-hbR.title" = "Use None"; 262 | 263 | /* Class = "NSMenuItem"; title = "Selection"; ObjectID = "cqv-fj-IhA"; */ 264 | "cqv-fj-IhA.title" = "Selection"; 265 | 266 | /* Class = "NSMenuItem"; title = "Smart Links"; ObjectID = "cwL-P1-jid"; */ 267 | "cwL-P1-jid.title" = "Smart Links"; 268 | 269 | /* Class = "NSMenuItem"; title = "Make Lower Case"; ObjectID = "d9M-CD-aMd"; */ 270 | "d9M-CD-aMd.title" = "Make Lower Case"; 271 | 272 | /* Class = "NSMenu"; title = "Text"; ObjectID = "d9c-me-L2H"; */ 273 | "d9c-me-L2H.title" = "Text"; 274 | 275 | /* Class = "NSMenuItem"; title = "File"; ObjectID = "dMs-cI-mzQ"; */ 276 | "dMs-cI-mzQ.title" = "File"; 277 | 278 | /* Class = "NSMenuItem"; title = "Undo"; ObjectID = "dRJ-4n-Yzg"; */ 279 | "dRJ-4n-Yzg.title" = "Undo"; 280 | 281 | /* Class = "NSMenuItem"; title = "Paste"; ObjectID = "gVA-U4-sdL"; */ 282 | "gVA-U4-sdL.title" = "Paste"; 283 | 284 | /* Class = "NSMenuItem"; title = "Smart Quotes"; ObjectID = "hQb-2v-fYv"; */ 285 | "hQb-2v-fYv.title" = "Smart Quotes"; 286 | 287 | /* Class = "NSMenuItem"; title = "Check Document Now"; ObjectID = "hz2-CU-CR7"; */ 288 | "hz2-CU-CR7.title" = "Check Document Now"; 289 | 290 | /* Class = "NSMenu"; title = "Services"; ObjectID = "hz9-B4-Xy5"; */ 291 | "hz9-B4-Xy5.title" = "Services"; 292 | 293 | /* Class = "NSMenuItem"; title = "Smaller"; ObjectID = "i1d-Er-qST"; */ 294 | "i1d-Er-qST.title" = "Smaller"; 295 | 296 | /* Class = "NSMenu"; title = "Baseline"; ObjectID = "ijk-EB-dga"; */ 297 | "ijk-EB-dga.title" = "Baseline"; 298 | 299 | /* Class = "NSMenuItem"; title = "Kern"; ObjectID = "jBQ-r6-VK2"; */ 300 | "jBQ-r6-VK2.title" = "Kern"; 301 | 302 | /* Class = "NSMenuItem"; title = "\tRight to Left"; ObjectID = "jFq-tB-4Kx"; */ 303 | "jFq-tB-4Kx.title" = "\tRight to Left"; 304 | 305 | /* Class = "NSMenuItem"; title = "Format"; ObjectID = "jxT-CU-nIS"; */ 306 | "jxT-CU-nIS.title" = "Format"; 307 | 308 | /* Class = "NSMenuItem"; title = "Check Grammar With Spelling"; ObjectID = "mK6-2p-4JG"; */ 309 | "mK6-2p-4JG.title" = "Check Grammar With Spelling"; 310 | 311 | /* Class = "NSMenuItem"; title = "Ligatures"; ObjectID = "o6e-r0-MWq"; */ 312 | "o6e-r0-MWq.title" = "Ligatures"; 313 | 314 | /* Class = "NSMenu"; title = "Open Recent"; ObjectID = "oas-Oc-fiZ"; */ 315 | "oas-Oc-fiZ.title" = "Open Recent"; 316 | 317 | /* Class = "NSMenuItem"; title = "Loosen"; ObjectID = "ogc-rX-tC1"; */ 318 | "ogc-rX-tC1.title" = "Loosen"; 319 | 320 | /* Class = "NSMenuItem"; title = "Delete"; ObjectID = "pa3-QI-u2k"; */ 321 | "pa3-QI-u2k.title" = "Delete"; 322 | 323 | /* Class = "NSMenuItem"; title = "Save…"; ObjectID = "pxx-59-PXV"; */ 324 | "pxx-59-PXV.title" = "Save…"; 325 | 326 | /* Class = "NSMenuItem"; title = "Find Next"; ObjectID = "q09-fT-Sye"; */ 327 | "q09-fT-Sye.title" = "Find Next"; 328 | 329 | /* Class = "NSMenuItem"; title = "Page Setup…"; ObjectID = "qIS-W8-SiK"; */ 330 | "qIS-W8-SiK.title" = "Page Setup…"; 331 | 332 | /* Class = "NSMenuItem"; title = "Check Spelling While Typing"; ObjectID = "rbD-Rh-wIN"; */ 333 | "rbD-Rh-wIN.title" = "Check Spelling While Typing"; 334 | 335 | /* Class = "NSMenuItem"; title = "Smart Dashes"; ObjectID = "rgM-f4-ycn"; */ 336 | "rgM-f4-ycn.title" = "Smart Dashes"; 337 | 338 | /* Class = "NSMenuItem"; title = "Show Toolbar"; ObjectID = "snW-S8-Cw5"; */ 339 | "snW-S8-Cw5.title" = "Show Toolbar"; 340 | 341 | /* Class = "NSMenuItem"; title = "Data Detectors"; ObjectID = "tRr-pd-1PS"; */ 342 | "tRr-pd-1PS.title" = "Data Detectors"; 343 | 344 | /* Class = "NSMenuItem"; title = "Open Recent"; ObjectID = "tXI-mr-wws"; */ 345 | "tXI-mr-wws.title" = "Open Recent"; 346 | 347 | /* Class = "NSMenu"; title = "Kern"; ObjectID = "tlD-Oa-oAM"; */ 348 | "tlD-Oa-oAM.title" = "Kern"; 349 | 350 | /* Class = "NSMenu"; title = "Blockpass"; ObjectID = "uQy-DD-JDr"; */ 351 | "uQy-DD-JDr.title" = "Blockpass"; 352 | 353 | /* Class = "NSMenuItem"; title = "Cut"; ObjectID = "uRl-iY-unG"; */ 354 | "uRl-iY-unG.title" = "Cut"; 355 | 356 | /* Class = "NSMenuItem"; title = "Paste Style"; ObjectID = "vKC-jM-MkH"; */ 357 | "vKC-jM-MkH.title" = "Paste Style"; 358 | 359 | /* Class = "NSMenuItem"; title = "Show Ruler"; ObjectID = "vLm-3I-IUL"; */ 360 | "vLm-3I-IUL.title" = "Show Ruler"; 361 | 362 | /* Class = "NSMenuItem"; title = "Clear Menu"; ObjectID = "vNY-rz-j42"; */ 363 | "vNY-rz-j42.title" = "Clear Menu"; 364 | 365 | /* Class = "NSMenuItem"; title = "Make Upper Case"; ObjectID = "vmV-6d-7jI"; */ 366 | "vmV-6d-7jI.title" = "Make Upper Case"; 367 | 368 | /* Class = "NSMenu"; title = "Ligatures"; ObjectID = "w0m-vy-SC9"; */ 369 | "w0m-vy-SC9.title" = "Ligatures"; 370 | 371 | /* Class = "NSMenuItem"; title = "Align Right"; ObjectID = "wb2-vD-lq4"; */ 372 | "wb2-vD-lq4.title" = "Align Right"; 373 | 374 | /* Class = "NSMenuItem"; title = "Help"; ObjectID = "wpr-3q-Mcd"; */ 375 | "wpr-3q-Mcd.title" = "Help"; 376 | 377 | /* Class = "NSMenuItem"; title = "Copy"; ObjectID = "x3v-GG-iWU"; */ 378 | "x3v-GG-iWU.title" = "Copy"; 379 | 380 | /* Class = "NSMenuItem"; title = "Use All"; ObjectID = "xQD-1f-W4t"; */ 381 | "xQD-1f-W4t.title" = "Use All"; 382 | 383 | /* Class = "NSMenuItem"; title = "Speech"; ObjectID = "xrE-MZ-jX0"; */ 384 | "xrE-MZ-jX0.title" = "Speech"; 385 | 386 | /* Class = "NSMenuItem"; title = "Show Substitutions"; ObjectID = "z6F-FW-3nz"; */ 387 | "z6F-FW-3nz.title" = "Show Substitutions"; 388 | -------------------------------------------------------------------------------- /BlockpassTests/CGEvent+RSUtilities-Tests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGEvent+RSUtilities-Tests.swift 3 | // Blockpass 4 | // 5 | // Created by Daniel Jalkut on 8/31/18. 6 | // Copyright © 2018 Daniel Jalkut. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | 11 | enum TestingError: Error { 12 | case genericError 13 | } 14 | 15 | class CGEvent_RSUtilities_Tests: XCTestCase { 16 | 17 | // Only works with ASCII chars 18 | func fakeEventWithKeycode(_ keyCode: CGKeyCode) -> CGEvent { 19 | return CGEvent(keyboardEventSource: nil, virtualKey: keyCode, keyDown: true)! 20 | } 21 | 22 | func testUpperCharacterStringExtraction() { 23 | let keyCode = CGKeyCode(38) // J 24 | let fakeEvent = fakeEventWithKeycode(keyCode) 25 | fakeEvent.flags = [.maskShift] 26 | XCTAssertEqual("J", fakeEvent.eventString) 27 | } 28 | 29 | func testLowerCharacterStringExtraction() { 30 | let keyCode = CGKeyCode(38) // J 31 | let fakeEvent = fakeEventWithKeycode(keyCode) 32 | XCTAssertEqual("j", fakeEvent.eventString) 33 | } 34 | 35 | func testNumericStringExtraction() { 36 | let keyCode = CGKeyCode(20) // 3 37 | let fakeEvent = fakeEventWithKeycode(keyCode) 38 | XCTAssertEqual("3", fakeEvent.eventString) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /BlockpassTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Daniel Jalkut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Blockpass - Hey dummy, don't type your password where everybody can see it! 2 | =========================================================================== 3 | 4 | Overview 5 | -------- 6 | 7 | Blockpass aims to save you from the heart-stopping problem of typing your computer's lock-screen password into a plain text field such as a chat, Twitter, etc., and pressing return before you realize you've just shared your password with a friend, or the world. 8 | 9 | Blockpass is a standard Mac OS X application that runs as a "faceless background application." As such it doesn't show up in the Dock. It also has no user NSStatusItem show it won't show up in the menu bar either. It simply runs quietly in the background, waiting for you to type your password (or whatever string you configure it with), and jolts you with an alert condemning the action. 10 | 11 | **Please note:** by default the tool ignores typing that occurs in either Terminal or Screen Sharing.app, it won't interfere with attempts to type your password in those apps. 12 | 13 | Requirements 14 | ------------ 15 | 16 | Blockpass is written in Swift 4.2, and expects to build for a deployment target of 10.10 or higher. It has not been tested in other environments. 17 | 18 | Blockpass requires that you enable "access for assistive devices" for the app. The app should prompt you to make this change when it first launches, after which you must reopen the app. If it doesn't prompt you automatically, make the change manually in System Preferences -> Security & Privacy -> Privacy -> Accessibility. 19 | 20 | Note that if you run the app directly from Xcode, it is Xcode that will require "access for assistive devices". 21 | 22 | Installation 23 | ------------ 24 | 25 | To install Blockpass you need to first build the application, run the app, and arrange for the app to be run every time you log in to your Mac. You can arrange this in the "Login Items" section of your user settings in the System Preferences "Users & Groups" pane. 26 | 27 | Configuration 28 | ------------- 29 | 30 | The first time you run Blockpass, it will prompt you for a secret string, which will be saved securely in the keychain. The string you enter will dictate which keystrokes cause Blockpass to later interrupt you and prevent your typing the password in plain text. 31 | 32 | If at any time after you first run and configure Blockpass, you wish to change the stored password, you can do so by holding the option key while Blockpass launches. First "killall Blockpass" from the Terminal, then relaunch the app. 33 | 34 | Theoretically you should be able to override the built-in default "ignored apps" configuration by declaring your own array of application bundle identifiers, e.g.: 35 | 36 | ``` 37 | % defaults write com.punkitup.Blockpass IgnoredAppIdentifiers -array com.apple.dt.Xcode com.apple.Safari 38 | % killall Blockpass 39 | % killall cfprefsd 40 | ``` 41 | 42 | However in my recent tests this does not seem to be working to effectively override the built-in list. It may have something to do with my system, sandboxing, or both. 43 | 44 | Security 45 | -------- 46 | 47 | Blockpass stores your configured password string securely in the System Keychain, using the application name "Blockpass" and account name "main password". It is read from the keychain at launch time unless the option key is held down to override the saved value with a new value. 48 | 49 | Un-Installation 50 | --------------- 51 | 52 | Because Blockpass has no user interface, to quit the app you will have to "killall Blockpass" from the Terminal. 53 | 54 | Should you decide you want to stop using Blockpass entirely, simply delete the app from your computer, remove it from your login items, and "killall Blockpass" from the Terminal, or restart your Mac to be sure it is no longer running. 55 | 56 | Caveats 57 | ------- 58 | 59 | This project is not particularly supported. I'm sharing this because of the outpouring of requests to do so after I [blogged about a related issue](http://bitsplitting.org/2014/12/09/insecure-keyboard-entry/). I've tried to make the tool as user-friendly as possible given time constraints, but do not expect the world from me! I hope you enjoy the tool and code. 60 | --------------------------------------------------------------------------------