Created
May 10, 2020 08:18
-
-
Save rene-d/7dc17857d96ec244528c51a34520af5e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # _ ____ _____ ______ ____ __ | |
| # / \ | _ \| ___/ ___\ \ / /\ \/ / | |
| # / _ \ | | | | |_ | | _ \ \ / / \ / | |
| # / ___ \| |_| | _|| |_| | \ V / / \ | |
| # /_/ \_\____/|_| \____| \_/ /_/\_\ | |
| # rene-d 2020 | |
| # références: | |
| # http://www.apprendre-en-ligne.net/crypto/subst/adfgvx.html | |
| # http://www.bibmath.net/crypto/index.php?action=affiche&quoi=debvingt/radiogramme | |
| from operator import itemgetter | |
| import itertools | |
| from textwrap import wrap | |
| import unicodedata | |
| from PyQt5.QtWidgets import ( | |
| QApplication, | |
| QPushButton, | |
| QHBoxLayout, | |
| QVBoxLayout, | |
| QPlainTextEdit, | |
| QDialog, | |
| ) | |
| import click | |
| import sys | |
| def nettoie_message(s): | |
| chiffres_et_lettres = ["Ll", "Lu", "Nd"] | |
| s_no_accents = "".join( | |
| (c for c in unicodedata.normalize("NFD", s) if unicodedata.category(c) in chiffres_et_lettres) | |
| ) | |
| return s_no_accents.lower() | |
| def ioc(message): | |
| n_i = [0] * 26 | |
| n = 0 | |
| for c in message.upper(): | |
| if c.isalpha(): | |
| n_i[ord(c) - 65] += 1 | |
| n += 1 | |
| r = 0 | |
| for i in range(26): | |
| r += n_i[i] * (n_i[i] - 1) | |
| r /= n * (n - 1) | |
| return r | |
| def nettoie_cle(cle): | |
| if "," in cle: | |
| cle = map(int, cle.split(",")) | |
| # réordonne la clé en commençant à 0 | |
| cle = list( | |
| map( | |
| itemgetter(0), | |
| sorted([(i, j[0]) for i, j in enumerate(sorted(enumerate(cle), key=itemgetter(1)))], key=itemgetter(1)), | |
| ) | |
| ) | |
| return cle | |
| def nettoie_cle_inverse(cle): | |
| cle = nettoie_cle(cle) | |
| inv = [0] * len(cle) | |
| for i, v in enumerate(cle): | |
| inv[v] = i | |
| return inv | |
| def crypte(message, cle="MARCEL", grille="c1ofwjymt5b4i7a28sp30qhxkeul6dvrgzn9"): | |
| cle = nettoie_cle(cle) | |
| nb_colonnes = len(cle) | |
| message = message.replace(" ", "").lower() | |
| grille = grille.lower() | |
| intermediaire = "" | |
| for c in message: | |
| pos = grille.find(c) | |
| intermediaire += "ADFGVX"[pos // 6] + "ADFGVX"[pos % 6] | |
| while len(intermediaire) % nb_colonnes != 0: | |
| intermediaire += "X" | |
| nb_lignes = len(intermediaire) // nb_colonnes | |
| # print(f"\033[2m{cle}\033[0m") | |
| # for i in range(0, len(intermediaire), nb_colonnes): | |
| # print(intermediaire[i : i + nb_colonnes]) | |
| # print("nb_colonnes", nb_colonnes) | |
| # print("nb_lignes", nb_lignes) | |
| # print("len", len(intermediaire)) | |
| colonnes = [""] * nb_colonnes | |
| for y in range(nb_lignes): | |
| for x in range(nb_colonnes): | |
| colonnes[x] += intermediaire[x + y * nb_colonnes] | |
| final = "" | |
| for x, _ in sorted(enumerate(cle), key=itemgetter(1)): | |
| final += colonnes[x] | |
| print(f"\033[2msecret\033[0m") | |
| print(" ".join(wrap(final, 5))) | |
| return final | |
| def decrypte(message, cle="MARCEL", grille="c1ofwjymt5b4i7a28sp30qhxkeul6dvrgzn9"): | |
| cle = nettoie_cle_inverse(cle) | |
| message = message.replace(" ", "") | |
| matrice_decodage = {} | |
| for pos, c in enumerate(grille): | |
| matrice_decodage["ADFGVX"[pos // 6] + "ADFGVX"[pos % 6]] = c | |
| nb_colonnes = len(cle) | |
| nb_lignes = len(message) // nb_colonnes | |
| # print("nb_colonnes", nb_colonnes) | |
| # print("nb_lignes", nb_lignes) | |
| # print("len", len(message), "=", nb_colonnes * nb_lignes) | |
| colonnes = [""] * nb_colonnes | |
| for x in range(nb_colonnes): | |
| colonnes[x] = message[x * nb_lignes : (x + 1) * nb_lignes] | |
| dest = [""] * nb_colonnes | |
| for i, k in enumerate(cle): | |
| dest[k] = colonnes[i] | |
| intermediaire = "" | |
| for y in range(nb_lignes): | |
| intermediaire += "".join(dest[x][y] for x in range(nb_colonnes)) | |
| if len(intermediaire) % 2 == 1: | |
| intermediaire = intermediaire[:-1] | |
| clair = "".join(matrice_decodage[i] for i in wrap(intermediaire, 2)) | |
| print(f"\033[2mclair\033[0m") | |
| print(clair) | |
| return clair | |
| def decrypte_test(message, cle, freq_lettres=None): | |
| cle = nettoie_cle_inverse(cle) | |
| if isinstance(freq_lettres, list): | |
| freq_lettres = "".join(freq_lettres) | |
| print(freq_lettres) | |
| message = message.replace(" ", "") | |
| decodage = {} | |
| symboles = [] | |
| for pos in range(36): | |
| symbole = "ADFGVX"[pos // 6] + "ADFGVX"[pos % 6] | |
| decodage[symbole] = pos | |
| symboles.append(symbole) | |
| message = message.replace(" ", "") | |
| nb_colonnes = len(cle) | |
| colonnes = [""] * nb_colonnes | |
| nb_lignes = len(message) // nb_colonnes | |
| # print("nb_colonnes", nb_colonnes) | |
| # print("nb_lignes", nb_lignes) | |
| # print("len", len(message), "=", nb_colonnes * nb_lignes) | |
| for x in range(nb_colonnes): | |
| colonnes[x] = message[x * nb_lignes : (x + 1) * nb_lignes] | |
| dest = [""] * nb_colonnes | |
| for i, k in enumerate(cle): | |
| dest[k] = colonnes[i] | |
| # for y in range(nb_lignes): | |
| # print("".join(dest[x][y] for x in range(nb_colonnes))) | |
| intermediaire = "" | |
| for y in range(nb_lignes): | |
| intermediaire += "".join(dest[x][y] for x in range(nb_colonnes)) | |
| if len(intermediaire) % 2 == 1: | |
| intermediaire = intermediaire[:-1] | |
| # print(" ".join(wrap(intermediaire, 2))) | |
| # print("".join(decodage[symbole] for symbole in wrap(intermediaire, 2))) | |
| freq = [0] * 36 | |
| for symbole in wrap(intermediaire, 2): | |
| pos = decodage[symbole] | |
| freq[pos] += 1 | |
| # print(intermediaire) | |
| print( | |
| "".join( | |
| f"{i/(len(intermediaire)/2)*100:.1f} " for _, i in sorted(enumerate(freq), reverse=True, key=itemgetter(1)) | |
| ) | |
| ) | |
| grille = {} | |
| for lettre, pos in enumerate(sorted(enumerate(freq), reverse=True, key=itemgetter(1))): | |
| pos = pos[0] | |
| symbole = "ADFGVX"[pos // 6] + "ADFGVX"[pos % 6] | |
| print(" " + symbole + " ", end="") | |
| grille[symbole] = freq_lettres[lettre] | |
| print() | |
| print("".join(f"{c:>3} " for c in freq_lettres)) | |
| texte_grille = "".join(grille[l + c] for l, c in itertools.product("ADFGVX", "ADFGVX")) | |
| print("grille:", texte_grille) | |
| clair = "".join(grille[symbole] for symbole in wrap(intermediaire, 2)) | |
| print(clair) | |
| return clair | |
| ############################################################################## | |
| @click.group() | |
| def cli(): | |
| pass | |
| @cli.command() | |
| def test1(): | |
| cle = "MARCEL" | |
| secret = crypte("objectif Arras 15h28", cle) | |
| clair = decrypte(secret, cle) | |
| assert secret == "FDADX VVAGD DGADF FDFXF FFGVA VFXFG DAAXA F".replace(" ", "") | |
| assert clair == "objectif Arras 15h28".replace(" ", "").lower() | |
| @cli.command() | |
| def test2(): | |
| # Der „Funkspruch des Sieges“ | |
| message = "Munitionierung beschleunigen Punkt soweit nicht eingesehen auch bei Tag" | |
| cle = nettoie_cle_inverse("12, 6, 18, 15, 4, 1, 3, 16, 10, 8, 19, 14, 11, 7, 9, 2, 5, 21, 17, 20, 13") | |
| grille = "c08xf4mk3az9nw1ojd5siyhuplvb6req7t2g" | |
| message_code = "FGAXA XAXFF FAFVA AVDFA GAXFX FAFAG DXGGX AGXFD XGAGX GAXGX AGXVF VXXAG XDDAX GGAAF DGGAF FXGGX XDFAX GXAXV AGXGG DFAGG GXVAX VFXGV FFGGA XDGAX FDVGG A" | |
| secret = crypte(message, cle, grille) | |
| clair = decrypte(message_code, cle, grille) | |
| assert secret == message_code.replace(" ", "") | |
| assert clair == message.replace(" ", "").lower() | |
| class App(QDialog): | |
| def __init__(self, message, cle, freq, parent=None): | |
| super(App, self).__init__(parent) | |
| layout = QHBoxLayout() | |
| self.message = message | |
| self.cle = cle | |
| self.freq = list(freq) | |
| self.swap = None | |
| self.b = [] | |
| for i, c in enumerate(self.freq): | |
| b = QPushButton(c, self) | |
| b.setFixedWidth(16) | |
| b.setCheckable(True) | |
| b.setFlat(True) | |
| b.clicked.connect(self.make_callback(i)) | |
| layout.addWidget(b) | |
| self.b.append(b) | |
| zz = QVBoxLayout() | |
| zz.addLayout(layout) | |
| self.text = QPlainTextEdit(self) | |
| self.text.setFixedHeight(256) | |
| zz.addWidget(self.text) | |
| self.setLayout(zz) | |
| self.setWindowTitle("Button demo") | |
| self.decrypte() | |
| def make_callback(self, i): | |
| return lambda: self.on_click(i) | |
| def on_click(self, id): | |
| if self.swap is None: | |
| self.swap = id | |
| else: | |
| self.freq[id], self.freq[self.swap] = self.freq[self.swap], self.freq[id] | |
| self.b[self.swap].setChecked(False) | |
| self.b[self.swap].setText(self.freq[self.swap]) | |
| self.b[id].setChecked(False) | |
| self.b[id].setText(self.freq[id]) | |
| self.setWindowTitle(f"swap {self.swap} et {id}") | |
| self.swap = None | |
| self.decrypte() | |
| def decrypte(self): | |
| clair = decrypte_test(self.message, self.cle, self.freq) | |
| self.text.setPlainText(clair) | |
| if __name__ == "__main__": | |
| cli() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment