├── .gitignore ├── 10_A_Modular_Arithmetic_Module_For_The_Affine_Cipher └── cryptomath.py ├── 11_Programming_The_Affine_Cipher ├── affineCipher.py └── paperclip.py ├── 1_The_Reverse_Cipher └── reverse_cipher.py ├── 2_The_Caesar_Cipher ├── caesarCipher.py ├── paperclip.py └── paperclip.pyc ├── 3_Hacking_the_Caesar_Cipher_With_Brute_Force └── Hacking_the_caesar_cipher_with_brute_force.py ├── 4_Encrypting_with_the_transposition_Cipher └── encrypting_with_the_transposition_cipher.py ├── 5_Decrypting_with_the_transposition_cipher └── transpositionDecrypt.py ├── 6_Programming_a_program_to_test_your_program ├── encrypting_with_the_transposition_cipher.py ├── transpositionDecrypt.py └── transpositionTets.py ├── 7_Encrypting_and_Decrypting_Files ├── encrypting_with_the_transposition_cipher.py ├── frankenstein.encrypted.txt ├── frankenstein.txt ├── transpositionDecrypt.py └── transpositionFileCipher.py ├── 8_Detecting_English_Programmatically ├── detectEnglish.py └── dictionary.txt ├── 9_Hacking_The_Transposition_Cipher ├── detectEnglish.py ├── dictionary.txt ├── transpositionDecrypt.py └── transpositionHacker.py ├── README.md └── paperclip.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /10_A_Modular_Arithmetic_Module_For_The_Affine_Cipher/cryptomath.py: -------------------------------------------------------------------------------- 1 | # Cryptomath Module 2 | 3 | def gcd(a, b): 4 | #Return the GCD of a and b using Euclid's algorithm: 5 | while a != 0: 6 | a, b = b % a, a 7 | return b 8 | 9 | def findModInverse(a, m): 10 | # Return the modular inverse of a % m, which is 11 | # the number x such that a*x % m = 1 12 | 13 | if gcd(a, m) != 1 : 14 | return None # No mod inverse if a & m aren't relatively prime. 15 | 16 | # Calculate using the extended Euclidean algorithm 17 | u1, u2, u3 = 1, 0, a 18 | v1, v2, v3 = 0, 1, m 19 | while v3 != 0: 20 | q = u3 // v3 # Note that // is the integer division operator. 21 | v1, v2, v3, u1, u2, u3 = (u1 - q * v1), (u2 - q * v2), 22 | (u3 - q * v3), v1, v2, v3 23 | return u1 % m 24 | 25 | -------------------------------------------------------------------------------- /11_Programming_The_Affine_Cipher/affineCipher.py: -------------------------------------------------------------------------------- 1 | # Affine Cipher 2 | 3 | import sys, pyperclip, cryptomath, random 4 | SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.' 5 | 6 | def main(): 7 | myMessage = """"A computer would deserve to be called intelligent if it 8 | could deceive a human into believing that it was human."-Alan Turing""" 9 | myKey = 2894 10 | myMode = 'encrypt' # Set to either 'encrypt or decrypt' 11 | 12 | 13 | if myMode == 'encrypt': 14 | translated = encryptMessage(myKey, myMessage) 15 | elif myMode == 'decrypt': 16 | translated = decryptMessage(myKey, myMessage) 17 | print('Key: %s' % (myKey)) 18 | print('%sed text:' % (myMode.title())) 19 | print(translated) 20 | pyperclip.copy(translated) 21 | print('Full %sed text copied to clipboard.' % (myMode)) 22 | 23 | def getKeyParts(key): 24 | keyA = key // len(SYMBOLS) 25 | keyB = key % len(SYMBOLS) 26 | return (keyA, keyB) 27 | 28 | def checkKeys(keyA, keyB, mode): 29 | if keyA == 1 and mode == 'encrypt': 30 | sys.exit('Cipher is weak if key A is 1. Choose a different key.') 31 | if keyB == 0 and mode == 'encrypt': 32 | sys.exit('Cipher is weak if key B is 0. Choose a different key.') 33 | if keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) - 1 : 34 | sys.exit('Key A must be greater than 0 and Key B must be between 0 and %s.' %(len(SYMBOLS) - 1)) 35 | if cryptomath.gcd(keyA, len(SYMBOLS)) != 1: 36 | sys.exit('Key A (%s) and the symbol set size (%s) are not relatively prime. Choose a different key.' % (keyA, len(SYMBOLS))) 37 | 38 | def encryptMessage(key, message): 39 | keyA, keyB = getKeyParts(key) 40 | checkKeys(keyA, keyB, 'encrypt') 41 | ciphertext = '' 42 | for symbol in message: 43 | if symbol in SYMBOLS: 44 | # encrypt the symbol: 45 | symbolIndex = SYMBOLS.find(symbol) 46 | ciphertext += SYMBOLS[(symbolIndex * keyA + keyB) % len(SYMBOLS)] 47 | else: 48 | ciphertext += symbol #Append the symbol without encrypting. 49 | return ciphertext 50 | 51 | def decryptMessage(key, message): 52 | keyA, keyB = getKeyParts(key) 53 | checkKeys(keyA, keyB, 'decrypt') 54 | plaintext = '' 55 | modInverseOfKeyA = cryptomath.findModInverse(key, len(SYMBOLS)) 56 | 57 | for symbol in message: 58 | if symbol in SYMBOLS: 59 | # Decrypt the symbol: 60 | symbolIndex = SYMBOLS.find(symbol) 61 | plaintext += SYMBOLS[(symbolIndex - keyB) * modInverseOfKeyA % len(SYMBOLS)] 62 | else: 63 | plaintext += symbol # Append the symbol without decrypting. 64 | return plaintext 65 | 66 | def getRandomKey(): 67 | while True: 68 | keyA = random.randint(2, len(SYMBOLS)) 69 | keyB = random.randint(2, len(SYMBOLS)) 70 | if cryptomath.gcd(keyA, len(SYMBOLS)) == 1: 71 | return keyA * len(SYMBOLS) + keyB 72 | 73 | # If afficeCipher.py is run (instead of imported as a module), call the main() function 74 | if __name__ == '__main__': 75 | main() 76 | -------------------------------------------------------------------------------- /11_Programming_The_Affine_Cipher/paperclip.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pyperclip 3 | 4 | A cross-platform clipboard module for Python, with copy & paste functions for plain text. 5 | By Al Sweigart al@inventwithpython.com 6 | BSD License 7 | 8 | Usage: 9 | import pyperclip 10 | pyperclip.copy('The text to be copied to the clipboard.') 11 | spam = pyperclip.paste() 12 | 13 | if not pyperclip.is_available(): 14 | print("Copy functionality unavailable!") 15 | 16 | On Windows, no additional modules are needed. 17 | On Mac, the pyobjc module is used, falling back to the pbcopy and pbpaste cli 18 | commands. (These commands should come with OS X.). 19 | On Linux, install xclip or xsel via package manager. For example, in Debian: 20 | sudo apt-get install xclip 21 | sudo apt-get install xsel 22 | 23 | Otherwise on Linux, you will need the gtk or PyQt5/PyQt4 modules installed. 24 | 25 | gtk and PyQt4 modules are not available for Python 3, 26 | and this module does not work with PyGObject yet. 27 | 28 | Note: There seems to be a way to get gtk on Python 3, according to: 29 | https://askubuntu.com/questions/697397/python3-is-not-supporting-gtk-module 30 | 31 | Cygwin is currently not supported. 32 | 33 | Security Note: This module runs programs with these names: 34 | - which 35 | - where 36 | - pbcopy 37 | - pbpaste 38 | - xclip 39 | - xsel 40 | - klipper 41 | - qdbus 42 | A malicious user could rename or add programs with these names, tricking 43 | Pyperclip into running them with whatever permissions the Python process has. 44 | 45 | """ 46 | __version__ = '1.7.0' 47 | 48 | import contextlib 49 | import ctypes 50 | import os 51 | import platform 52 | import subprocess 53 | import sys 54 | import time 55 | import warnings 56 | 57 | from ctypes import c_size_t, sizeof, c_wchar_p, get_errno, c_wchar 58 | 59 | 60 | # `import PyQt4` sys.exit()s if DISPLAY is not in the environment. 61 | # Thus, we need to detect the presence of $DISPLAY manually 62 | # and not load PyQt4 if it is absent. 63 | HAS_DISPLAY = os.getenv("DISPLAY", False) 64 | 65 | EXCEPT_MSG = """ 66 | Pyperclip could not find a copy/paste mechanism for your system. 67 | For more information, please visit https://pyperclip.readthedocs.io/en/latest/introduction.html#not-implemented-error """ 68 | 69 | PY2 = sys.version_info[0] == 2 70 | 71 | STR_OR_UNICODE = unicode if PY2 else str # For paste(): Python 3 uses str, Python 2 uses unicode. 72 | 73 | ENCODING = 'utf-8' 74 | 75 | # The "which" unix command finds where a command is. 76 | if platform.system() == 'Windows': 77 | WHICH_CMD = 'where' 78 | else: 79 | WHICH_CMD = 'which' 80 | 81 | def _executable_exists(name): 82 | return subprocess.call([WHICH_CMD, name], 83 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 84 | 85 | 86 | 87 | # Exceptions 88 | class PyperclipException(RuntimeError): 89 | pass 90 | 91 | class PyperclipWindowsException(PyperclipException): 92 | def __init__(self, message): 93 | message += " (%s)" % ctypes.WinError() 94 | super(PyperclipWindowsException, self).__init__(message) 95 | 96 | 97 | def _stringifyText(text): 98 | if PY2: 99 | acceptedTypes = (unicode, str, int, float, bool) 100 | else: 101 | acceptedTypes = (str, int, float, bool) 102 | if not isinstance(text, acceptedTypes): 103 | raise PyperclipException('only str, int, float, and bool values can be copied to the clipboard, not %s' % (text.__class__.__name__)) 104 | return STR_OR_UNICODE(text) 105 | 106 | 107 | def init_osx_pbcopy_clipboard(): 108 | 109 | def copy_osx_pbcopy(text): 110 | text = _stringifyText(text) # Converts non-str values to str. 111 | p = subprocess.Popen(['pbcopy', 'w'], 112 | stdin=subprocess.PIPE, close_fds=True) 113 | p.communicate(input=text.encode(ENCODING)) 114 | 115 | def paste_osx_pbcopy(): 116 | p = subprocess.Popen(['pbpaste', 'r'], 117 | stdout=subprocess.PIPE, close_fds=True) 118 | stdout, stderr = p.communicate() 119 | return stdout.decode(ENCODING) 120 | 121 | return copy_osx_pbcopy, paste_osx_pbcopy 122 | 123 | 124 | def init_osx_pyobjc_clipboard(): 125 | def copy_osx_pyobjc(text): 126 | '''Copy string argument to clipboard''' 127 | text = _stringifyText(text) # Converts non-str values to str. 128 | newStr = Foundation.NSString.stringWithString_(text).nsstring() 129 | newData = newStr.dataUsingEncoding_(Foundation.NSUTF8StringEncoding) 130 | board = AppKit.NSPasteboard.generalPasteboard() 131 | board.declareTypes_owner_([AppKit.NSStringPboardType], None) 132 | board.setData_forType_(newData, AppKit.NSStringPboardType) 133 | 134 | def paste_osx_pyobjc(): 135 | "Returns contents of clipboard" 136 | board = AppKit.NSPasteboard.generalPasteboard() 137 | content = board.stringForType_(AppKit.NSStringPboardType) 138 | return content 139 | 140 | return copy_osx_pyobjc, paste_osx_pyobjc 141 | 142 | 143 | def init_gtk_clipboard(): 144 | global gtk 145 | import gtk 146 | 147 | def copy_gtk(text): 148 | global cb 149 | text = _stringifyText(text) # Converts non-str values to str. 150 | cb = gtk.Clipboard() 151 | cb.set_text(text) 152 | cb.store() 153 | 154 | def paste_gtk(): 155 | clipboardContents = gtk.Clipboard().wait_for_text() 156 | # for python 2, returns None if the clipboard is blank. 157 | if clipboardContents is None: 158 | return '' 159 | else: 160 | return clipboardContents 161 | 162 | return copy_gtk, paste_gtk 163 | 164 | 165 | def init_qt_clipboard(): 166 | global QApplication 167 | # $DISPLAY should exist 168 | 169 | # Try to import from qtpy, but if that fails try PyQt5 then PyQt4 170 | try: 171 | from qtpy.QtWidgets import QApplication 172 | except: 173 | try: 174 | from PyQt5.QtWidgets import QApplication 175 | except: 176 | from PyQt4.QtGui import QApplication 177 | 178 | app = QApplication.instance() 179 | if app is None: 180 | app = QApplication([]) 181 | 182 | def copy_qt(text): 183 | text = _stringifyText(text) # Converts non-str values to str. 184 | cb = app.clipboard() 185 | cb.setText(text) 186 | 187 | def paste_qt(): 188 | cb = app.clipboard() 189 | return STR_OR_UNICODE(cb.text()) 190 | 191 | return copy_qt, paste_qt 192 | 193 | 194 | def init_xclip_clipboard(): 195 | DEFAULT_SELECTION='c' 196 | PRIMARY_SELECTION='p' 197 | 198 | def copy_xclip(text, primary=False): 199 | text = _stringifyText(text) # Converts non-str values to str. 200 | selection=DEFAULT_SELECTION 201 | if primary: 202 | selection=PRIMARY_SELECTION 203 | p = subprocess.Popen(['xclip', '-selection', selection], 204 | stdin=subprocess.PIPE, close_fds=True) 205 | p.communicate(input=text.encode(ENCODING)) 206 | 207 | def paste_xclip(primary=False): 208 | selection=DEFAULT_SELECTION 209 | if primary: 210 | selection=PRIMARY_SELECTION 211 | p = subprocess.Popen(['xclip', '-selection', selection, '-o'], 212 | stdout=subprocess.PIPE, 213 | stderr=subprocess.PIPE, 214 | close_fds=True) 215 | stdout, stderr = p.communicate() 216 | # Intentionally ignore extraneous output on stderr when clipboard is empty 217 | return stdout.decode(ENCODING) 218 | 219 | return copy_xclip, paste_xclip 220 | 221 | 222 | def init_xsel_clipboard(): 223 | DEFAULT_SELECTION='-b' 224 | PRIMARY_SELECTION='-p' 225 | 226 | def copy_xsel(text, primary=False): 227 | text = _stringifyText(text) # Converts non-str values to str. 228 | selection_flag = DEFAULT_SELECTION 229 | if primary: 230 | selection_flag = PRIMARY_SELECTION 231 | p = subprocess.Popen(['xsel', selection_flag, '-i'], 232 | stdin=subprocess.PIPE, close_fds=True) 233 | p.communicate(input=text.encode(ENCODING)) 234 | 235 | def paste_xsel(primary=False): 236 | selection_flag = DEFAULT_SELECTION 237 | if primary: 238 | selection_flag = PRIMARY_SELECTION 239 | p = subprocess.Popen(['xsel', selection_flag, '-o'], 240 | stdout=subprocess.PIPE, close_fds=True) 241 | stdout, stderr = p.communicate() 242 | return stdout.decode(ENCODING) 243 | 244 | return copy_xsel, paste_xsel 245 | 246 | 247 | def init_klipper_clipboard(): 248 | def copy_klipper(text): 249 | text = _stringifyText(text) # Converts non-str values to str. 250 | p = subprocess.Popen( 251 | ['qdbus', 'org.kde.klipper', '/klipper', 'setClipboardContents', 252 | text.encode(ENCODING)], 253 | stdin=subprocess.PIPE, close_fds=True) 254 | p.communicate(input=None) 255 | 256 | def paste_klipper(): 257 | p = subprocess.Popen( 258 | ['qdbus', 'org.kde.klipper', '/klipper', 'getClipboardContents'], 259 | stdout=subprocess.PIPE, close_fds=True) 260 | stdout, stderr = p.communicate() 261 | 262 | # Workaround for https://bugs.kde.org/show_bug.cgi?id=342874 263 | # TODO: https://github.com/asweigart/pyperclip/issues/43 264 | clipboardContents = stdout.decode(ENCODING) 265 | # even if blank, Klipper will append a newline at the end 266 | assert len(clipboardContents) > 0 267 | # make sure that newline is there 268 | assert clipboardContents.endswith('\n') 269 | if clipboardContents.endswith('\n'): 270 | clipboardContents = clipboardContents[:-1] 271 | return clipboardContents 272 | 273 | return copy_klipper, paste_klipper 274 | 275 | 276 | def init_dev_clipboard_clipboard(): 277 | def copy_dev_clipboard(text): 278 | text = _stringifyText(text) # Converts non-str values to str. 279 | if text == '': 280 | warnings.warn('Pyperclip cannot copy a blank string to the clipboard on Cygwin. This is effectively a no-op.') 281 | if '\r' in text: 282 | warnings.warn('Pyperclip cannot handle \\r characters on Cygwin.') 283 | 284 | fo = open('/dev/clipboard', 'wt') 285 | fo.write(text) 286 | fo.close() 287 | 288 | def paste_dev_clipboard(): 289 | fo = open('/dev/clipboard', 'rt') 290 | content = fo.read() 291 | fo.close() 292 | return content 293 | 294 | return copy_dev_clipboard, paste_dev_clipboard 295 | 296 | 297 | def init_no_clipboard(): 298 | class ClipboardUnavailable(object): 299 | 300 | def __call__(self, *args, **kwargs): 301 | raise PyperclipException(EXCEPT_MSG) 302 | 303 | if PY2: 304 | def __nonzero__(self): 305 | return False 306 | else: 307 | def __bool__(self): 308 | return False 309 | 310 | return ClipboardUnavailable(), ClipboardUnavailable() 311 | 312 | 313 | 314 | 315 | # Windows-related clipboard functions: 316 | class CheckedCall(object): 317 | def __init__(self, f): 318 | super(CheckedCall, self).__setattr__("f", f) 319 | 320 | def __call__(self, *args): 321 | ret = self.f(*args) 322 | if not ret and get_errno(): 323 | raise PyperclipWindowsException("Error calling " + self.f.__name__) 324 | return ret 325 | 326 | def __setattr__(self, key, value): 327 | setattr(self.f, key, value) 328 | 329 | 330 | def init_windows_clipboard(): 331 | global HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, HINSTANCE, HMENU, BOOL, UINT, HANDLE 332 | from ctypes.wintypes import (HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, 333 | HINSTANCE, HMENU, BOOL, UINT, HANDLE) 334 | 335 | windll = ctypes.windll 336 | msvcrt = ctypes.CDLL('msvcrt') 337 | 338 | safeCreateWindowExA = CheckedCall(windll.user32.CreateWindowExA) 339 | safeCreateWindowExA.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD, INT, INT, 340 | INT, INT, HWND, HMENU, HINSTANCE, LPVOID] 341 | safeCreateWindowExA.restype = HWND 342 | 343 | safeDestroyWindow = CheckedCall(windll.user32.DestroyWindow) 344 | safeDestroyWindow.argtypes = [HWND] 345 | safeDestroyWindow.restype = BOOL 346 | 347 | OpenClipboard = windll.user32.OpenClipboard 348 | OpenClipboard.argtypes = [HWND] 349 | OpenClipboard.restype = BOOL 350 | 351 | safeCloseClipboard = CheckedCall(windll.user32.CloseClipboard) 352 | safeCloseClipboard.argtypes = [] 353 | safeCloseClipboard.restype = BOOL 354 | 355 | safeEmptyClipboard = CheckedCall(windll.user32.EmptyClipboard) 356 | safeEmptyClipboard.argtypes = [] 357 | safeEmptyClipboard.restype = BOOL 358 | 359 | safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData) 360 | safeGetClipboardData.argtypes = [UINT] 361 | safeGetClipboardData.restype = HANDLE 362 | 363 | safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData) 364 | safeSetClipboardData.argtypes = [UINT, HANDLE] 365 | safeSetClipboardData.restype = HANDLE 366 | 367 | safeGlobalAlloc = CheckedCall(windll.kernel32.GlobalAlloc) 368 | safeGlobalAlloc.argtypes = [UINT, c_size_t] 369 | safeGlobalAlloc.restype = HGLOBAL 370 | 371 | safeGlobalLock = CheckedCall(windll.kernel32.GlobalLock) 372 | safeGlobalLock.argtypes = [HGLOBAL] 373 | safeGlobalLock.restype = LPVOID 374 | 375 | safeGlobalUnlock = CheckedCall(windll.kernel32.GlobalUnlock) 376 | safeGlobalUnlock.argtypes = [HGLOBAL] 377 | safeGlobalUnlock.restype = BOOL 378 | 379 | wcslen = CheckedCall(msvcrt.wcslen) 380 | wcslen.argtypes = [c_wchar_p] 381 | wcslen.restype = UINT 382 | 383 | GMEM_MOVEABLE = 0x0002 384 | CF_UNICODETEXT = 13 385 | 386 | @contextlib.contextmanager 387 | def window(): 388 | """ 389 | Context that provides a valid Windows hwnd. 390 | """ 391 | # we really just need the hwnd, so setting "STATIC" 392 | # as predefined lpClass is just fine. 393 | hwnd = safeCreateWindowExA(0, b"STATIC", None, 0, 0, 0, 0, 0, 394 | None, None, None, None) 395 | try: 396 | yield hwnd 397 | finally: 398 | safeDestroyWindow(hwnd) 399 | 400 | @contextlib.contextmanager 401 | def clipboard(hwnd): 402 | """ 403 | Context manager that opens the clipboard and prevents 404 | other applications from modifying the clipboard content. 405 | """ 406 | # We may not get the clipboard handle immediately because 407 | # some other application is accessing it (?) 408 | # We try for at least 500ms to get the clipboard. 409 | t = time.time() + 0.5 410 | success = False 411 | while time.time() < t: 412 | success = OpenClipboard(hwnd) 413 | if success: 414 | break 415 | time.sleep(0.01) 416 | if not success: 417 | raise PyperclipWindowsException("Error calling OpenClipboard") 418 | 419 | try: 420 | yield 421 | finally: 422 | safeCloseClipboard() 423 | 424 | def copy_windows(text): 425 | # This function is heavily based on 426 | # http://msdn.com/ms649016#_win32_Copying_Information_to_the_Clipboard 427 | 428 | text = _stringifyText(text) # Converts non-str values to str. 429 | 430 | with window() as hwnd: 431 | # http://msdn.com/ms649048 432 | # If an application calls OpenClipboard with hwnd set to NULL, 433 | # EmptyClipboard sets the clipboard owner to NULL; 434 | # this causes SetClipboardData to fail. 435 | # => We need a valid hwnd to copy something. 436 | with clipboard(hwnd): 437 | safeEmptyClipboard() 438 | 439 | if text: 440 | # http://msdn.com/ms649051 441 | # If the hMem parameter identifies a memory object, 442 | # the object must have been allocated using the 443 | # function with the GMEM_MOVEABLE flag. 444 | count = wcslen(text) + 1 445 | handle = safeGlobalAlloc(GMEM_MOVEABLE, 446 | count * sizeof(c_wchar)) 447 | locked_handle = safeGlobalLock(handle) 448 | 449 | ctypes.memmove(c_wchar_p(locked_handle), c_wchar_p(text), count * sizeof(c_wchar)) 450 | 451 | safeGlobalUnlock(handle) 452 | safeSetClipboardData(CF_UNICODETEXT, handle) 453 | 454 | def paste_windows(): 455 | with clipboard(None): 456 | handle = safeGetClipboardData(CF_UNICODETEXT) 457 | if not handle: 458 | # GetClipboardData may return NULL with errno == NO_ERROR 459 | # if the clipboard is empty. 460 | # (Also, it may return a handle to an empty buffer, 461 | # but technically that's not empty) 462 | return "" 463 | return c_wchar_p(handle).value 464 | 465 | return copy_windows, paste_windows 466 | 467 | 468 | def init_wsl_clipboard(): 469 | def copy_wsl(text): 470 | text = _stringifyText(text) # Converts non-str values to str. 471 | p = subprocess.Popen(['clip.exe'], 472 | stdin=subprocess.PIPE, close_fds=True) 473 | p.communicate(input=text.encode(ENCODING)) 474 | 475 | def paste_wsl(): 476 | p = subprocess.Popen(['powershell.exe', '-command', 'Get-Clipboard'], 477 | stdout=subprocess.PIPE, 478 | stderr=subprocess.PIPE, 479 | close_fds=True) 480 | stdout, stderr = p.communicate() 481 | # WSL appends "\r\n" to the contents. 482 | return stdout[:-2].decode(ENCODING) 483 | 484 | return copy_wsl, paste_wsl 485 | 486 | 487 | # Automatic detection of clipboard mechanisms and importing is done in deteremine_clipboard(): 488 | def determine_clipboard(): 489 | ''' 490 | Determine the OS/platform and set the copy() and paste() functions 491 | accordingly. 492 | ''' 493 | 494 | global Foundation, AppKit, gtk, qtpy, PyQt4, PyQt5 495 | 496 | # Setup for the CYGWIN platform: 497 | if 'cygwin' in platform.system().lower(): # Cygwin has a variety of values returned by platform.system(), such as 'CYGWIN_NT-6.1' 498 | # FIXME: pyperclip currently does not support Cygwin, 499 | # see https://github.com/asweigart/pyperclip/issues/55 500 | if os.path.exists('/dev/clipboard'): 501 | warnings.warn('Pyperclip\'s support for Cygwin is not perfect, see https://github.com/asweigart/pyperclip/issues/55') 502 | return init_dev_clipboard_clipboard() 503 | 504 | # Setup for the WINDOWS platform: 505 | elif os.name == 'nt' or platform.system() == 'Windows': 506 | return init_windows_clipboard() 507 | 508 | if platform.system() == 'Linux': 509 | with open('/proc/version', 'r') as f: 510 | if "Microsoft" in f.read(): 511 | return init_wsl_clipboard() 512 | 513 | # Setup for the MAC OS X platform: 514 | if os.name == 'mac' or platform.system() == 'Darwin': 515 | try: 516 | import Foundation # check if pyobjc is installed 517 | import AppKit 518 | except ImportError: 519 | return init_osx_pbcopy_clipboard() 520 | else: 521 | return init_osx_pyobjc_clipboard() 522 | 523 | # Setup for the LINUX platform: 524 | if HAS_DISPLAY: 525 | try: 526 | import gtk # check if gtk is installed 527 | except ImportError: 528 | pass # We want to fail fast for all non-ImportError exceptions. 529 | else: 530 | return init_gtk_clipboard() 531 | 532 | if _executable_exists("xsel"): 533 | return init_xsel_clipboard() 534 | if _executable_exists("xclip"): 535 | return init_xclip_clipboard() 536 | if _executable_exists("klipper") and _executable_exists("qdbus"): 537 | return init_klipper_clipboard() 538 | 539 | try: 540 | # qtpy is a small abstraction layer that lets you write applications using a single api call to either PyQt or PySide. 541 | # https://pypi.python.org/pypi/QtPy 542 | import qtpy # check if qtpy is installed 543 | except ImportError: 544 | # If qtpy isn't installed, fall back on importing PyQt4. 545 | try: 546 | import PyQt5 # check if PyQt5 is installed 547 | except ImportError: 548 | try: 549 | import PyQt4 # check if PyQt4 is installed 550 | except ImportError: 551 | pass # We want to fail fast for all non-ImportError exceptions. 552 | else: 553 | return init_qt_clipboard() 554 | else: 555 | return init_qt_clipboard() 556 | else: 557 | return init_qt_clipboard() 558 | 559 | 560 | return init_no_clipboard() 561 | 562 | 563 | def set_clipboard(clipboard): 564 | ''' 565 | Explicitly sets the clipboard mechanism. The "clipboard mechanism" is how 566 | the copy() and paste() functions interact with the operating system to 567 | implement the copy/paste feature. The clipboard parameter must be one of: 568 | - pbcopy 569 | - pbobjc (default on Mac OS X) 570 | - gtk 571 | - qt 572 | - xclip 573 | - xsel 574 | - klipper 575 | - windows (default on Windows) 576 | - no (this is what is set when no clipboard mechanism can be found) 577 | ''' 578 | global copy, paste 579 | 580 | clipboard_types = {'pbcopy': init_osx_pbcopy_clipboard, 581 | 'pyobjc': init_osx_pyobjc_clipboard, 582 | 'gtk': init_gtk_clipboard, 583 | 'qt': init_qt_clipboard, # TODO - split this into 'qtpy', 'pyqt4', and 'pyqt5' 584 | 'xclip': init_xclip_clipboard, 585 | 'xsel': init_xsel_clipboard, 586 | 'klipper': init_klipper_clipboard, 587 | 'windows': init_windows_clipboard, 588 | 'no': init_no_clipboard} 589 | 590 | if clipboard not in clipboard_types: 591 | raise ValueError('Argument must be one of %s' % (', '.join([repr(_) for _ in clipboard_types.keys()]))) 592 | 593 | # Sets pyperclip's copy() and paste() functions: 594 | copy, paste = clipboard_types[clipboard]() 595 | 596 | 597 | def lazy_load_stub_copy(text): 598 | ''' 599 | A stub function for copy(), which will load the real copy() function when 600 | called so that the real copy() function is used for later calls. 601 | 602 | This allows users to import pyperclip without having determine_clipboard() 603 | automatically run, which will automatically select a clipboard mechanism. 604 | This could be a problem if it selects, say, the memory-heavy PyQt4 module 605 | but the user was just going to immediately call set_clipboard() to use a 606 | different clipboard mechanism. 607 | 608 | The lazy loading this stub function implements gives the user a chance to 609 | call set_clipboard() to pick another clipboard mechanism. Or, if the user 610 | simply calls copy() or paste() without calling set_clipboard() first, 611 | will fall back on whatever clipboard mechanism that determine_clipboard() 612 | automatically chooses. 613 | ''' 614 | global copy, paste 615 | copy, paste = determine_clipboard() 616 | return copy(text) 617 | 618 | 619 | def lazy_load_stub_paste(): 620 | ''' 621 | A stub function for paste(), which will load the real paste() function when 622 | called so that the real paste() function is used for later calls. 623 | 624 | This allows users to import pyperclip without having determine_clipboard() 625 | automatically run, which will automatically select a clipboard mechanism. 626 | This could be a problem if it selects, say, the memory-heavy PyQt4 module 627 | but the user was just going to immediately call set_clipboard() to use a 628 | different clipboard mechanism. 629 | 630 | The lazy loading this stub function implements gives the user a chance to 631 | call set_clipboard() to pick another clipboard mechanism. Or, if the user 632 | simply calls copy() or paste() without calling set_clipboard() first, 633 | will fall back on whatever clipboard mechanism that determine_clipboard() 634 | automatically chooses. 635 | ''' 636 | global copy, paste 637 | copy, paste = determine_clipboard() 638 | return paste() 639 | 640 | 641 | def is_available(): 642 | return copy != lazy_load_stub_copy and paste != lazy_load_stub_paste 643 | 644 | 645 | # Initially, copy() and paste() are set to lazy loading wrappers which will 646 | # set `copy` and `paste` to real functions the first time they're used, unless 647 | # set_clipboard() or determine_clipboard() is called first. 648 | copy, paste = lazy_load_stub_copy, lazy_load_stub_paste 649 | 650 | 651 | __all__ = ['copy', 'paste', 'set_clipboard', 'determine_clipboard'] 652 | -------------------------------------------------------------------------------- /1_The_Reverse_Cipher/reverse_cipher.py: -------------------------------------------------------------------------------- 1 | #Reverse Cipher 2 | 3 | message = raw_input('Enter message to be encrypted:\n') 4 | translated = '' 5 | 6 | i = len(message) - 1 7 | while i >= 0 : 8 | translated = translated + message[i] 9 | i -= 1 10 | 11 | print(translated) 12 | -------------------------------------------------------------------------------- /2_The_Caesar_Cipher/caesarCipher.py: -------------------------------------------------------------------------------- 1 | # Caesar Cipher 2 | # Basic example from https://www.nostarch.com/crackingcodes/ (BSD Licensed) 3 | import paperclip 4 | 5 | print('-------------------------\n') 6 | print('| Caesar Cipher |\n') 7 | print('-------------------------\n') 8 | print('Hello, What would you like to do? :') 9 | print('1. Encrypt with Caesar Cipher') 10 | print('2. Decrypt with Caesar Cipher') 11 | status = raw_input() 12 | 13 | # Whether the program encrypts or decrypts : 14 | if status == '1' or status == 'encrypt': 15 | mode = 'encrypt' 16 | elif status == '2' or status == 'decrypt': 17 | mode = 'decrypt' 18 | 19 | #The encryption/decryption key: 20 | keyMessage = 'Please enter the key to ' + mode +' :\n' 21 | key = int(raw_input(keyMessage)) 22 | 23 | #The encryption/decryption message: 24 | messageInput = 'Enter message to be ' + mode +'ed:\n' 25 | message = raw_input(messageInput) 26 | 27 | # Every possible that can me encrypted : 28 | SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.' 29 | 30 | # Store the encrypted /decrypted form of the message : 31 | translated = '' 32 | 33 | for symbol in message : 34 | # Note: Only symbols in the SYMBOLS string can be encrypted/decrypted. 35 | if symbol in SYMBOLS: 36 | symbolIndex = SYMBOLS.find(symbol) 37 | 38 | # Perform encryption/decryption : 39 | if mode == 'encrypt': 40 | translatedIndex = symbolIndex + key 41 | elif mode == 'decrypt': 42 | translatedIndex = symbolIndex - key 43 | 44 | # Handle wraparound, if needed: 45 | if translatedIndex >= len(SYMBOLS): 46 | translatedIndex = translatedIndex - len(SYMBOLS) 47 | elif translatedIndex < 0: 48 | translatedIndex = translatedIndex + len(SYMBOLS) 49 | 50 | translated = translated + SYMBOLS[translatedIndex] 51 | else: 52 | # Append the symbol without encrypting/decrypting: 53 | translated = translated + symbol 54 | 55 | # Output the translated string: 56 | print(translated) 57 | paperclip.copy(translated) 58 | -------------------------------------------------------------------------------- /2_The_Caesar_Cipher/paperclip.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pyperclip 3 | 4 | A cross-platform clipboard module for Python, with copy & paste functions for plain text. 5 | By Al Sweigart al@inventwithpython.com 6 | BSD License 7 | 8 | Usage: 9 | import pyperclip 10 | pyperclip.copy('The text to be copied to the clipboard.') 11 | spam = pyperclip.paste() 12 | 13 | if not pyperclip.is_available(): 14 | print("Copy functionality unavailable!") 15 | 16 | On Windows, no additional modules are needed. 17 | On Mac, the pyobjc module is used, falling back to the pbcopy and pbpaste cli 18 | commands. (These commands should come with OS X.). 19 | On Linux, install xclip or xsel via package manager. For example, in Debian: 20 | sudo apt-get install xclip 21 | sudo apt-get install xsel 22 | 23 | Otherwise on Linux, you will need the gtk or PyQt5/PyQt4 modules installed. 24 | 25 | gtk and PyQt4 modules are not available for Python 3, 26 | and this module does not work with PyGObject yet. 27 | 28 | Note: There seems to be a way to get gtk on Python 3, according to: 29 | https://askubuntu.com/questions/697397/python3-is-not-supporting-gtk-module 30 | 31 | Cygwin is currently not supported. 32 | 33 | Security Note: This module runs programs with these names: 34 | - which 35 | - where 36 | - pbcopy 37 | - pbpaste 38 | - xclip 39 | - xsel 40 | - klipper 41 | - qdbus 42 | A malicious user could rename or add programs with these names, tricking 43 | Pyperclip into running them with whatever permissions the Python process has. 44 | 45 | """ 46 | __version__ = '1.7.0' 47 | 48 | import contextlib 49 | import ctypes 50 | import os 51 | import platform 52 | import subprocess 53 | import sys 54 | import time 55 | import warnings 56 | 57 | from ctypes import c_size_t, sizeof, c_wchar_p, get_errno, c_wchar 58 | 59 | 60 | # `import PyQt4` sys.exit()s if DISPLAY is not in the environment. 61 | # Thus, we need to detect the presence of $DISPLAY manually 62 | # and not load PyQt4 if it is absent. 63 | HAS_DISPLAY = os.getenv("DISPLAY", False) 64 | 65 | EXCEPT_MSG = """ 66 | Pyperclip could not find a copy/paste mechanism for your system. 67 | For more information, please visit https://pyperclip.readthedocs.io/en/latest/introduction.html#not-implemented-error """ 68 | 69 | PY2 = sys.version_info[0] == 2 70 | 71 | STR_OR_UNICODE = unicode if PY2 else str # For paste(): Python 3 uses str, Python 2 uses unicode. 72 | 73 | ENCODING = 'utf-8' 74 | 75 | # The "which" unix command finds where a command is. 76 | if platform.system() == 'Windows': 77 | WHICH_CMD = 'where' 78 | else: 79 | WHICH_CMD = 'which' 80 | 81 | def _executable_exists(name): 82 | return subprocess.call([WHICH_CMD, name], 83 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 84 | 85 | 86 | 87 | # Exceptions 88 | class PyperclipException(RuntimeError): 89 | pass 90 | 91 | class PyperclipWindowsException(PyperclipException): 92 | def __init__(self, message): 93 | message += " (%s)" % ctypes.WinError() 94 | super(PyperclipWindowsException, self).__init__(message) 95 | 96 | 97 | def _stringifyText(text): 98 | if PY2: 99 | acceptedTypes = (unicode, str, int, float, bool) 100 | else: 101 | acceptedTypes = (str, int, float, bool) 102 | if not isinstance(text, acceptedTypes): 103 | raise PyperclipException('only str, int, float, and bool values can be copied to the clipboard, not %s' % (text.__class__.__name__)) 104 | return STR_OR_UNICODE(text) 105 | 106 | 107 | def init_osx_pbcopy_clipboard(): 108 | 109 | def copy_osx_pbcopy(text): 110 | text = _stringifyText(text) # Converts non-str values to str. 111 | p = subprocess.Popen(['pbcopy', 'w'], 112 | stdin=subprocess.PIPE, close_fds=True) 113 | p.communicate(input=text.encode(ENCODING)) 114 | 115 | def paste_osx_pbcopy(): 116 | p = subprocess.Popen(['pbpaste', 'r'], 117 | stdout=subprocess.PIPE, close_fds=True) 118 | stdout, stderr = p.communicate() 119 | return stdout.decode(ENCODING) 120 | 121 | return copy_osx_pbcopy, paste_osx_pbcopy 122 | 123 | 124 | def init_osx_pyobjc_clipboard(): 125 | def copy_osx_pyobjc(text): 126 | '''Copy string argument to clipboard''' 127 | text = _stringifyText(text) # Converts non-str values to str. 128 | newStr = Foundation.NSString.stringWithString_(text).nsstring() 129 | newData = newStr.dataUsingEncoding_(Foundation.NSUTF8StringEncoding) 130 | board = AppKit.NSPasteboard.generalPasteboard() 131 | board.declareTypes_owner_([AppKit.NSStringPboardType], None) 132 | board.setData_forType_(newData, AppKit.NSStringPboardType) 133 | 134 | def paste_osx_pyobjc(): 135 | "Returns contents of clipboard" 136 | board = AppKit.NSPasteboard.generalPasteboard() 137 | content = board.stringForType_(AppKit.NSStringPboardType) 138 | return content 139 | 140 | return copy_osx_pyobjc, paste_osx_pyobjc 141 | 142 | 143 | def init_gtk_clipboard(): 144 | global gtk 145 | import gtk 146 | 147 | def copy_gtk(text): 148 | global cb 149 | text = _stringifyText(text) # Converts non-str values to str. 150 | cb = gtk.Clipboard() 151 | cb.set_text(text) 152 | cb.store() 153 | 154 | def paste_gtk(): 155 | clipboardContents = gtk.Clipboard().wait_for_text() 156 | # for python 2, returns None if the clipboard is blank. 157 | if clipboardContents is None: 158 | return '' 159 | else: 160 | return clipboardContents 161 | 162 | return copy_gtk, paste_gtk 163 | 164 | 165 | def init_qt_clipboard(): 166 | global QApplication 167 | # $DISPLAY should exist 168 | 169 | # Try to import from qtpy, but if that fails try PyQt5 then PyQt4 170 | try: 171 | from qtpy.QtWidgets import QApplication 172 | except: 173 | try: 174 | from PyQt5.QtWidgets import QApplication 175 | except: 176 | from PyQt4.QtGui import QApplication 177 | 178 | app = QApplication.instance() 179 | if app is None: 180 | app = QApplication([]) 181 | 182 | def copy_qt(text): 183 | text = _stringifyText(text) # Converts non-str values to str. 184 | cb = app.clipboard() 185 | cb.setText(text) 186 | 187 | def paste_qt(): 188 | cb = app.clipboard() 189 | return STR_OR_UNICODE(cb.text()) 190 | 191 | return copy_qt, paste_qt 192 | 193 | 194 | def init_xclip_clipboard(): 195 | DEFAULT_SELECTION='c' 196 | PRIMARY_SELECTION='p' 197 | 198 | def copy_xclip(text, primary=False): 199 | text = _stringifyText(text) # Converts non-str values to str. 200 | selection=DEFAULT_SELECTION 201 | if primary: 202 | selection=PRIMARY_SELECTION 203 | p = subprocess.Popen(['xclip', '-selection', selection], 204 | stdin=subprocess.PIPE, close_fds=True) 205 | p.communicate(input=text.encode(ENCODING)) 206 | 207 | def paste_xclip(primary=False): 208 | selection=DEFAULT_SELECTION 209 | if primary: 210 | selection=PRIMARY_SELECTION 211 | p = subprocess.Popen(['xclip', '-selection', selection, '-o'], 212 | stdout=subprocess.PIPE, 213 | stderr=subprocess.PIPE, 214 | close_fds=True) 215 | stdout, stderr = p.communicate() 216 | # Intentionally ignore extraneous output on stderr when clipboard is empty 217 | return stdout.decode(ENCODING) 218 | 219 | return copy_xclip, paste_xclip 220 | 221 | 222 | def init_xsel_clipboard(): 223 | DEFAULT_SELECTION='-b' 224 | PRIMARY_SELECTION='-p' 225 | 226 | def copy_xsel(text, primary=False): 227 | text = _stringifyText(text) # Converts non-str values to str. 228 | selection_flag = DEFAULT_SELECTION 229 | if primary: 230 | selection_flag = PRIMARY_SELECTION 231 | p = subprocess.Popen(['xsel', selection_flag, '-i'], 232 | stdin=subprocess.PIPE, close_fds=True) 233 | p.communicate(input=text.encode(ENCODING)) 234 | 235 | def paste_xsel(primary=False): 236 | selection_flag = DEFAULT_SELECTION 237 | if primary: 238 | selection_flag = PRIMARY_SELECTION 239 | p = subprocess.Popen(['xsel', selection_flag, '-o'], 240 | stdout=subprocess.PIPE, close_fds=True) 241 | stdout, stderr = p.communicate() 242 | return stdout.decode(ENCODING) 243 | 244 | return copy_xsel, paste_xsel 245 | 246 | 247 | def init_klipper_clipboard(): 248 | def copy_klipper(text): 249 | text = _stringifyText(text) # Converts non-str values to str. 250 | p = subprocess.Popen( 251 | ['qdbus', 'org.kde.klipper', '/klipper', 'setClipboardContents', 252 | text.encode(ENCODING)], 253 | stdin=subprocess.PIPE, close_fds=True) 254 | p.communicate(input=None) 255 | 256 | def paste_klipper(): 257 | p = subprocess.Popen( 258 | ['qdbus', 'org.kde.klipper', '/klipper', 'getClipboardContents'], 259 | stdout=subprocess.PIPE, close_fds=True) 260 | stdout, stderr = p.communicate() 261 | 262 | # Workaround for https://bugs.kde.org/show_bug.cgi?id=342874 263 | # TODO: https://github.com/asweigart/pyperclip/issues/43 264 | clipboardContents = stdout.decode(ENCODING) 265 | # even if blank, Klipper will append a newline at the end 266 | assert len(clipboardContents) > 0 267 | # make sure that newline is there 268 | assert clipboardContents.endswith('\n') 269 | if clipboardContents.endswith('\n'): 270 | clipboardContents = clipboardContents[:-1] 271 | return clipboardContents 272 | 273 | return copy_klipper, paste_klipper 274 | 275 | 276 | def init_dev_clipboard_clipboard(): 277 | def copy_dev_clipboard(text): 278 | text = _stringifyText(text) # Converts non-str values to str. 279 | if text == '': 280 | warnings.warn('Pyperclip cannot copy a blank string to the clipboard on Cygwin. This is effectively a no-op.') 281 | if '\r' in text: 282 | warnings.warn('Pyperclip cannot handle \\r characters on Cygwin.') 283 | 284 | fo = open('/dev/clipboard', 'wt') 285 | fo.write(text) 286 | fo.close() 287 | 288 | def paste_dev_clipboard(): 289 | fo = open('/dev/clipboard', 'rt') 290 | content = fo.read() 291 | fo.close() 292 | return content 293 | 294 | return copy_dev_clipboard, paste_dev_clipboard 295 | 296 | 297 | def init_no_clipboard(): 298 | class ClipboardUnavailable(object): 299 | 300 | def __call__(self, *args, **kwargs): 301 | raise PyperclipException(EXCEPT_MSG) 302 | 303 | if PY2: 304 | def __nonzero__(self): 305 | return False 306 | else: 307 | def __bool__(self): 308 | return False 309 | 310 | return ClipboardUnavailable(), ClipboardUnavailable() 311 | 312 | 313 | 314 | 315 | # Windows-related clipboard functions: 316 | class CheckedCall(object): 317 | def __init__(self, f): 318 | super(CheckedCall, self).__setattr__("f", f) 319 | 320 | def __call__(self, *args): 321 | ret = self.f(*args) 322 | if not ret and get_errno(): 323 | raise PyperclipWindowsException("Error calling " + self.f.__name__) 324 | return ret 325 | 326 | def __setattr__(self, key, value): 327 | setattr(self.f, key, value) 328 | 329 | 330 | def init_windows_clipboard(): 331 | global HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, HINSTANCE, HMENU, BOOL, UINT, HANDLE 332 | from ctypes.wintypes import (HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, 333 | HINSTANCE, HMENU, BOOL, UINT, HANDLE) 334 | 335 | windll = ctypes.windll 336 | msvcrt = ctypes.CDLL('msvcrt') 337 | 338 | safeCreateWindowExA = CheckedCall(windll.user32.CreateWindowExA) 339 | safeCreateWindowExA.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD, INT, INT, 340 | INT, INT, HWND, HMENU, HINSTANCE, LPVOID] 341 | safeCreateWindowExA.restype = HWND 342 | 343 | safeDestroyWindow = CheckedCall(windll.user32.DestroyWindow) 344 | safeDestroyWindow.argtypes = [HWND] 345 | safeDestroyWindow.restype = BOOL 346 | 347 | OpenClipboard = windll.user32.OpenClipboard 348 | OpenClipboard.argtypes = [HWND] 349 | OpenClipboard.restype = BOOL 350 | 351 | safeCloseClipboard = CheckedCall(windll.user32.CloseClipboard) 352 | safeCloseClipboard.argtypes = [] 353 | safeCloseClipboard.restype = BOOL 354 | 355 | safeEmptyClipboard = CheckedCall(windll.user32.EmptyClipboard) 356 | safeEmptyClipboard.argtypes = [] 357 | safeEmptyClipboard.restype = BOOL 358 | 359 | safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData) 360 | safeGetClipboardData.argtypes = [UINT] 361 | safeGetClipboardData.restype = HANDLE 362 | 363 | safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData) 364 | safeSetClipboardData.argtypes = [UINT, HANDLE] 365 | safeSetClipboardData.restype = HANDLE 366 | 367 | safeGlobalAlloc = CheckedCall(windll.kernel32.GlobalAlloc) 368 | safeGlobalAlloc.argtypes = [UINT, c_size_t] 369 | safeGlobalAlloc.restype = HGLOBAL 370 | 371 | safeGlobalLock = CheckedCall(windll.kernel32.GlobalLock) 372 | safeGlobalLock.argtypes = [HGLOBAL] 373 | safeGlobalLock.restype = LPVOID 374 | 375 | safeGlobalUnlock = CheckedCall(windll.kernel32.GlobalUnlock) 376 | safeGlobalUnlock.argtypes = [HGLOBAL] 377 | safeGlobalUnlock.restype = BOOL 378 | 379 | wcslen = CheckedCall(msvcrt.wcslen) 380 | wcslen.argtypes = [c_wchar_p] 381 | wcslen.restype = UINT 382 | 383 | GMEM_MOVEABLE = 0x0002 384 | CF_UNICODETEXT = 13 385 | 386 | @contextlib.contextmanager 387 | def window(): 388 | """ 389 | Context that provides a valid Windows hwnd. 390 | """ 391 | # we really just need the hwnd, so setting "STATIC" 392 | # as predefined lpClass is just fine. 393 | hwnd = safeCreateWindowExA(0, b"STATIC", None, 0, 0, 0, 0, 0, 394 | None, None, None, None) 395 | try: 396 | yield hwnd 397 | finally: 398 | safeDestroyWindow(hwnd) 399 | 400 | @contextlib.contextmanager 401 | def clipboard(hwnd): 402 | """ 403 | Context manager that opens the clipboard and prevents 404 | other applications from modifying the clipboard content. 405 | """ 406 | # We may not get the clipboard handle immediately because 407 | # some other application is accessing it (?) 408 | # We try for at least 500ms to get the clipboard. 409 | t = time.time() + 0.5 410 | success = False 411 | while time.time() < t: 412 | success = OpenClipboard(hwnd) 413 | if success: 414 | break 415 | time.sleep(0.01) 416 | if not success: 417 | raise PyperclipWindowsException("Error calling OpenClipboard") 418 | 419 | try: 420 | yield 421 | finally: 422 | safeCloseClipboard() 423 | 424 | def copy_windows(text): 425 | # This function is heavily based on 426 | # http://msdn.com/ms649016#_win32_Copying_Information_to_the_Clipboard 427 | 428 | text = _stringifyText(text) # Converts non-str values to str. 429 | 430 | with window() as hwnd: 431 | # http://msdn.com/ms649048 432 | # If an application calls OpenClipboard with hwnd set to NULL, 433 | # EmptyClipboard sets the clipboard owner to NULL; 434 | # this causes SetClipboardData to fail. 435 | # => We need a valid hwnd to copy something. 436 | with clipboard(hwnd): 437 | safeEmptyClipboard() 438 | 439 | if text: 440 | # http://msdn.com/ms649051 441 | # If the hMem parameter identifies a memory object, 442 | # the object must have been allocated using the 443 | # function with the GMEM_MOVEABLE flag. 444 | count = wcslen(text) + 1 445 | handle = safeGlobalAlloc(GMEM_MOVEABLE, 446 | count * sizeof(c_wchar)) 447 | locked_handle = safeGlobalLock(handle) 448 | 449 | ctypes.memmove(c_wchar_p(locked_handle), c_wchar_p(text), count * sizeof(c_wchar)) 450 | 451 | safeGlobalUnlock(handle) 452 | safeSetClipboardData(CF_UNICODETEXT, handle) 453 | 454 | def paste_windows(): 455 | with clipboard(None): 456 | handle = safeGetClipboardData(CF_UNICODETEXT) 457 | if not handle: 458 | # GetClipboardData may return NULL with errno == NO_ERROR 459 | # if the clipboard is empty. 460 | # (Also, it may return a handle to an empty buffer, 461 | # but technically that's not empty) 462 | return "" 463 | return c_wchar_p(handle).value 464 | 465 | return copy_windows, paste_windows 466 | 467 | 468 | def init_wsl_clipboard(): 469 | def copy_wsl(text): 470 | text = _stringifyText(text) # Converts non-str values to str. 471 | p = subprocess.Popen(['clip.exe'], 472 | stdin=subprocess.PIPE, close_fds=True) 473 | p.communicate(input=text.encode(ENCODING)) 474 | 475 | def paste_wsl(): 476 | p = subprocess.Popen(['powershell.exe', '-command', 'Get-Clipboard'], 477 | stdout=subprocess.PIPE, 478 | stderr=subprocess.PIPE, 479 | close_fds=True) 480 | stdout, stderr = p.communicate() 481 | # WSL appends "\r\n" to the contents. 482 | return stdout[:-2].decode(ENCODING) 483 | 484 | return copy_wsl, paste_wsl 485 | 486 | 487 | # Automatic detection of clipboard mechanisms and importing is done in deteremine_clipboard(): 488 | def determine_clipboard(): 489 | ''' 490 | Determine the OS/platform and set the copy() and paste() functions 491 | accordingly. 492 | ''' 493 | 494 | global Foundation, AppKit, gtk, qtpy, PyQt4, PyQt5 495 | 496 | # Setup for the CYGWIN platform: 497 | if 'cygwin' in platform.system().lower(): # Cygwin has a variety of values returned by platform.system(), such as 'CYGWIN_NT-6.1' 498 | # FIXME: pyperclip currently does not support Cygwin, 499 | # see https://github.com/asweigart/pyperclip/issues/55 500 | if os.path.exists('/dev/clipboard'): 501 | warnings.warn('Pyperclip\'s support for Cygwin is not perfect, see https://github.com/asweigart/pyperclip/issues/55') 502 | return init_dev_clipboard_clipboard() 503 | 504 | # Setup for the WINDOWS platform: 505 | elif os.name == 'nt' or platform.system() == 'Windows': 506 | return init_windows_clipboard() 507 | 508 | if platform.system() == 'Linux': 509 | with open('/proc/version', 'r') as f: 510 | if "Microsoft" in f.read(): 511 | return init_wsl_clipboard() 512 | 513 | # Setup for the MAC OS X platform: 514 | if os.name == 'mac' or platform.system() == 'Darwin': 515 | try: 516 | import Foundation # check if pyobjc is installed 517 | import AppKit 518 | except ImportError: 519 | return init_osx_pbcopy_clipboard() 520 | else: 521 | return init_osx_pyobjc_clipboard() 522 | 523 | # Setup for the LINUX platform: 524 | if HAS_DISPLAY: 525 | try: 526 | import gtk # check if gtk is installed 527 | except ImportError: 528 | pass # We want to fail fast for all non-ImportError exceptions. 529 | else: 530 | return init_gtk_clipboard() 531 | 532 | if _executable_exists("xsel"): 533 | return init_xsel_clipboard() 534 | if _executable_exists("xclip"): 535 | return init_xclip_clipboard() 536 | if _executable_exists("klipper") and _executable_exists("qdbus"): 537 | return init_klipper_clipboard() 538 | 539 | try: 540 | # qtpy is a small abstraction layer that lets you write applications using a single api call to either PyQt or PySide. 541 | # https://pypi.python.org/pypi/QtPy 542 | import qtpy # check if qtpy is installed 543 | except ImportError: 544 | # If qtpy isn't installed, fall back on importing PyQt4. 545 | try: 546 | import PyQt5 # check if PyQt5 is installed 547 | except ImportError: 548 | try: 549 | import PyQt4 # check if PyQt4 is installed 550 | except ImportError: 551 | pass # We want to fail fast for all non-ImportError exceptions. 552 | else: 553 | return init_qt_clipboard() 554 | else: 555 | return init_qt_clipboard() 556 | else: 557 | return init_qt_clipboard() 558 | 559 | 560 | return init_no_clipboard() 561 | 562 | 563 | def set_clipboard(clipboard): 564 | ''' 565 | Explicitly sets the clipboard mechanism. The "clipboard mechanism" is how 566 | the copy() and paste() functions interact with the operating system to 567 | implement the copy/paste feature. The clipboard parameter must be one of: 568 | - pbcopy 569 | - pbobjc (default on Mac OS X) 570 | - gtk 571 | - qt 572 | - xclip 573 | - xsel 574 | - klipper 575 | - windows (default on Windows) 576 | - no (this is what is set when no clipboard mechanism can be found) 577 | ''' 578 | global copy, paste 579 | 580 | clipboard_types = {'pbcopy': init_osx_pbcopy_clipboard, 581 | 'pyobjc': init_osx_pyobjc_clipboard, 582 | 'gtk': init_gtk_clipboard, 583 | 'qt': init_qt_clipboard, # TODO - split this into 'qtpy', 'pyqt4', and 'pyqt5' 584 | 'xclip': init_xclip_clipboard, 585 | 'xsel': init_xsel_clipboard, 586 | 'klipper': init_klipper_clipboard, 587 | 'windows': init_windows_clipboard, 588 | 'no': init_no_clipboard} 589 | 590 | if clipboard not in clipboard_types: 591 | raise ValueError('Argument must be one of %s' % (', '.join([repr(_) for _ in clipboard_types.keys()]))) 592 | 593 | # Sets pyperclip's copy() and paste() functions: 594 | copy, paste = clipboard_types[clipboard]() 595 | 596 | 597 | def lazy_load_stub_copy(text): 598 | ''' 599 | A stub function for copy(), which will load the real copy() function when 600 | called so that the real copy() function is used for later calls. 601 | 602 | This allows users to import pyperclip without having determine_clipboard() 603 | automatically run, which will automatically select a clipboard mechanism. 604 | This could be a problem if it selects, say, the memory-heavy PyQt4 module 605 | but the user was just going to immediately call set_clipboard() to use a 606 | different clipboard mechanism. 607 | 608 | The lazy loading this stub function implements gives the user a chance to 609 | call set_clipboard() to pick another clipboard mechanism. Or, if the user 610 | simply calls copy() or paste() without calling set_clipboard() first, 611 | will fall back on whatever clipboard mechanism that determine_clipboard() 612 | automatically chooses. 613 | ''' 614 | global copy, paste 615 | copy, paste = determine_clipboard() 616 | return copy(text) 617 | 618 | 619 | def lazy_load_stub_paste(): 620 | ''' 621 | A stub function for paste(), which will load the real paste() function when 622 | called so that the real paste() function is used for later calls. 623 | 624 | This allows users to import pyperclip without having determine_clipboard() 625 | automatically run, which will automatically select a clipboard mechanism. 626 | This could be a problem if it selects, say, the memory-heavy PyQt4 module 627 | but the user was just going to immediately call set_clipboard() to use a 628 | different clipboard mechanism. 629 | 630 | The lazy loading this stub function implements gives the user a chance to 631 | call set_clipboard() to pick another clipboard mechanism. Or, if the user 632 | simply calls copy() or paste() without calling set_clipboard() first, 633 | will fall back on whatever clipboard mechanism that determine_clipboard() 634 | automatically chooses. 635 | ''' 636 | global copy, paste 637 | copy, paste = determine_clipboard() 638 | return paste() 639 | 640 | 641 | def is_available(): 642 | return copy != lazy_load_stub_copy and paste != lazy_load_stub_paste 643 | 644 | 645 | # Initially, copy() and paste() are set to lazy loading wrappers which will 646 | # set `copy` and `paste` to real functions the first time they're used, unless 647 | # set_clipboard() or determine_clipboard() is called first. 648 | copy, paste = lazy_load_stub_copy, lazy_load_stub_paste 649 | 650 | 651 | __all__ = ['copy', 'paste', 'set_clipboard', 'determine_clipboard'] 652 | -------------------------------------------------------------------------------- /2_The_Caesar_Cipher/paperclip.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ioankatsi/Cracking_Codes_With_Python/075775ffd471adb7b5b2640bac19f04679827a87/2_The_Caesar_Cipher/paperclip.pyc -------------------------------------------------------------------------------- /3_Hacking_the_Caesar_Cipher_With_Brute_Force/Hacking_the_caesar_cipher_with_brute_force.py: -------------------------------------------------------------------------------- 1 | # Caesar Cipher with Brute Force 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | print('----------------------------------------------\n') 5 | print('| Caesar Cipher With Brute-Force |\n') 6 | print('----------------------------------------------\n') 7 | 8 | #The encryption/decryption key: 9 | messageInput = 'Please enter the message to be decrypted :\n' 10 | message = raw_input(messageInput) 11 | 12 | # Every possible that can me encrypted : 13 | SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.' 14 | 15 | # Loop through every possible key: 16 | for key in range(len(SYMBOLS)): 17 | # It is important to set translated to the blank string so thta the 18 | # previous iteration's value for translated is cleared 19 | translated = '' 20 | # Loop through each symbol in message: 21 | for symbol in message : 22 | if symbol in SYMBOLS: 23 | symbolIndex = SYMBOLS.find(symbol) 24 | translatedIndex = symbolIndex - key 25 | 26 | # Handle the wraparound: 27 | if translatedIndex < 0 : 28 | translatedIndex = translatedIndex + len(SYMBOLS) 29 | 30 | # Append the decrypted symbol: 31 | translated = translated + SYMBOLS[translatedIndex] 32 | 33 | else: 34 | # Append the symbol without encrypting/decrypting 35 | translated = translated + symbol 36 | 37 | # Display every possible decryption: 38 | print('Key #%s: %s' % (key, translated)) 39 | -------------------------------------------------------------------------------- /4_Encrypting_with_the_transposition_Cipher/encrypting_with_the_transposition_cipher.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | def main(): 5 | messageInput = 'Please enter the message to be encrypted :\n' 6 | myMessage = input(messageInput) 7 | myKey = 3 8 | 9 | ciphertext = encryptMessage(myKey, myMessage) 10 | 11 | # Print the encrypted string in ciphertext to the screen, with 12 | # a | ("pipe character") after it in case there are spaces at 13 | # the end of the encrypted message: 14 | print(ciphertext + '|') 15 | 16 | 17 | def encryptMessage(key, message): 18 | # Each string in ciphretext represents a column in the grid: 19 | ciphertext = ['']*key 20 | 21 | # Loop through each column in ciphertext : 22 | for column in range(key): 23 | currentIndex = column 24 | 25 | # Keep looping until currentIndex goes past the message length: 26 | while currentIndex < len(message): 27 | # Place the character at the currentIndex in message at the 28 | # end of the current column in the ciphertext list : 29 | ciphertext[column] += message[currentIndex] 30 | 31 | # Move currentIndex over : 32 | currentIndex += key 33 | 34 | # Convert the ciphertext list into a single string value and return it: 35 | return ''.join(ciphertext) 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /5_Decrypting_with_the_transposition_cipher/transpositionDecrypt.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher Decryption 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | import math 5 | 6 | def main(): 7 | myMessage = 'Cenoonommstmme oo snnio. s s c' 8 | myKey = 8 9 | 10 | plaintext = decryptMessage(myKey, myMessage) 11 | 12 | # Print with | (called "pipe" character ) after it in case 13 | # there are spaces at the end of the decrypted message : 14 | print(plaintext + '|') 15 | 16 | def decryptMessage(key, message): 17 | # The Transposition decrypt function will simulate the columns 18 | # and rows of the grid that the plaintext is written on by using a list 19 | # of string. FIrst, we need to calculate a few values. 20 | 21 | # The number of columns in our Transposition grid: 22 | numOfColumns = int(math.ceil(len(message) / float(key))) 23 | # The number of rows in our grid : 24 | numOfRows = key 25 | # The number of 'shaded boxes' in the last 'column' of the grid : 26 | numOfShadedBoxes = (numOfColumns * numOfRows) - len(message) 27 | 28 | # Each string in plaintext represents a column in the grid : 29 | plaintext = [''] * numOfColumns 30 | 31 | # The column and row variable points to where in the grid the next 32 | # character in the encrypted message will go : 33 | column = 0 34 | row = 0 35 | 36 | for symbol in message : 37 | plaintext[column] += symbol 38 | column += 1 # Point to the next column 39 | 40 | # if there are no more columns OR we are at the shaded box, go back 41 | # to the first column and the next row : 42 | if (column == numOfColumns) or (column == numOfColumns - 1 and 43 | row >= numOfRows - numOfShadedBoxes): 44 | column = 0 45 | row += 1 46 | 47 | return ''.join(plaintext) 48 | 49 | # if transpositionDecrypt.py is run (insted of imported as a module), 50 | # call the main() function: 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /6_Programming_a_program_to_test_your_program/encrypting_with_the_transposition_cipher.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | def main(): 5 | messageInput = 'Please enter the message to be encrypted :\n' 6 | myMessage = input(messageInput) 7 | myKey = 3 8 | 9 | ciphertext = encryptMessage(myKey, myMessage) 10 | 11 | # Print the encrypted string in ciphertext to the screen, with 12 | # a | ("pipe character") after it in case there are spaces at 13 | # the end of the encrypted message: 14 | print(ciphertext + '|') 15 | 16 | 17 | def encryptMessage(key, message): 18 | # Each string in ciphretext represents a column in the grid: 19 | ciphertext = ['']*key 20 | 21 | # Loop through each column in ciphertext : 22 | for column in range(key): 23 | currentIndex = column 24 | 25 | # Keep looping until currentIndex goes past the message length: 26 | while currentIndex < len(message): 27 | # Place the character at the currentIndex in message at the 28 | # end of the current column in the ciphertext list : 29 | ciphertext[column] += message[currentIndex] 30 | 31 | # Move currentIndex over : 32 | currentIndex += key 33 | 34 | # Convert the ciphertext list into a single string value and return it: 35 | return ''.join(ciphertext) 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /6_Programming_a_program_to_test_your_program/transpositionDecrypt.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher Decryption 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | import math 5 | 6 | def main(): 7 | myMessage = 'Cenoonommstmme oo snnio. s s c' 8 | myKey = 8 9 | 10 | plaintext = decryptMessage(myKey, myMessage) 11 | 12 | # Print with | (called "pipe" character ) after it in case 13 | # there are spaces at the end of the decrypted message : 14 | print(plaintext + '|') 15 | 16 | def decryptMessage(key, message): 17 | # The Transposition decrypt function will simulate the columns 18 | # and rows of the grid that the plaintext is written on by using a list 19 | # of string. FIrst, we need to calculate a few values. 20 | 21 | # The number of columns in our Transposition grid: 22 | numOfColumns = int(math.ceil(len(message) / float(key))) 23 | # The number of rows in our grid : 24 | numOfRows = key 25 | # The number of 'shaded boxes' in the last 'column' of the grid : 26 | numOfShadedBoxes = (numOfColumns * numOfRows) - len(message) 27 | 28 | # Each string in plaintext represents a column in the grid : 29 | plaintext = [''] * numOfColumns 30 | 31 | # The column and row variable points to where in the grid the next 32 | # character in the encrypted message will go : 33 | column = 0 34 | row = 0 35 | 36 | for symbol in message : 37 | plaintext[column] += symbol 38 | column += 1 # Point to the next column 39 | 40 | # if there are no more columns OR we are at the shaded box, go back 41 | # to the first column and the next row : 42 | if (column == numOfColumns) or (column == numOfColumns - 1 and 43 | row >= numOfRows - numOfShadedBoxes): 44 | column = 0 45 | row += 1 46 | 47 | return ''.join(plaintext) 48 | 49 | # if transpositionDecrypt.py is run (insted of imported as a module), 50 | # call the main() function: 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /6_Programming_a_program_to_test_your_program/transpositionTets.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher Test 2 | # https://www.nostarch.com/crackingcodes/ 3 | 4 | import random, sys, encrypting_with_the_transposition_cipher, transpositionDecrypt 5 | 6 | def main(): 7 | random.seed(42) # set the random "seed " to static value. 8 | 9 | for i in range(20): # run 20 tests 10 | # Generate random messages to test. 11 | 12 | # The message will have a random length 13 | message = 'ABCDEFGHIJKLMOPQRSTUVWXYZ' * random.randint(4,40) 14 | 15 | # Convert the message string to a list to shuffle it : 16 | message = list(message) 17 | random.shuffle(message) 18 | message = ''.join(message) # Convert the list back to a string. 19 | 20 | print('Test #%s: "%s..."' % (i + 1, message[:50])) 21 | 22 | # Check all posible keys for each message : 23 | for key in range(1, int(len(message)/2)): 24 | encrypted = encrypting_with_the_transposition_cipher.encryptMessage(key, message) 25 | decrypted = transpositionDecrypt.decryptMessage(key, encrypted) 26 | 27 | # If the decryption does not match the original message, display 28 | # an error messag and quit: 29 | if message != decrypted : 30 | print('Mismatch with key %s and message %s.' % (key, message)) 31 | print('Decrypted as: ' + decrypted) 32 | sys.exit() 33 | 34 | print('Transposition cipher test passed.') 35 | 36 | # If transpositionTest.py is run (instead of imported as a module) call 37 | # the main() function: 38 | if __name__ == '__main__': 39 | main() 40 | -------------------------------------------------------------------------------- /7_Encrypting_and_Decrypting_Files/encrypting_with_the_transposition_cipher.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | def main(): 5 | messageInput = 'Please enter the message to be encrypted :\n' 6 | myMessage = input(messageInput) 7 | myKey = 3 8 | 9 | ciphertext = encryptMessage(myKey, myMessage) 10 | 11 | # Print the encrypted string in ciphertext to the screen, with 12 | # a | ("pipe character") after it in case there are spaces at 13 | # the end of the encrypted message: 14 | print(ciphertext + '|') 15 | 16 | 17 | def encryptMessage(key, message): 18 | # Each string in ciphretext represents a column in the grid: 19 | ciphertext = ['']*key 20 | 21 | # Loop through each column in ciphertext : 22 | for column in range(key): 23 | currentIndex = column 24 | 25 | # Keep looping until currentIndex goes past the message length: 26 | while currentIndex < len(message): 27 | # Place the character at the currentIndex in message at the 28 | # end of the current column in the ciphertext list : 29 | ciphertext[column] += message[currentIndex] 30 | 31 | # Move currentIndex over : 32 | currentIndex += key 33 | 34 | # Convert the ciphertext list into a single string value and return it: 35 | return ''.join(ciphertext) 36 | 37 | if __name__ == '__main__': 38 | main() 39 | -------------------------------------------------------------------------------- /7_Encrypting_and_Decrypting_Files/frankenstein.encrypted.txt: -------------------------------------------------------------------------------- 1 | DryIt,yaome os enm aMnaiIadiGraank r nr dmnaasIgeSaeivm eim se oafcr,i l2lmre 2 | sKa5d o.oM as c -------------------------------------------------------------------------------- /7_Encrypting_and_Decrypting_Files/frankenstein.txt: -------------------------------------------------------------------------------- 1 | Dear Sir or Madam, My name is Ioannis Katsikavelas, I am 25 years old and I am coming from Greece. -------------------------------------------------------------------------------- /7_Encrypting_and_Decrypting_Files/transpositionDecrypt.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher Decryption 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | import math 5 | 6 | def main(): 7 | myMessage = 'Cenoonommstmme oo snnio. s s c' 8 | myKey = 8 9 | 10 | plaintext = decryptMessage(myKey, myMessage) 11 | 12 | # Print with | (called "pipe" character ) after it in case 13 | # there are spaces at the end of the decrypted message : 14 | print(plaintext + '|') 15 | 16 | def decryptMessage(key, message): 17 | # The Transposition decrypt function will simulate the columns 18 | # and rows of the grid that the plaintext is written on by using a list 19 | # of string. FIrst, we need to calculate a few values. 20 | 21 | # The number of columns in our Transposition grid: 22 | numOfColumns = int(math.ceil(len(message) / float(key))) 23 | # The number of rows in our grid : 24 | numOfRows = key 25 | # The number of 'shaded boxes' in the last 'column' of the grid : 26 | numOfShadedBoxes = (numOfColumns * numOfRows) - len(message) 27 | 28 | # Each string in plaintext represents a column in the grid : 29 | plaintext = [''] * numOfColumns 30 | 31 | # The column and row variable points to where in the grid the next 32 | # character in the encrypted message will go : 33 | column = 0 34 | row = 0 35 | 36 | for symbol in message : 37 | plaintext[column] += symbol 38 | column += 1 # Point to the next column 39 | 40 | # if there are no more columns OR we are at the shaded box, go back 41 | # to the first column and the next row : 42 | if (column == numOfColumns) or (column == numOfColumns - 1 and 43 | row >= numOfRows - numOfShadedBoxes): 44 | column = 0 45 | row += 1 46 | 47 | return ''.join(plaintext) 48 | 49 | # if transpositionDecrypt.py is run (insted of imported as a module), 50 | # call the main() function: 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /7_Encrypting_and_Decrypting_Files/transpositionFileCipher.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher Decryption 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | import time, os, sys, encrypting_with_the_transposition_cipher, transpositionDecrypt 5 | 6 | def main(): 7 | inputFilename = 'frankenstein.txt' 8 | # Be careful! if a file with the outputFilename name already exists, 9 | # this program will overwrite that file : 10 | 11 | outputFilename = 'frankenstein.encrypted.txt' 12 | myKey = 10 13 | myMode = 'decrypt' # Set to 'encrypt' or 'decrypt'. 14 | 15 | # If the input file does not exist, the program terminates early: 16 | if not os.path.exists(inputFilename): 17 | print('The file %s does not exist. Quitting...' % (inputFilename)) 18 | sys.exit() 19 | 20 | # If the output file already exists, give the user a chance to quit: 21 | if os.path.exists(outputFilename): 22 | print('This will overwrite the file %s. (C)ontinue or (Q)uit' %(outputFilename)) 23 | response = input('> ') 24 | if not response.lower().startswith('c'): 25 | sys.exit() 26 | 27 | # Read in the message from the input file: 28 | fileObj = open(inputFilename) 29 | content = fileObj.read() 30 | fileObj.close() 31 | 32 | print("%sing..." %(myMode.title())) 33 | 34 | # Measure how long the encryption / decryption takes: 35 | startTime = time.time() 36 | if myMode == 'encrypt': 37 | translated = encrypting_with_the_transposition_cipher.encryptMessage(myKey, content) 38 | elif myMode == 'decrypt': 39 | translated = transpositionDecrypt.decryptMessage(myKey, content) 40 | totalTime = round(time.time() - startTime, 2) 41 | print('%sion time: %s seconds' % (myMode.title(), totalTime)) 42 | 43 | # Write out the translated message to the output file : 44 | outputFileObj = open(outputFilename, 'w') 45 | outputFileObj.write(translated) 46 | outputFileObj.close() 47 | 48 | print('Done %sing %s (%s characters).' % (myMode, inputFilename, len(content))) 49 | print('%sed file is %s.' % (myMode.title(), outputFilename)) 50 | 51 | # If TranspositionCipherFile.py is run (insted of imported as a module), 52 | # call the main() function: 53 | if __name__ == '__main__': 54 | main() 55 | -------------------------------------------------------------------------------- /8_Detecting_English_Programmatically/detectEnglish.py: -------------------------------------------------------------------------------- 1 | # Detect English module 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | # To use, type this code: 5 | # import detectEnglish 6 | # detectEnglish.isEnglish(someString) # returns True or False 7 | # (There must be a "dictionary.txt" file in this directory with all) 8 | # English words in it, one word per line. Ypu can download thos from 9 | # https://www.nostarch.com/crackingcodes/.) 10 | 11 | UPPERLETTERS = 'ABCDEFGHIJKLMONPQRSTUVWXYZ' 12 | LETTERS_AND_SPACE = UPPERLETTERS + UPPERLETTERS.lower() + ' \t\n' 13 | 14 | def loadDictionary(): 15 | dictionaryFile = open('dictionary.txt') 16 | englishWords = {} 17 | for word in dictionaryFile.read().split('\n'): 18 | englishWords[word] = None 19 | dictionaryFile.close() 20 | return englishWords 21 | 22 | ENGLISH_WORDS = loadDictionary() 23 | 24 | def getEnglishCount(message): 25 | message = message.upper() 26 | message = removeNonLetters(message) 27 | possibleWords = message.split() 28 | 29 | if possibleWords == [] : 30 | return 0.0 # No words at all, so return 0.0 31 | 32 | matches = 0 33 | for word in possibleWords: 34 | if words in ENGLISH_WORDS: 35 | matches += 1 36 | return float(matches) / len(possibleWords) 37 | 38 | def removeNonLetters(message): 39 | lettersOnly = [] 40 | for symbol in message: 41 | if symbol in LETTERS_AND_SPACE: 42 | lettersOnly.append(symbol) 43 | return ''.join(lettersOnly) 44 | 45 | def isEnglish(message, wordPercentage=20, letterPerchentage=85 ): 46 | # By default, 20% of the words must exist in the dictionary file, and 47 | # 85% of all the characters in the message must be letters or spaces 48 | # (not punctuation or numbers). 49 | 50 | wordsMatch = getEnglishCount(message) * 100 >= wordPercentage 51 | numLetters = len(removeNonLetters(message)) 52 | messageLettersPercentage = float(numLetters) / len(message) * 100 53 | lettersMatch = messageLettersPercentage >= letterPerchentage 54 | return wordsMatch and lettersMatch 55 | -------------------------------------------------------------------------------- /9_Hacking_The_Transposition_Cipher/detectEnglish.py: -------------------------------------------------------------------------------- 1 | # Detect English module 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | # To use, type this code: 5 | # import detectEnglish 6 | # detectEnglish.isEnglish(someString) # returns True or False 7 | # (There must be a "dictionary.txt" file in this directory with all) 8 | # English words in it, one word per line. Ypu can download thos from 9 | # https://www.nostarch.com/crackingcodes/.) 10 | 11 | UPPERLETTERS = 'ABCDEFGHIJKLMONPQRSTUVWXYZ' 12 | LETTERS_AND_SPACE = UPPERLETTERS + UPPERLETTERS.lower() + ' \t\n' 13 | 14 | def loadDictionary(): 15 | dictionaryFile = open('dictionary.txt') 16 | englishWords = {} 17 | for word in dictionaryFile.read().split('\n'): 18 | englishWords[word] = None 19 | dictionaryFile.close() 20 | return englishWords 21 | 22 | ENGLISH_WORDS = loadDictionary() 23 | 24 | def getEnglishCount(message): 25 | message = message.upper() 26 | message = removeNonLetters(message) 27 | possibleWords = message.split() 28 | 29 | if possibleWords == [] : 30 | return 0.0 # No words at all, so return 0.0 31 | 32 | matches = 0 33 | for word in possibleWords: 34 | if word in ENGLISH_WORDS: 35 | matches += 1 36 | return float(matches) / len(possibleWords) 37 | 38 | def removeNonLetters(message): 39 | lettersOnly = [] 40 | for symbol in message: 41 | if symbol in LETTERS_AND_SPACE: 42 | lettersOnly.append(symbol) 43 | return ''.join(lettersOnly) 44 | 45 | def isEnglish(message, wordPercentage=20, letterPerchentage=85 ): 46 | # By default, 20% of the words must exist in the dictionary file, and 47 | # 85% of all the characters in the message must be letters or spaces 48 | # (not punctuation or numbers). 49 | 50 | wordsMatch = getEnglishCount(message) * 100 >= wordPercentage 51 | numLetters = len(removeNonLetters(message)) 52 | messageLettersPercentage = float(numLetters) / len(message) * 100 53 | lettersMatch = messageLettersPercentage >= letterPerchentage 54 | return wordsMatch and lettersMatch 55 | -------------------------------------------------------------------------------- /9_Hacking_The_Transposition_Cipher/transpositionDecrypt.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher Decryption 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | import math 5 | 6 | def main(): 7 | myMessage = 'Cenoonommstmme oo snnio. s s c' 8 | myKey = 8 9 | 10 | plaintext = decryptMessage(myKey, myMessage) 11 | 12 | # Print with | (called "pipe" character ) after it in case 13 | # there are spaces at the end of the decrypted message : 14 | print(plaintext + '|') 15 | 16 | def decryptMessage(key, message): 17 | # The Transposition decrypt function will simulate the columns 18 | # and rows of the grid that the plaintext is written on by using a list 19 | # of string. FIrst, we need to calculate a few values. 20 | 21 | # The number of columns in our Transposition grid: 22 | numOfColumns = int(math.ceil(len(message) / float(key))) 23 | # The number of rows in our grid : 24 | numOfRows = key 25 | # The number of 'shaded boxes' in the last 'column' of the grid : 26 | numOfShadedBoxes = (numOfColumns * numOfRows) - len(message) 27 | 28 | # Each string in plaintext represents a column in the grid : 29 | plaintext = [''] * numOfColumns 30 | 31 | # The column and row variable points to where in the grid the next 32 | # character in the encrypted message will go : 33 | column = 0 34 | row = 0 35 | 36 | for symbol in message : 37 | plaintext[column] += symbol 38 | column += 1 # Point to the next column 39 | 40 | # if there are no more columns OR we are at the shaded box, go back 41 | # to the first column and the next row : 42 | if (column == numOfColumns) or (column == numOfColumns - 1 and 43 | row >= numOfRows - numOfShadedBoxes): 44 | column = 0 45 | row += 1 46 | 47 | return ''.join(plaintext) 48 | 49 | # if transpositionDecrypt.py is run (insted of imported as a module), 50 | # call the main() function: 51 | if __name__ == '__main__': 52 | main() 53 | -------------------------------------------------------------------------------- /9_Hacking_The_Transposition_Cipher/transpositionHacker.py: -------------------------------------------------------------------------------- 1 | # Transposition Cipher Hacker 2 | # https://www.nostarch.com/crackingcode/ (BSD Licensed) 3 | 4 | import detectEnglish, transpositionDecrypt 5 | 6 | def main(): 7 | 8 | myMessage = """AaKoosoeDe5 b5sn ma reno ora'lhlrrceey e enlh na indeit n uhoretrm au ieu v er Ne2 gmanw,forwnlbsya apor tE.no euarisfatt e mealefedhsppmgAnlnoe(c -or)alat r lw o eb nglom,Ain one dtes ilhetcdba. t tg eturmudg,tfl1e1 v nitiaicynhrCsaemie-sp ncgHt nie cetrgmnoa yc r,ieaa toesa- e a0m82e1w shcnth ekh gaecnpeutaaieetgn iodhso d ro hAe snrsfcegrt NCsLc b17m8aEheideikfr aBercaeu thllnrshicwsg etriebruaisss d iorr.""" 9 | 10 | hackedMessage = hackTransposition(myMessage) 11 | 12 | if hackedMessage == None : 13 | print('Failed to hack encryption.') 14 | else: 15 | print('Message is decrypted :') 16 | print(hackedMessage) 17 | 18 | def hackTransposition(message): 19 | print('Hacking...') 20 | 21 | # Python programs canbe stopped at any time by pressing 22 | # Ctrl-C (on Windows) or Ctrl-D (on macOS and Linux): 23 | print('(Press Ctrl-C (on Windows) or Ctrl-D (on macOS and Linux) to quit at any time.)') 24 | 25 | # Brute-force by looping though every possible key: 26 | for key in range(1, len(message)): 27 | print('Trying key #%s...' % (key)) 28 | 29 | decryptedText = transpositionDecrypt.decryptMessage(key, message) 30 | 31 | if detectEnglish.isEnglish(decryptedText): 32 | # Ask user if this is the correct decryption: 33 | print() 34 | print('Possible encryption hack:') 35 | print('Key %s: %s' % (key, decryptedText[:100])) 36 | print() 37 | print('Enter D if done, anything else to continue hacking:') 38 | response = input('> ') 39 | 40 | if response.strip().upper().startswith('D'): 41 | return decryptedText 42 | 43 | return None 44 | 45 | if __name__ == '__main__': 46 | main() 47 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Cracking Codes With Python 2 | ### These chapters are from Cracking Codes With Python Book written by Al Sweigart.
Every Chapter, generally alternate between a Cipher and how You can hack that cipher.
The Book can be found [Here](https://nostarch.com/crackingcodes). 3 | 4 | # Brief Contents 5 | - The Reverse Cipher 6 | - The Caesar Cipher 7 | - Hacking the Caesar Cipher with Brute-Force 8 | - Encrypting with Transposition Cipher 9 | - Decrypting with the Transposition Cipher 10 | - Programming a Program to Test Your Program 11 | - Encrypting and Decrypting Files 12 | - Detecting English Programmatically 13 | - Hacking the transposition Cipher 14 | - A Modular Arithmetic Module for the Affine Cipher 15 | - Programming the Affine Cipher 16 | - Hacking The Affine Cipher 17 | - Programming the Simple Substitution Cipher 18 | - Hacking the Simple Substitution Cipher 19 | - Programming the Vigenére Cipher 20 | - Frequency Analysis 21 | - Hacking the Vigenére Cipher 22 | - The One-Time Pad Cipher 23 | - Finding and Generating Prime Numbers 24 | - Generating Keys for the Public Key Cipher 25 | - Programming the Public Key Cipher 26 | 27 | 1) ### The Reverse Cipher 28 | The reiverse cipher encrypts a message by printing it in reverse order. So "Hello, world!" encrypts to "!dlrow , olleH". To Decrypt, or get the original message, you simpy reverse the encrypted message. The encryption and the decryption steps are the same . 29 | 30 | 2) ### The Caesar Cipher 31 | The reverse cipher encrypts the same way. But the Caesar cipher uses keys, which encrypt the message differently depending on which key is used. The keys for the Caesar cipher are the integers from 0 to 25. Even if a cryptanalyst knows the Caesar cipher was used, that alone doesn't give them enough information to break the cipher. The must also know the key. 32 | 33 | 3) ### Hacking The Caesar Cipher With Brute-Force 34 | We can hack the Caesar cipher by using a cryptanalytic technique called brute-force. A brute-force attack tries every possible decryption key for a cipher. 35 | 36 | 4) ### Encrypting with Transposition Cipher 37 | The transposition cipher is more difficult to brute force because the number of possible keys depends on the message's length. There are many different types of transposition ciphers, including the rail fence cipher, route cipher, Myszkowski transposition cipher. This example covers a simple transposition cipher called the ***columnar transposition cipher*** 38 | 39 | 5) ### Decrypting with the Transposition Cipher 40 | Steps for decrypting the Transposition Cipher : 41 | * Calculate the number of columns you need by dividing the length of the message by the key and then rounding up. 42 | * Draw boxes in columns and rows. Use the number of columns and you calculated before. The number of rows is the same as the key 43 | * Calculate the number of boxes to shade in by taking the total number of boxes and subtracking the length of the ciphertext message. 44 | * Shade in the number of boxes you calculated in step 3 at the bottom of the rightmost column. 45 | * Fill in the characters of the ciphertext starting at the top row and going from left to right. Skip any of the shaded boxes. 46 | * Get the plaintext by reading the leftmost column from top to bottom, and continuing to do the same in each column. 47 | 48 | 6) ### Programming A Program to Test Your Program 49 | You can not be absolutely sure the programs always work unless you test the ***encryptMessage()*** and ***decryptMessage()*** functions with all sort of message and key parameter values. But this would take a lot of time because you would have to type a message in the encryption program, set the key, run the encryption program, paste the cipher txt into the decryption program, set the key, and then run the decryption program. 50 | 51 | 7) ### Encrypting And Decrypting Files 52 | In previous examples, our programs have only worked on small messages that we type directly into the source code as string values. The cipher program we will make in this chapter will allow us to encrypt and decrypt entire files, which can be millions of characters in size. 53 | 54 | 8) ### Detecting English Programmatically 55 | Previously, we used the transposition file cipher to encrypt and decrypt entire files, 56 | but we haven't tried writing a brute-force program to hack the cipher yet. Messages encrypted with the transposition file cipher can have thousand of possible keys, which your computer can still easily brute-force, but you would then have to look through thousands of decryptions to find the one correct plaintext. As you can imagine, this can be a big problem, but there is a work-around. 57 | 58 | When the computer decrypts a message using the wrong key, the resulting string is garbage text instead of English text. We can program the computer to recognize when a decrypted message is English. That way, if the computer decrypts using the wrong key, it knows to go on and try the next possible key. Eventually, when the computer tries a key that decrypts to english text, it can stop and bring that key to your attention, sparing you from having to look through thousands of incorrect decryptions. 59 | 60 | 9) ### Hacking The Transposition Cipher 61 | We will use a brute-force approach to hack the transposition cipher. Of the thousands of keys that could possibly be associated with the transposition cipher, the correct key should be the only one that results in legible English. Using the ***detectEnglish().py*** module we wrote in previous section, our transposition cipher hacker program will help us find the correct key. 62 | 63 | 10) ### A Modular Arithmetic Module For the Affine Cipher 64 | We will learn about the multiplicative cipher and the affine cipher. The multiplicative cipher is similar 65 | to the Caesar cipher but encrypts using multiplication rather than addition. The affine cipher combines the multiplicative cipher and the Caesar cipher, resulting in a stronger and more reliable encryption. 66 | 67 | 11) ### Programming The Affine Cipher 68 | We will build and run programs to implement the affine cipher. Because the affine cipher cipher uses two different ciphers as part of its encryption process, it needs two keys: one for the multiplicative cipher and another for the Caesar cipher. For the affine program, we will split a single integer into two keys. 69 | -------------------------------------------------------------------------------- /paperclip.py: -------------------------------------------------------------------------------- 1 | """ 2 | Pyperclip 3 | 4 | A cross-platform clipboard module for Python, with copy & paste functions for plain text. 5 | By Al Sweigart al@inventwithpython.com 6 | BSD License 7 | 8 | Usage: 9 | import pyperclip 10 | pyperclip.copy('The text to be copied to the clipboard.') 11 | spam = pyperclip.paste() 12 | 13 | if not pyperclip.is_available(): 14 | print("Copy functionality unavailable!") 15 | 16 | On Windows, no additional modules are needed. 17 | On Mac, the pyobjc module is used, falling back to the pbcopy and pbpaste cli 18 | commands. (These commands should come with OS X.). 19 | On Linux, install xclip or xsel via package manager. For example, in Debian: 20 | sudo apt-get install xclip 21 | sudo apt-get install xsel 22 | 23 | Otherwise on Linux, you will need the gtk or PyQt5/PyQt4 modules installed. 24 | 25 | gtk and PyQt4 modules are not available for Python 3, 26 | and this module does not work with PyGObject yet. 27 | 28 | Note: There seems to be a way to get gtk on Python 3, according to: 29 | https://askubuntu.com/questions/697397/python3-is-not-supporting-gtk-module 30 | 31 | Cygwin is currently not supported. 32 | 33 | Security Note: This module runs programs with these names: 34 | - which 35 | - where 36 | - pbcopy 37 | - pbpaste 38 | - xclip 39 | - xsel 40 | - klipper 41 | - qdbus 42 | A malicious user could rename or add programs with these names, tricking 43 | Pyperclip into running them with whatever permissions the Python process has. 44 | 45 | """ 46 | __version__ = '1.7.0' 47 | 48 | import contextlib 49 | import ctypes 50 | import os 51 | import platform 52 | import subprocess 53 | import sys 54 | import time 55 | import warnings 56 | 57 | from ctypes import c_size_t, sizeof, c_wchar_p, get_errno, c_wchar 58 | 59 | 60 | # `import PyQt4` sys.exit()s if DISPLAY is not in the environment. 61 | # Thus, we need to detect the presence of $DISPLAY manually 62 | # and not load PyQt4 if it is absent. 63 | HAS_DISPLAY = os.getenv("DISPLAY", False) 64 | 65 | EXCEPT_MSG = """ 66 | Pyperclip could not find a copy/paste mechanism for your system. 67 | For more information, please visit https://pyperclip.readthedocs.io/en/latest/introduction.html#not-implemented-error """ 68 | 69 | PY2 = sys.version_info[0] == 2 70 | 71 | STR_OR_UNICODE = unicode if PY2 else str # For paste(): Python 3 uses str, Python 2 uses unicode. 72 | 73 | ENCODING = 'utf-8' 74 | 75 | # The "which" unix command finds where a command is. 76 | if platform.system() == 'Windows': 77 | WHICH_CMD = 'where' 78 | else: 79 | WHICH_CMD = 'which' 80 | 81 | def _executable_exists(name): 82 | return subprocess.call([WHICH_CMD, name], 83 | stdout=subprocess.PIPE, stderr=subprocess.PIPE) == 0 84 | 85 | 86 | 87 | # Exceptions 88 | class PyperclipException(RuntimeError): 89 | pass 90 | 91 | class PyperclipWindowsException(PyperclipException): 92 | def __init__(self, message): 93 | message += " (%s)" % ctypes.WinError() 94 | super(PyperclipWindowsException, self).__init__(message) 95 | 96 | 97 | def _stringifyText(text): 98 | if PY2: 99 | acceptedTypes = (unicode, str, int, float, bool) 100 | else: 101 | acceptedTypes = (str, int, float, bool) 102 | if not isinstance(text, acceptedTypes): 103 | raise PyperclipException('only str, int, float, and bool values can be copied to the clipboard, not %s' % (text.__class__.__name__)) 104 | return STR_OR_UNICODE(text) 105 | 106 | 107 | def init_osx_pbcopy_clipboard(): 108 | 109 | def copy_osx_pbcopy(text): 110 | text = _stringifyText(text) # Converts non-str values to str. 111 | p = subprocess.Popen(['pbcopy', 'w'], 112 | stdin=subprocess.PIPE, close_fds=True) 113 | p.communicate(input=text.encode(ENCODING)) 114 | 115 | def paste_osx_pbcopy(): 116 | p = subprocess.Popen(['pbpaste', 'r'], 117 | stdout=subprocess.PIPE, close_fds=True) 118 | stdout, stderr = p.communicate() 119 | return stdout.decode(ENCODING) 120 | 121 | return copy_osx_pbcopy, paste_osx_pbcopy 122 | 123 | 124 | def init_osx_pyobjc_clipboard(): 125 | def copy_osx_pyobjc(text): 126 | '''Copy string argument to clipboard''' 127 | text = _stringifyText(text) # Converts non-str values to str. 128 | newStr = Foundation.NSString.stringWithString_(text).nsstring() 129 | newData = newStr.dataUsingEncoding_(Foundation.NSUTF8StringEncoding) 130 | board = AppKit.NSPasteboard.generalPasteboard() 131 | board.declareTypes_owner_([AppKit.NSStringPboardType], None) 132 | board.setData_forType_(newData, AppKit.NSStringPboardType) 133 | 134 | def paste_osx_pyobjc(): 135 | "Returns contents of clipboard" 136 | board = AppKit.NSPasteboard.generalPasteboard() 137 | content = board.stringForType_(AppKit.NSStringPboardType) 138 | return content 139 | 140 | return copy_osx_pyobjc, paste_osx_pyobjc 141 | 142 | 143 | def init_gtk_clipboard(): 144 | global gtk 145 | import gtk 146 | 147 | def copy_gtk(text): 148 | global cb 149 | text = _stringifyText(text) # Converts non-str values to str. 150 | cb = gtk.Clipboard() 151 | cb.set_text(text) 152 | cb.store() 153 | 154 | def paste_gtk(): 155 | clipboardContents = gtk.Clipboard().wait_for_text() 156 | # for python 2, returns None if the clipboard is blank. 157 | if clipboardContents is None: 158 | return '' 159 | else: 160 | return clipboardContents 161 | 162 | return copy_gtk, paste_gtk 163 | 164 | 165 | def init_qt_clipboard(): 166 | global QApplication 167 | # $DISPLAY should exist 168 | 169 | # Try to import from qtpy, but if that fails try PyQt5 then PyQt4 170 | try: 171 | from qtpy.QtWidgets import QApplication 172 | except: 173 | try: 174 | from PyQt5.QtWidgets import QApplication 175 | except: 176 | from PyQt4.QtGui import QApplication 177 | 178 | app = QApplication.instance() 179 | if app is None: 180 | app = QApplication([]) 181 | 182 | def copy_qt(text): 183 | text = _stringifyText(text) # Converts non-str values to str. 184 | cb = app.clipboard() 185 | cb.setText(text) 186 | 187 | def paste_qt(): 188 | cb = app.clipboard() 189 | return STR_OR_UNICODE(cb.text()) 190 | 191 | return copy_qt, paste_qt 192 | 193 | 194 | def init_xclip_clipboard(): 195 | DEFAULT_SELECTION='c' 196 | PRIMARY_SELECTION='p' 197 | 198 | def copy_xclip(text, primary=False): 199 | text = _stringifyText(text) # Converts non-str values to str. 200 | selection=DEFAULT_SELECTION 201 | if primary: 202 | selection=PRIMARY_SELECTION 203 | p = subprocess.Popen(['xclip', '-selection', selection], 204 | stdin=subprocess.PIPE, close_fds=True) 205 | p.communicate(input=text.encode(ENCODING)) 206 | 207 | def paste_xclip(primary=False): 208 | selection=DEFAULT_SELECTION 209 | if primary: 210 | selection=PRIMARY_SELECTION 211 | p = subprocess.Popen(['xclip', '-selection', selection, '-o'], 212 | stdout=subprocess.PIPE, 213 | stderr=subprocess.PIPE, 214 | close_fds=True) 215 | stdout, stderr = p.communicate() 216 | # Intentionally ignore extraneous output on stderr when clipboard is empty 217 | return stdout.decode(ENCODING) 218 | 219 | return copy_xclip, paste_xclip 220 | 221 | 222 | def init_xsel_clipboard(): 223 | DEFAULT_SELECTION='-b' 224 | PRIMARY_SELECTION='-p' 225 | 226 | def copy_xsel(text, primary=False): 227 | text = _stringifyText(text) # Converts non-str values to str. 228 | selection_flag = DEFAULT_SELECTION 229 | if primary: 230 | selection_flag = PRIMARY_SELECTION 231 | p = subprocess.Popen(['xsel', selection_flag, '-i'], 232 | stdin=subprocess.PIPE, close_fds=True) 233 | p.communicate(input=text.encode(ENCODING)) 234 | 235 | def paste_xsel(primary=False): 236 | selection_flag = DEFAULT_SELECTION 237 | if primary: 238 | selection_flag = PRIMARY_SELECTION 239 | p = subprocess.Popen(['xsel', selection_flag, '-o'], 240 | stdout=subprocess.PIPE, close_fds=True) 241 | stdout, stderr = p.communicate() 242 | return stdout.decode(ENCODING) 243 | 244 | return copy_xsel, paste_xsel 245 | 246 | 247 | def init_klipper_clipboard(): 248 | def copy_klipper(text): 249 | text = _stringifyText(text) # Converts non-str values to str. 250 | p = subprocess.Popen( 251 | ['qdbus', 'org.kde.klipper', '/klipper', 'setClipboardContents', 252 | text.encode(ENCODING)], 253 | stdin=subprocess.PIPE, close_fds=True) 254 | p.communicate(input=None) 255 | 256 | def paste_klipper(): 257 | p = subprocess.Popen( 258 | ['qdbus', 'org.kde.klipper', '/klipper', 'getClipboardContents'], 259 | stdout=subprocess.PIPE, close_fds=True) 260 | stdout, stderr = p.communicate() 261 | 262 | # Workaround for https://bugs.kde.org/show_bug.cgi?id=342874 263 | # TODO: https://github.com/asweigart/pyperclip/issues/43 264 | clipboardContents = stdout.decode(ENCODING) 265 | # even if blank, Klipper will append a newline at the end 266 | assert len(clipboardContents) > 0 267 | # make sure that newline is there 268 | assert clipboardContents.endswith('\n') 269 | if clipboardContents.endswith('\n'): 270 | clipboardContents = clipboardContents[:-1] 271 | return clipboardContents 272 | 273 | return copy_klipper, paste_klipper 274 | 275 | 276 | def init_dev_clipboard_clipboard(): 277 | def copy_dev_clipboard(text): 278 | text = _stringifyText(text) # Converts non-str values to str. 279 | if text == '': 280 | warnings.warn('Pyperclip cannot copy a blank string to the clipboard on Cygwin. This is effectively a no-op.') 281 | if '\r' in text: 282 | warnings.warn('Pyperclip cannot handle \\r characters on Cygwin.') 283 | 284 | fo = open('/dev/clipboard', 'wt') 285 | fo.write(text) 286 | fo.close() 287 | 288 | def paste_dev_clipboard(): 289 | fo = open('/dev/clipboard', 'rt') 290 | content = fo.read() 291 | fo.close() 292 | return content 293 | 294 | return copy_dev_clipboard, paste_dev_clipboard 295 | 296 | 297 | def init_no_clipboard(): 298 | class ClipboardUnavailable(object): 299 | 300 | def __call__(self, *args, **kwargs): 301 | raise PyperclipException(EXCEPT_MSG) 302 | 303 | if PY2: 304 | def __nonzero__(self): 305 | return False 306 | else: 307 | def __bool__(self): 308 | return False 309 | 310 | return ClipboardUnavailable(), ClipboardUnavailable() 311 | 312 | 313 | 314 | 315 | # Windows-related clipboard functions: 316 | class CheckedCall(object): 317 | def __init__(self, f): 318 | super(CheckedCall, self).__setattr__("f", f) 319 | 320 | def __call__(self, *args): 321 | ret = self.f(*args) 322 | if not ret and get_errno(): 323 | raise PyperclipWindowsException("Error calling " + self.f.__name__) 324 | return ret 325 | 326 | def __setattr__(self, key, value): 327 | setattr(self.f, key, value) 328 | 329 | 330 | def init_windows_clipboard(): 331 | global HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, HINSTANCE, HMENU, BOOL, UINT, HANDLE 332 | from ctypes.wintypes import (HGLOBAL, LPVOID, DWORD, LPCSTR, INT, HWND, 333 | HINSTANCE, HMENU, BOOL, UINT, HANDLE) 334 | 335 | windll = ctypes.windll 336 | msvcrt = ctypes.CDLL('msvcrt') 337 | 338 | safeCreateWindowExA = CheckedCall(windll.user32.CreateWindowExA) 339 | safeCreateWindowExA.argtypes = [DWORD, LPCSTR, LPCSTR, DWORD, INT, INT, 340 | INT, INT, HWND, HMENU, HINSTANCE, LPVOID] 341 | safeCreateWindowExA.restype = HWND 342 | 343 | safeDestroyWindow = CheckedCall(windll.user32.DestroyWindow) 344 | safeDestroyWindow.argtypes = [HWND] 345 | safeDestroyWindow.restype = BOOL 346 | 347 | OpenClipboard = windll.user32.OpenClipboard 348 | OpenClipboard.argtypes = [HWND] 349 | OpenClipboard.restype = BOOL 350 | 351 | safeCloseClipboard = CheckedCall(windll.user32.CloseClipboard) 352 | safeCloseClipboard.argtypes = [] 353 | safeCloseClipboard.restype = BOOL 354 | 355 | safeEmptyClipboard = CheckedCall(windll.user32.EmptyClipboard) 356 | safeEmptyClipboard.argtypes = [] 357 | safeEmptyClipboard.restype = BOOL 358 | 359 | safeGetClipboardData = CheckedCall(windll.user32.GetClipboardData) 360 | safeGetClipboardData.argtypes = [UINT] 361 | safeGetClipboardData.restype = HANDLE 362 | 363 | safeSetClipboardData = CheckedCall(windll.user32.SetClipboardData) 364 | safeSetClipboardData.argtypes = [UINT, HANDLE] 365 | safeSetClipboardData.restype = HANDLE 366 | 367 | safeGlobalAlloc = CheckedCall(windll.kernel32.GlobalAlloc) 368 | safeGlobalAlloc.argtypes = [UINT, c_size_t] 369 | safeGlobalAlloc.restype = HGLOBAL 370 | 371 | safeGlobalLock = CheckedCall(windll.kernel32.GlobalLock) 372 | safeGlobalLock.argtypes = [HGLOBAL] 373 | safeGlobalLock.restype = LPVOID 374 | 375 | safeGlobalUnlock = CheckedCall(windll.kernel32.GlobalUnlock) 376 | safeGlobalUnlock.argtypes = [HGLOBAL] 377 | safeGlobalUnlock.restype = BOOL 378 | 379 | wcslen = CheckedCall(msvcrt.wcslen) 380 | wcslen.argtypes = [c_wchar_p] 381 | wcslen.restype = UINT 382 | 383 | GMEM_MOVEABLE = 0x0002 384 | CF_UNICODETEXT = 13 385 | 386 | @contextlib.contextmanager 387 | def window(): 388 | """ 389 | Context that provides a valid Windows hwnd. 390 | """ 391 | # we really just need the hwnd, so setting "STATIC" 392 | # as predefined lpClass is just fine. 393 | hwnd = safeCreateWindowExA(0, b"STATIC", None, 0, 0, 0, 0, 0, 394 | None, None, None, None) 395 | try: 396 | yield hwnd 397 | finally: 398 | safeDestroyWindow(hwnd) 399 | 400 | @contextlib.contextmanager 401 | def clipboard(hwnd): 402 | """ 403 | Context manager that opens the clipboard and prevents 404 | other applications from modifying the clipboard content. 405 | """ 406 | # We may not get the clipboard handle immediately because 407 | # some other application is accessing it (?) 408 | # We try for at least 500ms to get the clipboard. 409 | t = time.time() + 0.5 410 | success = False 411 | while time.time() < t: 412 | success = OpenClipboard(hwnd) 413 | if success: 414 | break 415 | time.sleep(0.01) 416 | if not success: 417 | raise PyperclipWindowsException("Error calling OpenClipboard") 418 | 419 | try: 420 | yield 421 | finally: 422 | safeCloseClipboard() 423 | 424 | def copy_windows(text): 425 | # This function is heavily based on 426 | # http://msdn.com/ms649016#_win32_Copying_Information_to_the_Clipboard 427 | 428 | text = _stringifyText(text) # Converts non-str values to str. 429 | 430 | with window() as hwnd: 431 | # http://msdn.com/ms649048 432 | # If an application calls OpenClipboard with hwnd set to NULL, 433 | # EmptyClipboard sets the clipboard owner to NULL; 434 | # this causes SetClipboardData to fail. 435 | # => We need a valid hwnd to copy something. 436 | with clipboard(hwnd): 437 | safeEmptyClipboard() 438 | 439 | if text: 440 | # http://msdn.com/ms649051 441 | # If the hMem parameter identifies a memory object, 442 | # the object must have been allocated using the 443 | # function with the GMEM_MOVEABLE flag. 444 | count = wcslen(text) + 1 445 | handle = safeGlobalAlloc(GMEM_MOVEABLE, 446 | count * sizeof(c_wchar)) 447 | locked_handle = safeGlobalLock(handle) 448 | 449 | ctypes.memmove(c_wchar_p(locked_handle), c_wchar_p(text), count * sizeof(c_wchar)) 450 | 451 | safeGlobalUnlock(handle) 452 | safeSetClipboardData(CF_UNICODETEXT, handle) 453 | 454 | def paste_windows(): 455 | with clipboard(None): 456 | handle = safeGetClipboardData(CF_UNICODETEXT) 457 | if not handle: 458 | # GetClipboardData may return NULL with errno == NO_ERROR 459 | # if the clipboard is empty. 460 | # (Also, it may return a handle to an empty buffer, 461 | # but technically that's not empty) 462 | return "" 463 | return c_wchar_p(handle).value 464 | 465 | return copy_windows, paste_windows 466 | 467 | 468 | def init_wsl_clipboard(): 469 | def copy_wsl(text): 470 | text = _stringifyText(text) # Converts non-str values to str. 471 | p = subprocess.Popen(['clip.exe'], 472 | stdin=subprocess.PIPE, close_fds=True) 473 | p.communicate(input=text.encode(ENCODING)) 474 | 475 | def paste_wsl(): 476 | p = subprocess.Popen(['powershell.exe', '-command', 'Get-Clipboard'], 477 | stdout=subprocess.PIPE, 478 | stderr=subprocess.PIPE, 479 | close_fds=True) 480 | stdout, stderr = p.communicate() 481 | # WSL appends "\r\n" to the contents. 482 | return stdout[:-2].decode(ENCODING) 483 | 484 | return copy_wsl, paste_wsl 485 | 486 | 487 | # Automatic detection of clipboard mechanisms and importing is done in deteremine_clipboard(): 488 | def determine_clipboard(): 489 | ''' 490 | Determine the OS/platform and set the copy() and paste() functions 491 | accordingly. 492 | ''' 493 | 494 | global Foundation, AppKit, gtk, qtpy, PyQt4, PyQt5 495 | 496 | # Setup for the CYGWIN platform: 497 | if 'cygwin' in platform.system().lower(): # Cygwin has a variety of values returned by platform.system(), such as 'CYGWIN_NT-6.1' 498 | # FIXME: pyperclip currently does not support Cygwin, 499 | # see https://github.com/asweigart/pyperclip/issues/55 500 | if os.path.exists('/dev/clipboard'): 501 | warnings.warn('Pyperclip\'s support for Cygwin is not perfect, see https://github.com/asweigart/pyperclip/issues/55') 502 | return init_dev_clipboard_clipboard() 503 | 504 | # Setup for the WINDOWS platform: 505 | elif os.name == 'nt' or platform.system() == 'Windows': 506 | return init_windows_clipboard() 507 | 508 | if platform.system() == 'Linux': 509 | with open('/proc/version', 'r') as f: 510 | if "Microsoft" in f.read(): 511 | return init_wsl_clipboard() 512 | 513 | # Setup for the MAC OS X platform: 514 | if os.name == 'mac' or platform.system() == 'Darwin': 515 | try: 516 | import Foundation # check if pyobjc is installed 517 | import AppKit 518 | except ImportError: 519 | return init_osx_pbcopy_clipboard() 520 | else: 521 | return init_osx_pyobjc_clipboard() 522 | 523 | # Setup for the LINUX platform: 524 | if HAS_DISPLAY: 525 | try: 526 | import gtk # check if gtk is installed 527 | except ImportError: 528 | pass # We want to fail fast for all non-ImportError exceptions. 529 | else: 530 | return init_gtk_clipboard() 531 | 532 | if _executable_exists("xsel"): 533 | return init_xsel_clipboard() 534 | if _executable_exists("xclip"): 535 | return init_xclip_clipboard() 536 | if _executable_exists("klipper") and _executable_exists("qdbus"): 537 | return init_klipper_clipboard() 538 | 539 | try: 540 | # qtpy is a small abstraction layer that lets you write applications using a single api call to either PyQt or PySide. 541 | # https://pypi.python.org/pypi/QtPy 542 | import qtpy # check if qtpy is installed 543 | except ImportError: 544 | # If qtpy isn't installed, fall back on importing PyQt4. 545 | try: 546 | import PyQt5 # check if PyQt5 is installed 547 | except ImportError: 548 | try: 549 | import PyQt4 # check if PyQt4 is installed 550 | except ImportError: 551 | pass # We want to fail fast for all non-ImportError exceptions. 552 | else: 553 | return init_qt_clipboard() 554 | else: 555 | return init_qt_clipboard() 556 | else: 557 | return init_qt_clipboard() 558 | 559 | 560 | return init_no_clipboard() 561 | 562 | 563 | def set_clipboard(clipboard): 564 | ''' 565 | Explicitly sets the clipboard mechanism. The "clipboard mechanism" is how 566 | the copy() and paste() functions interact with the operating system to 567 | implement the copy/paste feature. The clipboard parameter must be one of: 568 | - pbcopy 569 | - pbobjc (default on Mac OS X) 570 | - gtk 571 | - qt 572 | - xclip 573 | - xsel 574 | - klipper 575 | - windows (default on Windows) 576 | - no (this is what is set when no clipboard mechanism can be found) 577 | ''' 578 | global copy, paste 579 | 580 | clipboard_types = {'pbcopy': init_osx_pbcopy_clipboard, 581 | 'pyobjc': init_osx_pyobjc_clipboard, 582 | 'gtk': init_gtk_clipboard, 583 | 'qt': init_qt_clipboard, # TODO - split this into 'qtpy', 'pyqt4', and 'pyqt5' 584 | 'xclip': init_xclip_clipboard, 585 | 'xsel': init_xsel_clipboard, 586 | 'klipper': init_klipper_clipboard, 587 | 'windows': init_windows_clipboard, 588 | 'no': init_no_clipboard} 589 | 590 | if clipboard not in clipboard_types: 591 | raise ValueError('Argument must be one of %s' % (', '.join([repr(_) for _ in clipboard_types.keys()]))) 592 | 593 | # Sets pyperclip's copy() and paste() functions: 594 | copy, paste = clipboard_types[clipboard]() 595 | 596 | 597 | def lazy_load_stub_copy(text): 598 | ''' 599 | A stub function for copy(), which will load the real copy() function when 600 | called so that the real copy() function is used for later calls. 601 | 602 | This allows users to import pyperclip without having determine_clipboard() 603 | automatically run, which will automatically select a clipboard mechanism. 604 | This could be a problem if it selects, say, the memory-heavy PyQt4 module 605 | but the user was just going to immediately call set_clipboard() to use a 606 | different clipboard mechanism. 607 | 608 | The lazy loading this stub function implements gives the user a chance to 609 | call set_clipboard() to pick another clipboard mechanism. Or, if the user 610 | simply calls copy() or paste() without calling set_clipboard() first, 611 | will fall back on whatever clipboard mechanism that determine_clipboard() 612 | automatically chooses. 613 | ''' 614 | global copy, paste 615 | copy, paste = determine_clipboard() 616 | return copy(text) 617 | 618 | 619 | def lazy_load_stub_paste(): 620 | ''' 621 | A stub function for paste(), which will load the real paste() function when 622 | called so that the real paste() function is used for later calls. 623 | 624 | This allows users to import pyperclip without having determine_clipboard() 625 | automatically run, which will automatically select a clipboard mechanism. 626 | This could be a problem if it selects, say, the memory-heavy PyQt4 module 627 | but the user was just going to immediately call set_clipboard() to use a 628 | different clipboard mechanism. 629 | 630 | The lazy loading this stub function implements gives the user a chance to 631 | call set_clipboard() to pick another clipboard mechanism. Or, if the user 632 | simply calls copy() or paste() without calling set_clipboard() first, 633 | will fall back on whatever clipboard mechanism that determine_clipboard() 634 | automatically chooses. 635 | ''' 636 | global copy, paste 637 | copy, paste = determine_clipboard() 638 | return paste() 639 | 640 | 641 | def is_available(): 642 | return copy != lazy_load_stub_copy and paste != lazy_load_stub_paste 643 | 644 | 645 | # Initially, copy() and paste() are set to lazy loading wrappers which will 646 | # set `copy` and `paste` to real functions the first time they're used, unless 647 | # set_clipboard() or determine_clipboard() is called first. 648 | copy, paste = lazy_load_stub_copy, lazy_load_stub_paste 649 | 650 | 651 | __all__ = ['copy', 'paste', 'set_clipboard', 'determine_clipboard'] 652 | --------------------------------------------------------------------------------