How to Play an Encrypted Video File Directly with openssl and mpv
openssl
と mpv
を使用して暗号化された動画ファイルを直接再生する方法
やむを得ない事情により, 復号したファイルを保存できない場合があるかもしれない.
openssl
と mpv
を使用することで, 暗号化された動画を直接再生することができる.
Example
Assume that your file vvv.enc
is encrypted with AES256-CBC with salt, and the passphrase is wholesome
.
You can decrypt it and play the movie with the following command:
$ openssl enc -d -aes-256-cbc -salt -pass pass:wholesome -in vvv.enc | mpv --force-seekable -
Linux Kernel の黒魔術マクロ: __is_constexpr(x)
__is_constexpr(x)
は Linux kernel 4.17 で導入されたマクロで、与えられた式が定数式であるかを 式を評価せずに 判定する。
/* * This returns a constant expression while determining if an argument is * a constant expression, most importantly without evaluating the argument. * Glory to Martin Uecker <Martin.Uecker@med.uni-goettingen.de> */ #define __is_constexpr(x) \ (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8)))
kernel/git/torvalds/linux.git - Linux kernel source tree
Linus さんも「それはアイデアとは言わねえ。天才かマジで病んだ頭脳のどっちかだわ」(適当訳) とべた褒めしている。
LKML: Linus Torvalds: Re: detecting integer constant expressions in macros
サンプルコード:
#include <stdio.h> #define __is_constexpr(x) \ (sizeof(int) == sizeof(*(8 ? ((void *)((long)(x) * 0l)) : (int *)8))) #define sq(x) ((x) * (x)) int main(void) { int a = 2; if (__is_constexpr(sq(2))) puts("Yes"); else puts("No"); if (__is_constexpr(sq(a))) puts("Yes"); else puts("No"); return 0; }
実行結果:
Yes No
はい天才。
解説は暇があったら書くが、既にある:
漢字変換を雑に実装してみる
一発変換と変換候補を出すだけ。 たぶん一番基本的なやつ。
真面目に日本語入力を実現するには形態素解析とかいろいろしなきゃいけないので自分には MURI。 ライブラリもあるけど興味もそこまでない。
Mozc 更新して。
コメント書けないのはアレだけど、Python だと JSON を標準ライブラリでサポートしてるので便利。
Wikipedia の Trie の記事を参考にした: https://en.wikipedia.org/wiki/Trie
ソースコード
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Copyright (C) 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from collections import deque class Node(object): def __init__(self): self.values = [] self.children = {} class Trie(object): def __init__(self, vocabulary=None): self.__root = Node() if vocabulary is not None: for key, values in vocabulary.items(): self.insert(key, values) def insert(self, key, values): node = self.__root i = 0 while i < len(key): if key[i] in node.children: node = node.children[key[i]] i += 1 else: break while i < len(key): node.children[key[i]] = Node() node = node.children[key[i]] i += 1 node.values.extend(values) def query(self, key): node = self.__root for char in key: if char in node.children: node = node.children[char] else: return list() return node.values def suggest(self, key, n=10): suggestions = [] node = self.__root for char in key: if char in node.children: node = node.children[char] else: break if node is not self.__root: suggestions.extend(node.values) que = deque() que.append(node.children) while len(que) > 0: for child in que.popleft().values(): que.append(child.children) suggestions.extend(child.values) if len(suggestions) > n: return suggestions return suggestions
vocabulary.json
{ "い": ["胃", "異", "煎"], "いい": ["良い", "謂", "易々"], "いいん": ["委員", "医院"], "いいんちょう": ["委員長"] }
使用例
$ python3 Python 3.5.3 (default, Jan 19 2017, 14:11:04) [GCC 6.3.0 20170118] on linux Type "help", "copyright", "credits" or "license" for more information. >>> from trie import Trie >>> import json >>> with open('vocabulary.json', 'r') as f: ... vocabulary = json.load(f) ... >>> trie = Trie(vocabulary) >>> trie.query('いいんちょう') ['委員長'] >>> trie.query('い') ['胃', '異', '煎'] >>> trie.query('がっきゅういいん') [] >>> trie.suggest('いい') ['良い', '謂', '易々', '委員', '医院', '委員長'] >>>
Uniqfuck: Brainfuck Translator
Uniqfuck
Brainfuck プログラムと Brainfuck 派生言語プログラムを相互変換するプログラムを作った。 名前は Uniqfuck とした。 ソースコードは記事の最後に載せる。 ライセンスは MIT License とする。
使い方
Uniqfuck の使い方を簡単に説明する。 Python が読める方はソースを読んだほうがはやい。
必須入力ファイルはソースファイルと文法定義ファイルである。 文法定義は JSON で記述する。
例: Mukadefuck
使い方の例として新たな Brainfuck 派生言語を作った。 言語名は Mukadefuck とした。
文法定義: mukade.json
{ "commands": { ">": "起立! 気を付け!", "<": "これがバーチャルYouTuberなんだよなあ……。", "+": "よいしょ。", "-": "わたくしで隠さなきゃ……!", ",": "クソ雑魚パンチやめてくださいwww", ".": "僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです!", "[": "ぜろ……。ぜろ……。ぜろ……。", "]": "おるやんけ! おるやんけ! どなたかおるやんけ!" }, "special_characters": { "delimiter": " ", "r_delimiter": "" } }
Brainfuck -> Mukadefuck
"Hello, world!" を出力する Brainfuck プログラムを Mukadefuck に変換してみる。 ソースコードはニコニコ大百科から引用した。
helloworld.brainfuck
+++++++++[>++++++++>+++++++++++>+++++<<<-]>.>++.+++++++..+ ++.>-.------------.<++++++++.--------.+++.------.--------.>+.
出典: http://dic.nicovideo.jp/a/brainfuck
次のコマンドを実行すると
$ ./uniqfuck.py -o helloworld.mukadefuck mukade.json helloworld.brainfuck
次のファイルが得られる。
helloworld.mukadefuck
よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 ぜろ……。ぜろ……。ぜろ……。 起立! 気を付け! よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 起立! 気を付け! よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 起立! 気を付け! よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 これがバーチャルYouTuberなんだよなあ……。 これがバーチャルYouTuberなんだよなあ……。 これがバーチャルYouTuberなんだよなあ……。 わたくしで隠さなきゃ……! おるやんけ! おるやんけ! どなたかおるやんけ! 起立! 気を付け! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! 起立! 気を付け! よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! よいしょ。 よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! 起立! 気を付け! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! これがバーチャルYouTuberなんだよなあ……。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! よいしょ。 よいしょ。 よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! わたくしで隠さなきゃ……! 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです! 起立! 気を付け! よいしょ。 僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです!
Mukadefuck -> Brainfuck
'X' を 8 個出力する Mukadefuck プログラムを Brainfuck に変換してみる。
x8.mukadefuck
# 'X' を 8 個出力するプログラムを作りましょう。 # ASCII コードの 'X' に対応する数値 88 を 3 番地に用意します。 # まずカウントダウン用に 0 番地に 8 を用意します。 「よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。」 # ここから 0 番地の値が 0 になるまでループします。 「ぜろ……。ぜろ……。ぜろ……。」 # 2 番地に移動します。 「起立! 気を付け! 起立! 気を付け!」 # カウントダウン用 2 番地に 11 を用意します (8 * 11 = 88)。 「よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。よいしょ。」 # 2 番地が 0 になるまでループします。 「ぜろ……。ぜろ……。ぜろ……。」 # 3 番地に移動し、1 加えます。 # ここに 88 が用意されます。 「起立! 気を付け! よいしょ。」 # 2 番地に移動し、1 引きます。 「これがバーチャルYouTuberなんだよなあ……。わたくしで隠さなきゃ……!」 # 2 番地が 0 であれば、次に進みます。 「おるやんけ! おるやんけ! どなたかおるやんけ!」 # 1 番地に移動し 1 加えます。 # なぜかというと、'X' を 8 回表示するために、 # カウントダウン用の 8 を用意する必要があるからです # (「僕はね音楽なんて(略)」を 8 回書くと大変)。 # ここのループは 8 回繰り返されるので 8 が得られます。 「これがバーチャルYouTuberなんだよなあ……。よいしょ。」 # 0 番地に移動して 1 引きます。 「これがバーチャルYouTuberなんだよなあ……。わたくしで隠さなきゃ……!」 # 0 番地が 0 であれば、次に進みます。 「おるやんけ! おるやんけ! どなたかおるやんけ!」 # 1 番地に移動します (1 番地の値は 8 です)。 「起立! 気を付け!」 # ここから 1 番地の値が 0 になるまでループします。 「ぜろ……。ぜろ……。ぜろ……。」 # 3 番地まで移動します。 「起立! 気を付け!起立! 気を付け!」 # 3 番地の値 88、つまり 'X' を出力します。 「僕はね音楽なんてどうでもよくて君のことが好きなんやけどでもあのそのだから楽器を握るんじゃなくて君の手を握りたいけどだけれどもだけれでも僕はもうこうやって音楽を奏でて君に言葉を伝えるその術しか持ってないから僕は君の為に歌うもぼ僕のために歌いたいんです!」 # 1 番地に移動して、1 引きます。 「これがバーチャルYouTuberなんだよなあ……。これがバーチャルYouTuberなんだよなあ……。わたくしで隠さなきゃ……!」 # 1 番地が 0 であればプログラムは終了します。 「おるやんけ! おるやんけ! どなたかおるやんけ!」 # これでプログラムは終了です。楽しんでいただけましたか? # 気を付け! 着席! # 以上、Mukadefuck のプログラムの例をお送りしました。
'-r' オプションを付けて実行すると
$ ./uniqfuck.py -r -o x8.brainfuck mukade.json x8.mukadefuck
次のファイルが得られる。
x8.brainfuck
++++++++[>>+++++++++++[>+<-]<+<-]>[>>.<<-]
ソースコード
#!/usr/bin/env python3 # -*- coding: utf-8 -*- """ Copyright (C) 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ import sys import argparse import json def is_uniquely_decodable(codewords): def get_residual_words(prefixes, words): residual_words = set() if len(prefixes) == 0: return words for prefix in prefixes: for word in words: if word.find(prefix) == 0: residual_words.add(word[len(prefix):]) return residual_words s = [set()] for word in get_residual_words(codewords, codewords): s[0].add(word) s[0].remove('') i = 0 while True: if len(s[i]) == 0: return True for word in s[i]: if word == '' or word in codewords: return False for t in s[:i]: if t == s[i]: return True s.append(set()) for word in get_residual_words(codewords, s[i]): s[i + 1].add(word) for word in get_residual_words(s[i], codewords): s[i + 1].add(word) i += 1 def main(): parser = argparse.ArgumentParser(description='Uniqfuck: Meta(?) Brainfuck') parser.add_argument('-r', '--reverse', action='store_true', help='convert source code to Brainfuck code') parser.add_argument('-s', '--stdout', action='store_true', help='set output to stdout') parser.add_argument('-i', '--stdin', action='store_true', help='read source code from stdin') parser.add_argument('-o', '--output', nargs='?', default='a.out.txt', help='set output file name') parser.add_argument('grammar', metavar='grammar_file', nargs='?', help='grammar definition file') parser.add_argument('source', metavar='src_file', nargs='?', help='source file') args = parser.parse_args() reverse = args.reverse stdout = args.stdout stdin = args.stdin output = args.output if args.grammar is None or stdin == False and args.source is None: print('must specify files') sys.exit() grammar = args.grammar source = args.source commands = None special_characters = None with open(grammar, 'r') as f: grammar_dict = json.load(f) commands = grammar_dict['commands'] special_characters = grammar_dict['special_characters'] if is_uniquely_decodable(set(v for v in commands.values())) == False: print('not uniquely decodable') sys.exit() if reverse == True: commands = dict(((v, k) for k, v in commands.items())) source_code = None if stdin is True: source_code = ''.join(sys.stdin.readlines()) else: with open(source, 'r') as f: source_code = ''.join(f.readlines()) compiled_code = None if reverse == True: commands_lengths = sorted([len(k) for k in commands.keys()], reverse=True) compiled_commands = [] i = 0 while i + commands_lengths[-1] < len(source_code): for length in commands_lengths: if i + length > len(source_code): continue command = source_code[i:i + length] if command in commands: i += length compiled_commands.append(commands[command]) break else: i += 1 compiled_code = special_characters['r_delimiter'].join(compiled_commands) else: compiled_code = special_characters['delimiter'].join(commands[command] for command in source_code if command in commands) if stdout == True: print(compiled_code) else: with open(output, 'w') as f: f.write(''.join([compiled_code, '\n'])) if __name__ == '__main__': main()
ExcludeNodes の設定を半自動化
追記
ExcludeNodes, ExcludeExitNodes の設定は参考サイトから丸パクリして載せていましたが, 消したようなので消しました.
このプログラムは, BadExit の付いたノードが一つも存在しない場合の処理が抜けています.
気が向いたら直しておきます.直しました.
ここから記事スタート
最近 Tails を使っている。 ExcludeNodes に BadExit フラグが付いたノードを追加するのが面倒だったので、半自動化した。 cURL と Python を使用した。
現在のノードのリストはここで確認できる (JSON):
https://onionoo.torproject.org/details
cURL で JSON を取得し、BadExit が付いたノードの fingerprints をすべて torrc に突っ込む。
これを実行すると torrc にそのままコピペできる出力が得られる:
#!/bin/sh URL='https://onionoo.torproject.org/summary?flag=badexit' echo 'ExcludeNodes SlowServer'|tr -d '\n' curl -s ${URL}|python3 -c "import sys;import json;a=['$'+r['f']for r in json.loads(sys.stdin.read())['relays']];print(len(a)and',{}'.format(','.join(a))or'')" echo 'StrictNodes 1'
そのまま使ってもよいが, 各自調整したほうがよい.
参考ウェブサイト
Enigma Machine Emulator in Python
For now, I am not going to translate this article into English because there are many web pages that explain the details about the Enigma machines in English.
The source code below might be uploaded to GitHub.
はじめに
第二次世界大戦中, ナチスドイツ軍が使用していた Enigma 暗号機のエミュレータを Python で書いた (書き直した).
以前のやつは継承とか使っていたが, バラバラにした.
Bombeを書こうと思い始めたので, リファクタリングになっているかわからないがリファクタした.
記事の最後にソースコードを置いてある.
オリジナルのリフレクタとロータの追加機能はまだテストしていない.
オリジナルのリフレクタとロータを動かせるようになった(たぶん).
Enigma に関する英語記事はたくさんあるが, 日本語ではほとんどない. ここで軽い解説を書こうと思う.
後で.
動作
この記事の最後にあるコードを実行すると次の結果が得られる.
test.py
動作確認.
$ python ./test.py Enigma machine emulator version 1.4.8 Registered reflectors: { "A": "EJMZALYXVBWFCRQUONTSPIKHGD", "B": "YRUHQSLDPXNGOKMIEBFZCWVJAT", "C": "FVPJIAOYEDRZXWGCTKUQSBNMHL", "B Thin": "ENKQAUYWJICOPBLMDXZVFTHRGS", "C Thin": "RDOBJNTKVEHMLFCWZAXGYIPSUQ" } Registered rotors: { "I": "EKMFLGDQVZNTOWYHXUSPAIBRCJ", "II": "AJDKSIRUXBLHWTMCQGZNPYFVOE", "III": "BDFHJLCPRTXVZNYEIWGAKMUSQO", "IV": "ESOVPZJAYQUIRHXLNFTGKDCMWB", "V": "VZBRGITYUPSDNHLXAWMJQOFECK", "VI": "JPGVOUMFYQBENHZRDKASXLICTW", "VII": "NZJHGRCXMYSWBOUFAIVLPEKQDT", "VIII": "FKQHTLXOCBJSPDZRAMEWNIUYGV", "Beta": "LEYJVCNIXWPBQMDRTAKZGFUHOS", "Gamma": "FSOKANUERHMBTIYCWLQPZXVGJD" } Registered plugboards: { "Plugs": "KHYDVGFBQRALPNSMIJOTZEWXCU" } wirings: { "reflector": { "name": "C Thin", "wiring": "RDOBJNTKVEHMLFCWZAXGYIPSUQ", "pairs": { "A": "R", "B": "D", "C": "O", "E": "J", "F": "N", "G": "T", "H": "K", "I": "V", "L": "M", "P": "W", "Q": "Z", "S": "X", "U": "Y" } }, "rotors": [ { "name": "Gamma", "wiring": "FSOKANUERHMBTIYCWLQPZXVGJD", "turnover_position": [], "initial_position": "T", "ring_position": "M" }, { "name": "III", "wiring": "BDFHJLCPRTXVZNYEIWGAKMUSQO", "turnover_position": [ "V" ], "initial_position": "P", "ring_position": "S" }, { "name": "VIII", "wiring": "FKQHTLXOCBJSPDZRAMEWNIUYGV", "turnover_position": [ "Z", "M" ], "initial_position": "X", "ring_position": "F" }, { "name": "II", "wiring": "AJDKSIRUXBLHWTMCQGZNPYFVOE", "turnover_position": [ "E" ], "initial_position": "J", "ring_position": "Z" } ], "plugboard": { "name": "Plugs", "wiring": "KHYDVGFBQRALPNSMIJOTZEWXCU", "pairs": { "A": "K", "B": "H", "C": "Y", "E": "V", "F": "G", "I": "Q", "J": "R", "M": "P", "O": "S", "U": "Z" } }, "keeps_initial_state": false } initial positions of the rotors: [ "T", "P", "X", "J" ] plain: I really don't know why it works. formatted: IREALLYDONTKNOWWHYITWORKSX encoded: NHTFFMGYHDEMIQUTGXQSMKBDHG decoded: IREALLYDONTKNOWWHYITWORKSX current positions of the rotors: [ "T", "P", "Y", "J" ]
original.py
オリジナルの設定ができる. 今回は3ロータで配線はランダムにしたので実行毎に結果が変わる.
$ ./original.py 3 wirings: { "reflector": { "name": "original reflector", "wiring": "PDTBJRNXOEYVWGIAUFZCQLMHKS", "pairs": { "E": "J", "K": "Y", "C": "T", "Q": "U", "M": "W", "S": "Z", "A": "P", "F": "R", "L": "V", "G": "N", "H": "X", "I": "O", "B": "D" } }, "rotors": [ { "name": "original rotor 1", "wiring": "IXROUDJVGEHKATFPZMBQSNWLCY", "turnover_position": [ "A", "X" ], "initial_position": "A", "ring_position": "E" }, { "name": "original rotor 2", "wiring": "DQPWVZOYCBTHIMJSUFAERKNGLX", "turnover_position": [ "D", "W" ], "initial_position": "Q", "ring_position": "J" }, { "name": "original rotor 3", "wiring": "HRCNFKAPYTQJSZUOXMGVDWBEIL", "turnover_position": [ "Z", "A" ], "initial_position": "U", "ring_position": "T" } ], "plugboard": { "name": "original plugboard", "wiring": "DYGAJRCIHETVWSXQPFNKZLMOBU", "pairs": { "F": "R", "P": "Q", "C": "G", "K": "T", "B": "Y", "A": "D", "E": "J", "O": "X", "M": "W", "U": "Z", "H": "I", "L": "V", "N": "S" } }, "keeps_initial_state": true } initial positions of the rotors: [ "A", "U", "K" ] plain: Ich weiss wirklich nicht, warum sie funktioniert. formatted: ICHWEISSWIRKLICHNICHTWARUMSIEFUNKTIONIERTX encoded: QNAIBHJYPTWZNUEPUYYIOKFUKTBYAUBIZMCNJMDDKV decoded: ICHWEISSWIRKLICHNICHTWARUMSIEFUNKTIONIERTX
Source code
enigma/__init__.py
# -*- coding: utf-8 -*- """ Enigma machine emulator Copyright (C) 2017, 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from . import component from . import emulator PROG_NAME = 'Enigma machine emulator' VERSION = '1.4.8' AUTHOR = 'Vanaestea' LICENSE = 'MIT'
enigma/component.py
# -*- coding: utf-8 -*- """ Enigma machine emulator Copyright (C) 2017, 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from abc import ABC, abstractmethod from collections import Counter from operator import itemgetter import string _MOD_BASE = len(string.ascii_uppercase) class BaseComponent(ABC): _shifts_in = {} _shifts_out = {} _wirings = {} def __init__(self): self._name = None self._wiring = None self._shift_in = None self._shift_out = None @abstractmethod def _set_shifts(self): pass @abstractmethod def set(self): pass @abstractmethod def add(self): pass @abstractmethod def encode(self): pass @abstractmethod def decode(self): pass @property def wiring(self): return self._wiring @property def name(self): return self._name @classmethod def get_list(cls): return cls._wirings class Rotor(BaseComponent): _wirings = { 'I': 'EKMFLGDQVZNTOWYHXUSPAIBRCJ', 'II': 'AJDKSIRUXBLHWTMCQGZNPYFVOE', 'III': 'BDFHJLCPRTXVZNYEIWGAKMUSQO', 'IV': 'ESOVPZJAYQUIRHXLNFTGKDCMWB', 'V': 'VZBRGITYUPSDNHLXAWMJQOFECK', 'VI': 'JPGVOUMFYQBENHZRDKASXLICTW', 'VII': 'NZJHGRCXMYSWBOUFAIVLPEKQDT', 'VIII': 'FKQHTLXOCBJSPDZRAMEWNIUYGV', 'Beta': 'LEYJVCNIXWPBQMDRTAKZGFUHOS', 'Gamma': 'FSOKANUERHMBTIYCWLQPZXVGJD', } # A turnover position is one character ahead of the real one _turnovers = { 'I': (17,), # R 'II': (5,), # E 'III': (22,), # V 'IV': (10,), # J 'V': (0,), # Z 'VI': (0, 13), # Z+M 'VII': (0, 13), # Z+M 'VIII': (0, 13), # Z+M 'Beta': (), # nothing 'Gamma': (), # nothing } def __init__(self): super().__init__() self._turnover = None self._initial_shift = 0 self._shift = 0 self._ring = 0 self._fixed = False def _set_shifts(self, name): self._shifts_in[name] = tuple(ord(ch) - ord('A') for ch in self._wirings[self._name]) self._shifts_out[name] = tuple(i for i, _ in sorted(enumerate(self._shifts_in[name]), key=itemgetter(1))) def set_with_shift(self, name, initial_shift, ring): if name not in self._wirings or\ initial_shift < 0 or _MOD_BASE <= initial_shift or\ ring < 0 or _MOD_BASE <= ring: return False self._name = name if name not in self._shifts_in: self._set_shifts(name) self._shift_in = self._shifts_in[name] self._shift_out = self._shifts_out[name] self._wiring = self._wirings[name] self._turnover = self._turnovers[name] self._ring = ring self._shift = self._initial_shift = initial_shift self._fixed = True if len(self._turnover) == 0 else False return True def set(self, name, initial_position, ring_position): return self.set_with_shift(name, ord(initial_position) - ord('A'), ord(ring_position) - ord('A')) def add_with_shift(self, name, wiring, turnover): if name not in self._wirings and\ Counter(wiring) == Counter(string.ascii_uppercase): for idx in turnover: if idx < 0 or _MOD_BASE <= idx: break else: self._wirings[name] = wiring self._turnovers[name] = tuple(sorted((i + 1) % _MOD_BASE for i in turnover)) return True return False def add(self, name, wiring, turnover_position): return self.add_with_shift(name, wiring, tuple(ord(pos) - ord('A') for pos in turnover_position)) def encode(self, idx, shift, ring=0): return self._shift_in[(idx + self._shift - self._ring - shift + ring) % _MOD_BASE] def decode(self, idx, shift, ring=0): return self._shift_out[(idx + self._shift - self._ring - shift + ring) % _MOD_BASE] def reset(self): self._shift = self._initial_shift def step(self): if not self._fixed: self._shift = (self._shift + 1) % _MOD_BASE def on_turnover(self, next_to_fast=False): for idx in self._turnover: if next_to_fast: if (self._shift + 1) % _MOD_BASE == idx: break elif self._shift == idx: break else: return False return True @property def initial_shift(self): return self._initial_shift @initial_shift.setter def initial_shift(self, initial_shift): if 0 <= initial_shift < _MOD_BASE: self._initial_shift = initial_shift else: raise ValueError() @property def initial_position(self): return chr(self.initial_shift + ord('A')) @initial_position.setter def initial_position(self, initial_position): self.initial_shift = ord(initial_position) - ord('A') @property def shift(self): return self._shift @shift.setter def shift(self, shift): if 0 <= shift < _MOD_BASE: self._shift = shift else: raise ValueError() @property def position(self): return chr(self.shift + ord('A')) @position.setter def position(self, position): self.shift = ord(position) - ord('A') @property def ring(self): return self._ring @ring.setter def ring(self, ring): if 0 <= ring < _MOD_BASE: self._ring = ring else: raise ValueError() @property def ring_position(self): return chr(self.ring + ord('A')) @ring_position.setter def ring_position(self, ring_position): self.ring = ord(ring_position) - ord('A') # return the real turnover shift @property def turnover(self): return (self._turnover + _MOD_BASE - 1) % _MOD_BASE # return the real turnover position @property def turnover_position(self): return tuple(chr(ord('A') + (idx + _MOD_BASE - 1) % _MOD_BASE) for idx in self._turnover) @property def is_fixed(self): return self._fixed class PairwiseComponent(BaseComponent): _wirings_pairs = {} def __init__(self): super().__init__() self._pairs = None def _is_pairwise(self, pairs): s = set() t = set(string.ascii_uppercase) for k, v in pairs.items(): if k in s or k not in t: break s.add(k) if v in s or v not in t: break s.add(v) else: return True return False def _set_pairs(self, name): shifts = self._shifts_in[name] pairs = {} s = set() for i, e in enumerate(shifts): if i == e or i in s or e in s: continue pairs[chr(i + ord('A'))] = chr(e + ord('A')) s.add(i) s.add(e) self._wirings_pairs[name] = pairs def set(self, name): if name not in self._wirings: return False self._name = name self._wiring = self._wirings[name] if name not in self._shifts_in: self._set_shifts(name) self._shift_in = self._shifts_in[name] self._shift_out = self._shifts_out[name] if name not in self._wirings_pairs: self._set_pairs(name) self._pairs = self._wirings_pairs[name] return True def add(self, name, pairs): if name in self._wirings or not self._is_pairwise(pairs): return False self._wirings_pairs[name] = dict((k, v) if k < v else (v, k) for k, v in tuple(pairs.items())) a = list(string.ascii_uppercase) for k, v in self._wirings_pairs[name].items(): i = ord(k) - ord('A') j = ord(v) - ord('A') a[i], a[j] = a[j], a[i] self._wirings[name] = ''.join(a) return True @property def pairs(self): return self._pairs @classmethod def get_pairs_list(cls): return cls.pairs class Reflector(PairwiseComponent): _wirings = { 'A': 'EJMZALYXVBWFCRQUONTSPIKHGD', 'B': 'YRUHQSLDPXNGOKMIEBFZCWVJAT', 'C': 'FVPJIAOYEDRZXWGCTKUQSBNMHL', 'B Thin': 'ENKQAUYWJICOPBLMDXZVFTHRGS', 'C Thin': 'RDOBJNTKVEHMLFCWZAXGYIPSUQ', } def _set_shifts(self, name): self._shifts_in[name] = tuple(ord(ch) - ord('A') for ch in self._wirings[self._name]) self._shifts_out[name] = None def encode(self, idx, shift, ring=0): return self._shift_in[(idx - shift + ring) % _MOD_BASE] def decode(self, idx, shift, ring=0): return encode(idx, shift, ring) class Plugboard(PairwiseComponent): def _set_shifts(self, name): self._shifts_in[name] = tuple(ord(ch) - ord('A') for ch in self._wirings[name]) self._shifts_out[name] = tuple(i for i, _ in sorted(enumerate(self._shifts_in[name]), key=itemgetter(1))) def encode(self, idx): return self._shift_in[idx] def decode(self, idx, shift, ring=0): return self._shift_out[(idx - shift + ring) % _MOD_BASE]
enigma/emulator.py
# -*- coding: utf-8 -*- """ Enigma machine emulator Copyright (C) 2017, 2018 Vanaestea Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """ from . import component import string class Enigma(object): def __init__(self, keeps_initial_state=True): self._reflector = component.Reflector() self._rotors = [] self._plugboard = component.Plugboard() self._keeps_initial_state = keeps_initial_state def set_reflector(self, name): return self._reflector.set(name) def add_reflector(self, name, wiring): return self._reflector.add(name, wiring) def append_rotor(self, name, initial_position='A', ring_position='A'): rotor = component.Rotor() if rotor.set(name, initial_position, ring_position): self._rotors.append(rotor) return True return False # deprecated def push_rotor(self, name, initial_position='A', ring_position='A'): self.append_rotor(name, initial_position, ring_position) def add_rotor(self, name, wiring, turnover=('A',)): rotor = component.Rotor() return rotor.add(name, wiring, turnover) def replace_rotor(self, index, name, initial_position='A', ring_position='A'): rotor = component.Rotor() if rotor.set(name, initial_position, ring_position): self._rotors[index] = rotor return True return False def insert_rotor(self, index, name, initial_position='A', ring_position='A'): rotor = component.Rotor() if rotor.set(name, initial_position, ring_position): self._rotors.insert(index, rotor) return True return False def clear_rotors(self): self._rotors.clear() def add_plugboard(self, name, pairs): return self._plugboard.add(name, pairs) def set_plugboard(self, name): return self._plugboard.set(name) def add_set_plugboard(self, name, pairs): if self._plugboard.add(name, pairs): return self._plugboard.set(name) return False def reset(self): for rotor in self._rotors: rotor.reset() def set_initial_positions(self, positions): positions = positions.upper() try: for rotor, pos in zip(self._rotors, positions): rotor.initial_position = pos except: return False self.reset() return True def is_text_valid(self, text): if set(text.upper()) <= set(string.ascii_uppercase): return True return False def encode(self, text): if self._keeps_initial_state: self.reset() text = text.upper() if not self.is_text_valid(text): raise ValueError('The text includes invalid characters') o = [] for ch in text: idx = ord(ch) - ord('A') # Step i = len(self._rotors) - 1 self._rotors[i].step() i -= 1 if i > 0 and (self._rotors[i + 1].on_turnover() or\ self._rotors[i].on_turnover(True)): self._rotors[i].step() while i > 0: if self._rotors[i].on_turnover(): i -= 1 self._rotors[i].step() else: break # Encode # Incoming e = self._plugboard.encode(idx) shift = 0 ring = 0 for rotor in reversed(self._rotors): e = rotor.encode(e, shift, ring) shift = rotor.shift ring = rotor.ring e = self._reflector.encode(e, shift, ring) # Outgoing shift = 0 ring = 0 for rotor in self._rotors: e = rotor.decode(e, shift, ring) shift = rotor.shift ring = rotor.ring e = self._plugboard.decode(e, shift, ring) o.append(chr(e + ord('A'))) return ''.join(o) def decode(self, text): return self.encode(text) def get_setting(self): return { 'reflector': { 'name': self._reflector.name, 'wiring': self._reflector.wiring, 'pairs': self._reflector.pairs }, 'rotors': tuple( { 'name': rotor.name, 'wiring': rotor.wiring, 'turnover_position': tuple(e for e in rotor.turnover_position), 'initial_position': rotor.initial_position, 'ring_position': rotor.ring_position, } for rotor in self._rotors ), 'plugboard': { 'name': self._plugboard.name, 'wiring': self._plugboard.wiring, 'pairs': self._plugboard.pairs, }, 'keeps_initial_state': self._keeps_initial_state, } def get_positions(self): return [rotor.position for rotor in self._rotors]
test.py
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import enigma from enigma import emulator from enigma import component import json def main(): print('{} version {}'.format(enigma.PROG_NAME, enigma.VERSION)) print() e = emulator.Enigma(False) e.set_reflector('C Thin') e.append_rotor('Gamma', 'T', 'M') e.append_rotor('III', 'P', 'S') e.append_rotor('VIII', 'X', 'F') e.append_rotor('II', 'J', 'Z') e.add_set_plugboard('Plugs', {'A':'K', 'B':'H', 'C':'Y', 'E':'V', 'F':'G', 'I':'Q', 'J':'R', 'M':'P', 'O':'S', 'U':'Z'}) print('Registered reflectors:') print(json.dumps(component.Reflector.get_list(), indent=4)) print('Registered rotors:') print(json.dumps(component.Rotor.get_list(), indent=4)) print('Registered plugboards:') print(json.dumps(component.Plugboard.get_list(), indent=4)) print() print('wirings:') print(json.dumps(e.get_setting(), indent=4)) print() print('initial positions of the rotors:') print(json.dumps(e.get_positions(), indent=4)) print() plain = "I really don't know why it works." formatted = plain.replace(' ', '').replace("'", '').replace('.', 'X').upper() encoded = e.encode(formatted) e.reset() decoded = e.decode(encoded) print('plain: {}'.format(plain)) print('formatted: {}'.format(formatted)) print('encoded: {}'.format(encoded)) print('decoded: {}'.format(decoded)) print() print('current positions of the rotors:') print(json.dumps(e.get_positions(), indent=4)) if __name__ == '__main__': main()
original.py
#!/usr/bin/env python3 # -*- coding: utf-8 -*- import sys import json from random import choice, shuffle from string import ascii_uppercase from enigma import emulator def main(): if len(sys.argv) != 2: sys.exit() n = int(sys.argv[1]) e = emulator.Enigma() a = list(ascii_uppercase) shuffle(a) e.add_reflector('original reflector', {a:b for a, b in zip(a[0::2], a[1::2])}) e.set_reflector('original reflector') shuffle(a) e.add_set_plugboard('original plugboard', {a:b for a, b in zip(a[0::2], a[1::2])}) for i in range(n): name = 'original rotor {}'.format(i + 1) shuffle(a) e.add_rotor(name, ''.join(a), tuple(set((choice(a), choice(a))))) e.append_rotor(name, choice(a), choice(a)) plain = 'Ich weiss wirklich nicht, warum sie funktioniert.' formatted = plain.replace(' ', '').replace(',', '').replace('.', 'X').upper() enc = e.encode(formatted) dec = e.decode(enc) print('wirings:') print(json.dumps(e.get_setting(), indent=4)) print() print('initial positions of the rotors:') print(json.dumps(e.get_positions(), indent=4)) print() print('plain: {}'.format(plain)) print('formatted: {}'.format(formatted)) print('encoded: {}'.format(enc)) print('decoded: {}'.format(dec)) if __name__ == '__main__': main()