Złam ten kod z Pythonem
Jak
tworzyć, testować i łamać szyfry
Szyfrowanie
do niedawna było wiązane z bezpieczeństwem publicznym.
Najbezpieczniejsze implementacje podlegały takim samym rządowym
regulacjom jak przemysł zbrojeniowy. Do dzisiaj rządy i
różnego rodzaju służby dążą do uzyskania możliwości
odczytywania zaszyfrowanych danych. Tymczasem silna kryptografia jest
podstawą globalnej ekonomii, zapewnia codzienną ochronę milionom
użytkowników i większości organizacji. A to nie wszystko.
Algorytmy szyfrujące, ich implementacja czy programowe łamanie
szyfrów to równocześnie fascynująca dziedzina
wiedzy i pole do zabawy, ćwiczeń oraz eksperymentowania z
programowaniem.
Ta książka jest
przeznaczona dla osób, które nie umieją
programować, ale chciałyby zapoznać się z kryptografią.
Omówiono
tu podstawowe koncepcje programowania w Pythonie, który dziś
jest uważany za najlepszy język dla początkujących koderów.
Pokazano, jak tworzyć, testować i łamać programy implementujące szyfry
klasyczne, takie jak przestawieniowy i Vigenere'a, by stopniowo przejść
do znacznie bardziej zaawansowanych zagadnień, w tym kryptografii
klucza publicznego. Każdy program przedstawiono w postaci pełnego kodu
źródłowego, wyjaśniono także wiersz po wierszu jego
działanie. Dzięki tej książce można się zarówno nauczyć
zasad kryptografii, jak i zdobyć umiejętności pisania kodu szyfrującego
i deszyfrującego w Pythonie.
Znajdziesz tutaj między innymi:
- wprowadzenie do
programowania w Pythonie: pętle, zmienne, kontrola przepływu działania
programu
- omówienie
technik szyfrowania stosowanych przed wynalezieniem
komputerów
- różne algorytmy
szyfrowania z wykorzystaniem Pythona
- testowanie
programów szyfrujących i deszyfrujących
- szyfrowanie i
deszyfrowanie plików
- łamanie szyfrów
techniką brute force czy analiza częstotliwości
O
autorze 4
O korektorach merytorycznych 4
Podziękowania 17
Wprowadzenie 19
Kto powinien przeczytać tę książkę? 20
Co znajdziesz w tej książce? 21
Jak używać tej książki? 23
Wpisywanie kodu źródłowego 23
Sprawdzanie pod kątem błędów 23
Konwencje zastosowane w książce 24
Zasoby w internecie 24
Pobieranie i instalowanie Pythona 24
Instalacja Pythona w systemie Windows 24
Instalacja Pythona w systemie macOS 25
Instalacja Pythona w systemie Ubuntu 25
Pobieranie pliku pyperclip.py 25
Uruchamianie środowiska IDLE 26
Podsumowanie 27
1.
Papier
jako narzędzie kryptograficzne 29
Co to jest kryptografia? 30
Kod a szyfr 30
Szyfr Cezara 32
Krążek szyfrowania 32
Szyfrowanie wiadomości za pomocą krążka szyfrowania 33
Deszyfrowanie za pomocą krążka szyfrowania 34
Szyfrowanie i deszyfrowanie z użyciem arytmetyki 35
Dlaczego podwójne szyfrowanie nie działa? 36
Podsumowanie 36
2.
Programowanie
w powłoce interaktywnej 39
Kilka prostych wyrażeń matematycznych 40
Wartości całkowite i wartości zmiennoprzecinkowe 41
Wyrażenia 41
Kolejność wykonywania działań 42
Obliczanie wartości wyrażeń 42
Przechowywanie wartości w zmiennych 43
Nadpisywanie zmiennej 45
Nazwy zmiennych 46
Podsumowanie 47
3.
Ciągi
tekstowe i tworzenie programów
49
Praca z tekstem przy użyciu wartości w postaci ciągu tekstowego 50
Konkatenacja ciągu tekstowego za pomocą operatora + 51
Replikacja ciągu tekstowego przy użyciu operatora * 52
Pobieranie znaków z ciągu tekstowego przy użyciu
indeksów 53
Wyświetlanie wartości za pomocą funkcji print() 56
Wyświetlanie znaków sterujących 57
Apostrof i cudzysłów 58
Tworzenie programów w edytorze pliku IDLE 59
Kod źródłowy programu typu Witaj, świecie! 59
Sprawdzanie kodu źródłowego za pomocą narzędzia Online Diff
Tool 61
Użycie środowiska IDLE w celu późniejszego uzyskania dostępu
do programu 62
Zapisywanie programu 62
Uruchamianie programu 63
Otwieranie wcześniej zapisanych programów 64
W jaki sposób działa program Witaj, świecie!? 64
Komentarze 64
Wyświetlanie wskazówek dla użytkownika 65
Pobieranie danych wejściowych od użytkownika 65
Zakończenie programu 66
Podsumowanie 66
4.
Szyfr
odwrotny 69
Kod źródłowy programu wykorzystującego szyfr odwrotny 70
Przykładowe uruchomienie programu 70
Definiowanie komentarzy i zmiennych 71
Określanie długości ciągu tekstowego 72
Wprowadzenie do pętli while 73
Boolowski typ danych 73
Operatory porównania 74
Blok kodu 76
Konstrukcja pętli while 77
"Rośnięcie" ciągu tekstowego 78
Usprawnianie programu za pomocą funkcji input() 81
Podsumowanie 82
5.
Szyfr
Cezara 85
Kod źródłowy programu wykorzystującego szyfr Cezara 86
Przykładowe uruchomienie programu 87
Importowanie modułu i przypisywanie zmiennych 88
Stałe i zmienne 89
Pętla for 90
Przykład pętli for 90
Pętla while będąca odpowiednikiem pętli for 91
Konstrukcja if 92
Przykład użycia polecenia if 92
Polecenie else 92
Polecenie elif 93
Operatory in i not in 94
Metoda find() 95
Szyfrowanie i deszyfrowanie symboli 96
Obsługa zawinięcia 97
Obsługa symboli spoza zbioru symboli 98
Wyświetlanie i kopiowanie skonwertowanego ciągu tekstowego 98
Szyfrowanie innych symboli 99
Podsumowanie 100
6.
Łamanie
szyfru Cezara za pomocą ataku brute force 103
Kod źródłowy programu wykorzystującego szyfr odwrotny 104
Przykładowe uruchomienie programu 105
Definiowanie zmiennych 106
Iteracja z użyciem funkcji range() 106
Deszyfrowanie wiadomości 108
Stosowanie formatowania ciągu tekstowego do wyświetlenia klucza i
deszyfrowanej wiadomości 109
Podsumowanie 110
7.
Szyfrowanie za pomocą szyfru przestawieniowego
113
Sposób działania szyfru przestawieniowego 113
Ręczne szyfrowanie wiadomości 114
Tworzenie programu szyfrującego 116
Kod źródłowy programu wykorzystującego szyfr kolumnowy 117
Przykładowe uruchomienie programu 118
Samodzielne definiowanie funkcji za pomocą polecenia def 118
Definiowanie funkcji pobierającej argumenty 119
Zmiana parametru istniejącego tylko wewnątrz funkcji 120
Definiowanie funkcji main() 121
Przekazywanie klucza i wiadomości jako argumentów 122
Typ danych listy 123
Ponowne przypisywanie elementów na liście 124
Lista list 125
Stosowanie funkcji len() i operatora in z listą 126
Konkatenacja listy i replikacja za pomocą operatorów + i *
127
Algorytm szyfrowania przestawieniowego 127
Rozszerzone operatory przypisania 128
Iteracja currentIndex przez wiadomość 129
Metoda join() 131
Wartość zwrotna i polecenie return 132
Przykład polecenia return 132
Zwrot szyfrogramu 133
Zmienna __name__ 133
Podsumowanie 134
8.
Deszyfrowanie wiadomości chronionej szyfrem przestawieniowym 137
Łamanie szyfru przestawieniowego za pomocą kartki i ołówka
138
Kod źródłowy programu deszyfrującego wiadomość chronioną
szyfrem przestawieniowym 139
Przykładowe uruchomienie programu 141
Importowanie modułów i definiowanie funkcji main() 141
Deszyfrowanie wiadomości za pomocą klucza 142
Funkcje round(), math.ceil() i math.floor() 142
Funkcja decryptMessage() 143
Operatory boolowskie 145
Dostosowywanie wartości zmiennych column i row 148
Wywoływanie funkcji main() 150
Podsumowanie 150
9.
Tworzenie
programu do testowania innych programów
153
Kod źródłowy programu do testowania innych
programów 154
Przykładowe uruchomienie programu 155
Importowanie modułów 156
Generowanie liczb pseudolosowych 156
Tworzenie losowo wybranego ciągu tekstowego 158
Powielanie ciągu tekstowego losowo wybraną liczbę razy 158
Zmienna listy używa odwołania 159
Przekazywanie odwołania 162
Stosowanie funkcji copy.deepcopy() do powielenia listy 162
Funkcja random.shuffle() 163
Losowe mieszanie ciągu tekstowego 163
Testowanie poszczególnych wiadomości 164
Sprawdzanie poprawności szyfrowania i zakończenie programu 165
Wywoływanie funkcji main() 166
Testowanie programu 166
Podsumowanie 167
10.
Szyfrowanie
i deszyfrowanie plików
169
Pliki zwykłego tekstu 170
Kod źródłowy programu wykorzystującego szyfr przestawieniowy
do szyfrowania pliku 170
Przykładowe uruchomienie programu 171
Praca z plikami 172
Otwieranie pliku 172
Zapisywanie i zamykanie pliku 173
Odczyt danych z pliku 174
Funkcja main() programu 175
Sprawdzanie istnienia pliku 175
Funkcja os.path.exists() 176
Sprawdzanie za pomocą funkcji os.path.exists() istnienia pliku danych
wejściowych 176
Stosowanie metod ciągu tekstowego do zapewnienia większej elastyczności
danych wejściowych 177
Metody ciągu tekstowego upper(), lower() i title() 177
Metody ciągu tekstowego startswith() i endswith() 177
Stosowanie metod ciągu tekstowego w programie 178
Odczyt pliku danych wejściowych 179
Pomiar czasu operacji szyfrowania i deszyfrowania 179
Moduł time i funkcja time.time() 179
Stosowanie funkcji time.time() w programie 180
Zapis danych wyjściowych do pliku 181
Wywoływanie funkcji main() 181
Podsumowanie 182
11.
Programowe
wykrywanie języka angielskiego
183
Jak komputer może zrozumieć język angielski? 184
Kod źródłowy modułu do wykrywania języka angielskiego 186
Przykładowe uruchomienie programu 187
Polecenia i definiowanie stałych 187
Typ danych w postaci słownika 188
Różnice między słownikiem i listą 189
Dodawanie lub modyfikowanie elementów słownika 190
Stosowanie funkcji len() ze słownikiem 191
Stosowanie operatora in ze słownikiem 191
Wyszukiwanie elementów w słowniku odbywa się szybciej niż na
liście 192
Stosowanie pętli for w słowniku 192
Implementacja pliku słownika 193
Metoda split() 193
Podział słownika na poszczególne słowa 194
Zwrot danych słownika 194
Zliczanie liczby słów angielskich w wiadomości 195
Błąd dzielenia przez zero 196
Zliczanie dopasowań słów w języku angielskim 196
Funkcje float(), int() i str() oraz dzielenie całkowite 197
Określanie proporcji angielskich słów w wiadomości 198
Usuwanie znaków innych niż litery 198
Metoda append() typu listy 199
Tworzenie ciągu tekstowego liter 200
Wykrywanie słów angielskich 200
Stosowanie argumentów domyślnych 200
Obliczanie wartości procentowych 201
Podsumowanie 203
12.
Łamanie
szyfru przestawieniowego 205
Kod źródłowy programu umożliwiającego złamanie szyfru
przestawieniowego 206
Przykładowe uruchomienie programu 207
Importowanie modułów 208
Wielowierszowy ciąg tekstowy ujęty w potrójny
cudzysłów 208
Wyświetlanie wyniku deszyfrowania wiadomości 209
Pobranie deszyfrowanej wiadomości 210
Metoda strip() ciągu tekstowego 212
Stosowanie metody strip() ciągu tekstowego 213
Nieudana próba deszyfrowania wiadomości 213
Wywoływanie funkcji main() 214
Podsumowanie 214
13.
Moduł
arytmetyki modularnej dla szyfru afinicznego
215
Arytmetyka modularna 216
Operator reszty z dzielenia 217
Wyszukiwanie dzielników do obliczenia największego
wspólnego dzielnika 218
Przypisanie wielokrotne 220
Algorytm Euklidesa do wyszukiwania największego wspólnego
dzielnika 221
Sposób działania szyfrów multiplikatywnego i
afinicznego 222
Wybór poprawnego klucza multiplikatywnego 223
Szyfrowanie z użyciem szyfru afinicznego 224
Deszyfrowanie szyfru afinicznego 225
Określanie odwrotności modularnej 226
Operator dzielenia całkowitego 226
Kod źródłowy modułu cryptomath 227
Podsumowanie 228
14.
Programowanie szyfru afinicznego 231
Kod źródłowy programu wykorzystującego szyfr afiniczny 232
Przykładowe uruchomienie programu 233
Importowanie modułów i stałych oraz definiowanie funkcji
main() 234
Generowanie i weryfikowanie kluczy 236
Typ danych w postaci krotki 236
Sprawdzanie pod kątem słabych kluczy 237
Ile kluczy może mieć szyfr afiniczny? 238
Tworzenie funkcji szyfrującej 240
Tworzenie funkcji deszyfrującej 241
Generowanie losowych kluczy 242
Wywoływanie funkcji main() 243
Podsumowanie 244
15.
Łamanie
szyfru afinicznego 245
Kod źródłowy programu umożliwiającego złamanie szyfru
afinicznego 245
Przykładowe uruchomienie programu 247
Importowanie modułów i stałych oraz definiowanie funkcji
main() 248
Funkcja odpowiedzialna za złamanie szyfru afinicznego 249
Operator wykładniczy 249
Obliczanie całkowitej liczby kluczy, których można użyć 250
Polecenie continue 251
Stosowanie polecenia continue do pominięcia kodu 252
Wywoływanie funkcji main() 253
Podsumowanie 254
16.
Programowanie
prostego szyfru podstawieniowego
255
Jak działa prosty szyfr podstawieniowy? 256
Kod źródłowy programu wykorzystującego szyfr podstawieniowy
257
Przykładowe uruchomienie programu 259
Importowanie modułów i stałych oraz definiowanie funkcji
main() 259
Metoda sort() listy 261
Funkcje opakowujące 262
Funkcja translateMessage() 263
Metody isupper() i islower() ciągu tekstowego 265
Zachowywanie wielkości liter dzięki metodzie isupper() 266
Generowanie losowego klucza 267
Wywoływanie funkcji main() 268
Podsumowanie 268
17.
Łamanie prostego szyfru podstawieniowego
271
Stosowanie wzorca słowa do deszyfrowania 272
Znajdowanie wzorca słowa 272
Wyszukiwanie potencjalnych liter odszyfrowujących 273
Omówienie procesu łamania szyfru 275
Moduł wzorca słowa 275
Kod źródłowy programu wykorzystującego szyfr podstawieniowy
276
Przykładowe uruchomienie programu 280
Importowanie modułów i stałych 280
Wyszukiwanie znaków za pomocą wyrażeń regularnych 281
Konfigurowanie funkcji main() 281
Wyświetlanie użytkownikowi wyniku operacji łamania szyfru 282
Tworzenie mapowania szyfrogramu 283
Tworzenie pustego mapowania 283
Dodawanie liter do mapowania 283
Łączenie dwóch mapowań 285
W jaki sposób działają funkcje pomocnicze mapowania liter?
286
Wyszukiwanie zdeszyfrowanych liter w mapowaniu 290
Testowanie funkcji removeSolvedLettersFromMapping() 292
Funkcja hackSimpleSub() 292
Metoda replace() ciągu tekstowego 294
Deszyfrowanie wiadomości 295
Deszyfrowanie w powłoce interaktywnej 296
Wywoływanie funkcji main() 297
Podsumowanie 298
18.
Programowanie
szyfru Vigenere'a 299
Stosowanie wielu liter kluczy w szyfrze Vigenere'a 300
Dłuższe klucze szyfru Vigenere'a są znacznie bezpieczniejsze 302
Wybór klucza uniemożliwiającego atak słownikowy 303
Kod źródłowy programu wykorzystującego szyfr Vigenere'a 303
Przykładowe uruchomienie programu 305
Importowanie modułów i stałych oraz definiowanie funkcji
main() 305
Tworzenie ciągu tekstowego za pomocą procesu dołączania do listy 306
Szyfrowanie i deszyfrowanie wiadomości 307
Wywoływanie funkcji main() 310
Podsumowanie 310
19.
Analiza
częstotliwości 313
Analiza częstotliwości występowania liter w tekście 314
Dopasowywanie częstotliwości występowania liter 316
Obliczanie wyniku dopasowania częstotliwości dla prostego szyfru
podstawieniowego 316
Obliczanie wyniku dopasowania częstotliwości dla prostego szyfru
przestawieniowego 317
Stosowanie analizy częstotliwości do złamania szyfru Vigenere'a 318
Kod źródłowy programu obliczającego wynik dopasowania
częstotliwości 319
Przechowywanie liter w kolejności ETAOIN 321
Zliczanie liter w wiadomości 321
Pobieranie pierwszego elementu składowego krotki 323
Układanie liter według częstotliwości ich występowania w wiadomości 323
Zliczanie liter za pomocą funkcji getLetterCount() 324
Tworzenie słownika częstotliwości wystąpień i listy liter 324
Sortowanie liter w odwrotnej kolejności ETAOIN 325
Sortowanie list słownika według częstotliwości występowania 330
Tworzenie listy sortowanych liter 332
Obliczanie wyniku dopasowania częstotliwości dla wiadomości 332
Podsumowanie 334
20.
Łamanie
szyfru Vigenere'a 335
Atak słownikowy w celu złamania szyfru Vigenere'a metodą brute force 336
Kod źródłowy programu umożliwiającego złamanie szyfru
Vigenere'a za pomocą ataku słownikowego 336
Przykładowe uruchomienie programu 337
Informacje o programie do łamania szyfru Vigenere'a za pomocą ataku
słownikowego 337
Stosowanie metody Kasiskiego do ustalenia długości klucza 338
Odszukanie powtarzających się sekwencji 338
Pobieranie dzielników liczb określających odstępy 339
Pobieranie każdej n-tej litery ciągu tekstowego 341
Stosowanie analizy częstotliwości do złamania poszczególnych
podkluczy 342
Przeprowadzanie ataku brute force na możliwe klucze 344
Kod źródłowy programu umożliwiającego złamanie szyfru
Vigenere'a 344
Przykładowe uruchomienie programu 349
Importowanie modułów i definiowanie funkcji main() 350
Wyszukiwanie powtarzających się sekwencji 351
Obliczanie dzielników odstępów 354
Usuwanie duplikatów za pomocą funkcji set() 355
Usuwanie powtarzających się dzielników i sortowanie listy 355
Wyszukiwanie najczęściej występujących dzielników 356
Określanie prawdopodobnej długości klucza 358
Metoda listy extend() 358
Rozszerzanie słownika repeatedSeqSpacings 359
Pobieranie dzielników z factorsByCount 360
Pobieranie liter szyfrowanych za pomocą tego samego podklucza 360
Próba deszyfrowania z użyciem potencjalnych długości klucza
362
Argument w postaci słowa kluczowego key funkcji print() 364
Uruchamianie programu w trybie cichym lub wyświetlania informacji
użytkownikowi 365
Wyszukiwanie możliwych kombinacji podkluczy 365
Wyświetlanie deszyfrowanego tekstu z użyciem właściwej wielkości liter
369
Zwrot deszyfrowanej wiadomości 370
Opuszczanie pętli po znalezieniu potencjalnego klucza 371
Atak brute force na wszystkie długości klucza 371
Wywoływanie funkcji main() 372
Modyfikowanie stałych programu 373
Podsumowanie 373
21.
Szyfr
z kluczem jednorazowym 375
Niemożliwy do złamania szyfr z kluczem jednorazowym 376
Tworzenie klucza o długości odpowiadającej długości wiadomości 376
Zapewnianie prawdziwej losowości klucza 378
Dlaczego klucza jednorazowego można użyć tylko raz? 379
Dlaczego dwukrotnie użyty klucz jednorazowy to szyfr Vigenere'a? 379
Podsumowanie 380
22.
Wyszukiwanie
i generowanie liczb pierwszych
381
Co to jest liczba pierwsza? 382
Kod źródłowy modułu liczb pierwszych 384
Przykładowe uruchomienie modułu 386
Sposób działania algorytmu próbnego dzielenia 386
Implementacja algorytmu próbnego dzielenia 388
Sito Eratostenesa 389
Generowanie liczb pierwszych za pomocą sita Eratostenesa 391
Algorytm pierwszości Rabina-Millera 392
Wyszukiwanie ogromnych liczb pierwszych 393
Generowanie ogromnych liczb pierwszych 395
Podsumowanie 395
23.
Generowanie
kluczy dla szyfru klucza publicznego
397
Kryptografia klucza publicznego 398
Problem z uwierzytelnieniem 400
Podpis cyfrowy 400
Uważaj na atak MITM 401
Etapy generowania kluczy publicznego i prywatnego 402
Kod źródłowy programu generującego klucze kryptografii
klucza publicznego 403
Przykładowe uruchomienie programu 404
Tworzenie funkcji main() 406
Generowanie kluczy za pomocą funkcji generateKey() 406
Obliczanie wartości e 407
Obliczanie wartości d 407
Zwracanie kluczy 408
Tworzenie plików kluczy za pomocą funkcji makeKeyFiles() 408
Wywoływanie funkcji main() 410
Hybrydowe systemy kryptograficzne 411
Podsumowanie 411
24.
Programowanie
szyfru klucza publicznego 413
Jak działa kryptografia klucza publicznego? 414
Tworzenie bloku 414
Konwersja ciągu tekstowego na blok 415
Matematyka szyfrowania i deszyfrowania za pomocą kryptografii klucza
publicznego 416
Konwersja bloku na ciąg tekstowy 418
Dlaczego nie można złamać szyfru wykorzystującego kryptografię klucza
publicznego? 420
Kod źródłowy programu wykorzystującego kryptografię klucza
publicznego 421
Przykładowe uruchomienie programu 425
Konfiguracja programu 426
Wybór trybu pracy programu 426
Konwersja ciągu tekstowego na bloki za pomocą funkcji
getBlocksFromText() 428
Funkcje min() i max() 428
Przechowywanie bloków w blockInt 429
Stosowanie funkcji getTextFromBlocks() do deszyfrowania wiadomości 431
Stosowanie metody insert() listy 432
Łączenie listy message i tworzenie na jej podstawie jednego ciągu
tekstowego 432
Tworzenie funkcji encryptMessage() 433
Tworzenie funkcji decryptMessage() 433
Odczytywanie kluczy publicznego i prywatnego z ich plików 434
Zapisywanie szyfrogramu do pliku 435
Deszyfrowanie danych z pliku 437
Wywoływanie funkcji main() 439
Podsumowanie 439
A. Debugowanie kodu Pythona 441
Na czym polega działanie debugera? 441
Usuwanie błędów z programu wykorzystującego szyfr odwrotny
443
Definiowanie punktu przerwania 445
Podsumowanie 447
B. Odpowiedzi do ćwiczeń 449
472 strony, Format: 17.0x24.0cm, oprawa miękka