Last active
January 23, 2021 08:03
-
-
Save NaoY-2501/4534e6864a9e168e6928504ddc2c5287 to your computer and use it in GitHub Desktop.
『Pythonでいかにして暗号を破るか』1章 練習問題 回答
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
| # 『Pythonでいかにして暗号を破るか』1章 練習問題 | |
| from typing import Any | |
| PLAIN_ALPHABET = [chr(i) for i in range(65, 91)] | |
| NUM_TO_ALPHABET = {idx: char for idx, char in enumerate(PLAIN_ALPHABET)} | |
| ALPHABET_TO_NUM = {char: idx for idx, char in enumerate(PLAIN_ALPHABET)} | |
| def encode_caeser(key: int, plain: str) -> str: | |
| plain_nums = [] | |
| not_encodes = [] | |
| for idx, s in enumerate(plain): | |
| s = s.upper() | |
| if s in PLAIN_ALPHABET: | |
| plain_nums.append(ALPHABET_TO_NUM[s]) | |
| else: | |
| # アルファベット以外の文字(記号など)は平文でのインデックスと文字を保持しておく | |
| not_encodes.append((idx, s)) | |
| encoded_nums = [] | |
| for num in plain_nums: | |
| if num + key < 26: | |
| encoded_nums.append(num + key) | |
| else: | |
| # 番号と鍵の和が26以上の場合、(番号+鍵)-26 する | |
| encoded_nums.append((num + key) - 26) | |
| encoded_sentence = [NUM_TO_ALPHABET[num] for num in encoded_nums] | |
| for not_encoded in not_encodes: | |
| # アルファベット以外の文字(記号など)を平文と同じインデックスに戻す | |
| idx, s = not_encoded | |
| encoded_sentence.insert(idx, s) | |
| return ''.join(encoded_sentence) | |
| def decode_caesar(key: int, cipher: str) -> str: | |
| cipher_nums = [] | |
| not_decodes = [] | |
| for idx, s in enumerate(cipher): | |
| s = s.upper() | |
| if s in PLAIN_ALPHABET: | |
| cipher_nums.append(ALPHABET_TO_NUM[s]) | |
| else: | |
| # アルファベット以外の文字(記号など)は平文でのインデックスと文字を保持しておく | |
| not_decodes.append((idx, s)) | |
| decoded_nums = [] | |
| for num in cipher_nums: | |
| if num - key >= 0: | |
| decoded_nums.append(num - key) | |
| else: | |
| # 番号と鍵の差がゼロ未満の場合、 (番号-鍵)+26 する | |
| decoded_nums.append((num - key) + 26) | |
| decoded_sentence = [NUM_TO_ALPHABET[num] for num in decoded_nums] | |
| for not_decode in not_decodes: | |
| # アルファベット以外の文字(記号など)を平文と同じインデックスに戻す | |
| idx, s = not_decode | |
| decoded_sentence.insert(idx, s) | |
| return ''.join(decoded_sentence) | |
| def guess_key(plain: str, cipher: str) -> Any: | |
| plain_nums = [] | |
| for s in plain: | |
| plain_nums.append(ALPHABET_TO_NUM[s.upper()]) | |
| cipher_nums = [] | |
| for s in cipher: | |
| cipher_nums.append(ALPHABET_TO_NUM[s.upper()]) | |
| # 平文のアルファベットの番号と暗号文のアルファベットの番号の差分を求める(鍵の推測) | |
| diffs = [p-c for p, c in zip(plain_nums, cipher_nums)] | |
| keys = [] | |
| for diff, plain, cipher in zip(diffs, plain_nums, cipher_nums): | |
| if diff < 0: | |
| # 差分がゼロ未満の場合、(平文のアルファベットの番号+26) - 暗号文のアルファベットの番号 にする | |
| keys.append((plain + 26) - cipher) | |
| else: | |
| keys.append(diff) | |
| # 推測した鍵の配列がすべて同じ値であるか確認する | |
| keys_validate = [keys[i] == keys[i+1] for i in range(0, len(keys)-1)] | |
| if all(keys_validate): | |
| return keys[0] | |
| return None | |
| # 1-1 | |
| plain = 'AMBIDEXTROUS: Able to pick with equal skill a right-hand pocket or a left.' | |
| key = 4 | |
| cipher = encode_caeser(key, plain) | |
| print(f'{"-"*5} 1-1 {"-"*5}') | |
| print(plain) | |
| print(cipher) | |
| # 1-2 | |
| plain = 'GUILLOTINE: A machine which makes a Frenchman shrug his shoulders with good reasons.' | |
| key = 17 | |
| cipher = encode_caeser(key, plain) | |
| print(f'{"-"*5} 1-2 {"-"*5}') | |
| print(plain) | |
| print(cipher) | |
| # 1-3 | |
| plain = 'IMPIETY: Your irreverence toward to my deity.' | |
| key = 21 | |
| cipher = encode_caeser(key, plain) | |
| print(f'{"-"*5} 1-3 {"-"*5}') | |
| print(plain) | |
| print(cipher) | |
| # 2-1 | |
| cipher = 'ZXAI: P RDHIJBT HDBTIXBTH LDGC QN HRDIRWBTC XC PBTGXRP PCS PBTGXRPCH XC HRDIAPCS.' | |
| key = 15 | |
| plain = decode_caesar(key, cipher) | |
| print(f'{"-"*5} 2-1 {"-"*5}') | |
| print(cipher) | |
| print(plain) | |
| # 2-2 | |
| cipher = 'MQTSWXSV: E VMZEP EWTMVERX XS TYFPMG LSRSVW.' | |
| key = 4 | |
| plain = decode_caesar(key, cipher) | |
| print(f'{"-"*5} 2-2 {"-"*5}') | |
| print(cipher) | |
| print(plain) | |
| # 3-1 | |
| plain = 'This is silly example' | |
| key = 0 | |
| cipher = encode_caeser(key, plain) | |
| print(f'{"-"*5} 3-1 {"-"*5}') | |
| print(plain) | |
| print(cipher) | |
| # 4-1 | |
| plain = 'ROSEBUD' | |
| cipher = 'LIMYVOX' | |
| key = guess_key(plain, cipher) | |
| print(f'{"-"*5} 4-1 {"-"*5}') | |
| print(f'Plain: {plain} Cipher: {cipher}') | |
| print(f'key: {key}') | |
| print(decode_caesar(key, plain)) | |
| print(encode_caeser(key, cipher)) | |
| # 4-2 | |
| plain = 'YAMAMOTO' | |
| cipher = 'PRDRDFKF' | |
| key = guess_key(plain, cipher) | |
| print(f'{"-"*5} 4-2 {"-"*5}') | |
| print(f'Plain: {plain} Cipher: {cipher}') | |
| print(f'key: {key}') | |
| print(decode_caesar(key, plain)) | |
| print(encode_caeser(key, cipher)) | |
| # 4-3 | |
| plain = 'ASTRONOMY' | |
| cipher = 'HZAYVUVTF' | |
| key = guess_key(plain, cipher) | |
| print(f'{"-"*5} 4-3 {"-"*5}') | |
| print(f'Plain: {plain} Cipher: {cipher}') | |
| print(f'key: {key}') | |
| print(decode_caesar(key, plain)) | |
| print(encode_caeser(key, cipher)) | |
| # 5-1 | |
| cipher = 'UMMSVMAA: Cvkwuuwv xibqmvkm qv xtivvqvo i zmdmvom bpib qa ewzbp epqtm.' | |
| key = 8 | |
| plain = decode_caesar(key, cipher) | |
| print(f'{"-"*5} 5-1 (correct) {"-"*5}') | |
| print(cipher) | |
| print(plain) | |
| key = 9 | |
| plain = decode_caesar(key, cipher) | |
| print(f'{"-"*5} 5-1 (incorrect) {"-"*5}') | |
| print(cipher) | |
| print(plain) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment