diff --git a/ASCI.py b/ASCI.py new file mode 100644 index 0000000..46e1bd7 --- /dev/null +++ b/ASCI.py @@ -0,0 +1,43 @@ +class ASCI: + num_to_char = dict() + char_to_num = dict() + + for num_char in range(0,128): + char = chr(num_char) + pos = len(char_to_num) + num_to_char[pos] = char + char_to_num[char] = pos + + @staticmethod + def contains(char: str) -> bool: + return (char in ASCI.char_to_num) + pass + + @staticmethod + def isalpha(char: str) -> bool: + return (contains(char) and char.isalpha()) + pass + + @staticmethod + def lower(char: str) -> str: + + return char.lower() + + @staticmethod + def order(char: str) -> int: + + return ASCI.char_to_num[char] + pass + + @staticmethod + def getchar(num: int) -> str: + + return ASCI.num_to_char[num] + pass + + @staticmethod + def size() -> int: + return len(ASCI.num_to_char) + pass + + pass diff --git a/README.md b/README.md index 3f8073c..296e5bf 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,34 @@ # encryptor Encruptor Python MIPT 2021 +Aplication runnig by comand : + +python3 encryptor.py[command][parameters] + + +Commands: +encode - encoding text +--cipher [type of cipher] || Cipher type +--key [key] || Key to encrypt +--input [file path] || Path to input file +--output [file path] || Path to output file + + +decode - decoding text +--cipher [type of cipher] || Cipher type +--key [key] || Key to encrypt +--input [file path] || Path to input file +--output [file path] || Path to output file + + +train - training model +--input [file path] || Text to analyze +--output [file path] || Output model + + +hack - hack caesar cipher +--input [file path] || File to hack +--output [file path] || Hacked file +--model [file path] || Model file + +Example: python3 main.py encode --cipher caesar --key 231 --input ./example.txt --output ./encrypted.txt + python3 main.py encode --cipher vernam --key 231 --input ./example.txt --output ./encrypted.txt \ No newline at end of file diff --git a/alphabet.py b/alphabet.py new file mode 100644 index 0000000..8586534 --- /dev/null +++ b/alphabet.py @@ -0,0 +1,48 @@ +class Alphabet: + num_to_char = dict() + char_to_num = dict() + for num_char in range(ord('a'), ord('z') + 1): + char = chr(num_char) + pos = len(char_to_num) + num_to_char[pos] = char + char_to_num[char] = pos + + for num_char in range(ord('A'), ord('Z') + 1): + char = chr(num_char) + pos = len(char_to_num) + num_to_char[pos] = char + char_to_num[char] = pos + + @staticmethod + def contains(char: str) -> bool: + return (char in Alphabet.char_to_num) + pass + + @staticmethod + def isalpha(char: str) -> bool: + return (contains(char) and char.isalpha()) + pass + + @staticmethod + def lower(char: str) -> str: + + return char.lower() + + @staticmethod + def order(char: str) -> int: + + return Alphabet.char_to_num[char] + pass + + @staticmethod + def getchar(num: int) -> str: + + return Alphabet.num_to_char[num] + pass + + @staticmethod + def size() -> int: + return len(Alphabet.num_to_char) + pass + + pass diff --git a/caesar.py b/caesar.py new file mode 100644 index 0000000..260ef79 --- /dev/null +++ b/caesar.py @@ -0,0 +1,37 @@ +import abc +from alphabet import Alphabet +from encoder_abstract import Encoder + + +class CaesarEncoder(Encoder): + + def __init__(self, key: int): + self.key = int(key) % Alphabet.size() + + + def encode_char(self, char: str, pos: int) -> str: + if Alphabet.contains(char): + sum_code = Alphabet.order(char) + self.key + return Alphabet.getchar(sum_code % Alphabet.size()) + else: + return char + + + + + +class CaesarDecoder(Encoder): + + def __init__(self, key: int): + self.key = int(key) % Alphabet.size() + + + def encode_char(self, char: str, pos: int) -> str: + if Alphabet.contains(char): + sum_code = Alphabet.order(char) - self.key + return Alphabet.getchar(sum_code % Alphabet.size()) + else: + return char + + + diff --git a/encode.py b/encode.py new file mode 100644 index 0000000..d87467d --- /dev/null +++ b/encode.py @@ -0,0 +1,40 @@ +from caesar import CaesarEncoder, CaesarDecoder +from vigenere import VigenereEncoder, VigenereDecoder +from vernam import VernamDecoder, VernamEncoder +from hacker import CaesarHacker +from train import FrequencyTrainer + +def encode(cipher: str, key, text: str) -> str: + + if cipher == 'vigenere': + encoder = VigenereEncoder(key) + elif cipher == 'caesar': + encoder = CaesarEncoder(key) + elif cipher == 'vernam': + encoder = VernamEncoder(key) + return encoder.encode(text) + + + +def decode(cipher: str, key, text: str) -> str: + if cipher == 'vigenere': + decoder = VigenereDecoder(key) + elif cipher == 'caesar': + decoder = CaesarDecoder(key) + elif cipher == 'vernam': + decoder = VernamDecoder(key) + return decoder.encode(text) + + +def train(text: str) -> str: + trainer = FrequencyTrainer() + trainer.train(text) + return trainer.get_json_model() + pass + + +def hack(model: dict, text: str) -> str: + hacker = CaesarHacker(model) + hacker_decoder = CaesarDecoder(hacker.hack_key(text)) + return hacker_decoder.encode(text) + pass \ No newline at end of file diff --git a/encoder_abstract.py b/encoder_abstract.py new file mode 100644 index 0000000..f44b773 --- /dev/null +++ b/encoder_abstract.py @@ -0,0 +1,25 @@ +import abc + + +class Encoder(): + + __metaclass__ = abc.ABCMeta + + @abc.abstractmethod + def __init__(self, key): + pass + + @abc.abstractmethod + def encode_char(self, char: str, pos: int) -> str: + pass + + def encode(self, text: str) -> str: + result = "" + position = 0 + for char in text: + result += self.encode_char(char, position) + position += 1 + return result + pass + + pass diff --git a/example.txt b/example.txt new file mode 100644 index 0000000..6b24ba2 --- /dev/null +++ b/example.txt @@ -0,0 +1,3 @@ + +William Shakespeare +William Shakespeare (baptised 26 April 1564 – died 23 April 1616) was an English poet and playwright, widely regarded as the greatest writer in the English language and the world's pre-eminent dramatist. He is often called England's national poet and the "Bard of Avon" (or simply "The Bard"). His surviving works consist of 38 plays, 154 sonnets, two long narrative poems, and several other poems. His plays have been translated into every major living language, and are performed more often than those of any other playwright. Shakespeare was born and raised in Stratford-upon-Avon. At the age of 18 he married Anne Hathaway, who bore him three children: Susanna, and twins Hamnet and Judith. Between 1585 and 1592 he began a successful career in London as an actor, writer, and part owner of the playing company the Lord Chamberlain's Men, later known as the King's Men. He appears to have retired to Stratford around 1613, where he died three years later. Few records of Shakespeare's private life survive, and there has been considerable speculation about such matters as his sexuality, religious beliefs, and whether the works attributed to him were written by others. Shakespeare produced most of his known work between 1590 and 1613. His early plays were mainly comedies and histories, genres he raised to the peak of sophistication and artistry by the end of the sixteenth century. Next he wrote mainly tragedies until about 1608, including Hamlet, King Lear, and Macbeth, considered some of the finest examples in the English language. In his last phase, he wrote tragicomedies, also known as romances, and collaborated with other playwrights. Many of his plays were published in editions of varying quality and accuracy during his lifetime, and in 1623 two of his former theatrical colleagues published the First Folio, a collected edition of his dramatic works that included all but two of the plays now recognised as Shakespeare's. Shakespeare was a respected poet and playwright in his own day, but his reputation did not rise to its present heights until the nineteenth century. The Romantics, in particular, acclaimed Shakespeare's genius, and the Victorians hero-worshipped Shakespeare with a reverence that George Bernard Shaw called "bardolatry". In the twentieth century, his work was repeatedly adopted and rediscovered by new movements in scholarship and performance. His plays remain highly popular today and are consistently performed and reinterpreted in diverse cultural and political contexts throughout the world. Source: Wikipedia diff --git a/hacker.py b/hacker.py new file mode 100644 index 0000000..411f460 --- /dev/null +++ b/hacker.py @@ -0,0 +1,65 @@ +from abc import ABCMeta, abstractmethod +from train import FrequencyTrainer +from alphabet import Alphabet + + +class Hacker: + + __metaclass__ = ABCMeta + + @abstractmethod + def __init__(self, model): + self.model = dict(model) + pass + + @abstractmethod + def hack_text(self, text: str) -> str: + pass + + pass + + +class CaesarHacker(Hacker): + + def __init__(self, model): + super().__init__(model) + pass + + def find_frequency(self, text: str): + + self.frequency = {} + + for char in text: + if not Alphabet.contains(char): + continue + if char not in self.frequency: + self.frequency[char] = 0 + self.frequency[char] += 1 + pass + + pass + + def hack_key(self, text: str): + + self.find_frequency(text) + + min_shift = 0 + min_delta = -1 + + for shift in range(0, Alphabet.size()): + delta = 0 + for (char, freq) in self.frequency.items(): + num_new_char = (Alphabet.order(char) + shift) % Alphabet.size() + new_char = Alphabet.getchar(num_new_char) + + delta += (self.frequency[char] if char in self.frequency else 0) ** 2 - \ + (self.model[new_char] if new_char in self.model else 0) ** 2 + + if min_delta == -1 or delta < min_delta: + min_delta = delta + min_shift = shift + + return Alphabet.size() - min_shift + pass + + pass diff --git a/main.py b/main.py new file mode 100644 index 0000000..538ddc5 --- /dev/null +++ b/main.py @@ -0,0 +1,130 @@ +import argparse +import json +import sys +from encode import encode, decode, train, hack + + +def parse_args(): + + parser = argparse.ArgumentParser() + + subparsers = parser.add_subparsers() + + # Encoding + encode_parser = subparsers.add_parser('encode') + encode_parser.set_defaults(mode='encode') + encode_parser.add_argument('--cipher', + choices=['caesar', 'vigenere', 'vernam'], + help='Type of cipher', required=True) + encode_parser.add_argument('--key', help='Key to encode', + required=True) + encode_parser.add_argument('--input', type=argparse.FileType('r'), + help='Input file', required=True) + encode_parser.add_argument('--output', type=argparse.FileType('w'), + help='Output file', required=True) + + # Decoding + decode_parser = subparsers.add_parser('decode') + decode_parser.set_defaults(mode='decode') + decode_parser.add_argument('--cipher', + choices=['caesar', 'vigenere', 'vernam'], + help='Type of cipher', required=True) + decode_parser.add_argument('--key', help='Key to decode', + required=True) + decode_parser.add_argument('--input', type=argparse.FileType('r'), + help='Input file', required=True) + decode_parser.add_argument('--output', type=argparse.FileType('w'), + help='Output file', required=True) + + # Train + train_parser = subparsers.add_parser('train') + train_parser.set_defaults(mode='train') + train_parser.add_argument('--input', type=argparse.FileType('r'), + help='File with text') + train_parser.add_argument('--output', type=argparse.FileType('w'), + help='File for model', required=True) + + # Hack + hack_parser = subparsers.add_parser('hack') + hack_parser.set_defaults(mode='hack') + hack_parser.add_argument('--input', type=argparse.FileType('r'), + help='Input file') + hack_parser.add_argument('--output', type=argparse.FileType('w'), + help='Output file') + hack_parser.add_argument('--model', type=argparse.FileType('r'), + help='File with model', required=True) + + return parser.parse_args() + + + +def input(args) -> dict: + + args = parse_args() + + if args.input: + args.text = args.input.read() + else: + args.text = sys.stdin.read() + + if args.mode == 'encode': + return { + "mode": args.mode, + "cipher": args.cipher, + "key": args.key, + "text": args.text + } + elif args.mode == 'decode': + return { + "mode": args.mode, + "cipher": args.cipher, + "key": args.key, + "text": args.text + } + elif args.mode == 'train': + return { + "mode": args.mode, + "text": args.text + } + elif args.mode == 'hack': + try: + args.model = json.load(args.model) + except json.JSONDecodeError: + raise Exception('Model file is not in json format') + + return { + "mode": args.mode, + "text": args.text, + "model": args.model + } + + + + +def output(args, result: str): + + + if args.output: + args.output.write(result) + else: + sys.stdout.write(result) + + + + +shell_args = parse_args() +args = input(shell_args) + +if args['mode'] == 'encode': + result = encode(cipher=args['cipher'], + key=args['key'], text=args['text']) +elif args['mode'] == 'decode': + result = decode(cipher=args['cipher'], + key=args['key'], text=args['text']) +elif args['mode'] == 'train': + result = train(text=args['text']) +elif args['mode'] == 'hack': + result = hack(model=args['model'], text=args['text']) + +output(shell_args, result) + diff --git a/train.py b/train.py new file mode 100644 index 0000000..35d7639 --- /dev/null +++ b/train.py @@ -0,0 +1,59 @@ +from abc import ABCMeta, abstractmethod +import json +from alphabet import Alphabet + + +class Trainer: + + __metaclass__ = ABCMeta + + @abstractmethod + def __init__(self): + pass + + @abstractmethod + def train(self, text: str): + pass + + @abstractmethod + def clear(self): + pass + + @abstractmethod + def get_dict_model(self) -> dict: + pass + + @abstractmethod + def get_json_model(self) -> str: + pass + + pass + + +class FrequencyTrainer(Trainer): + + def __init__(self): + self.model = {} + + def train(self, text: str): + for char in text: + if not Alphabet.contains(char): + continue + if char not in self.model: + self.model[char] = 0 + self.model[char] += 1 + pass + + def clear(self): + self.model = {} + pass + + def get_dict_model(self) -> dict: + return self.model + pass + + def get_json_model(self) -> str: + return json.dumps(self.model) + pass + + pass diff --git a/vernam.py b/vernam.py new file mode 100644 index 0000000..b0e668f --- /dev/null +++ b/vernam.py @@ -0,0 +1,32 @@ +import abc + +from ASCI import ASCI +from encoder_abstract import Encoder + +class VernamEncoder(Encoder): + def __init__(self, key: int): + if (key == ""): + raise Exception("Empty string") + self.key = int(key) % ASCI.size() + pass + def encode_char(self, char:str, pos: int) -> str: + if (ASCI.contains(char)): + return ASCI.getchar((ASCI.order(char) ^ self.key) % ASCI.size()) + else: + return char + + +class VernamDecoder(Encoder): + + def __init__(self, key: str): + if key == "": + raise Exception("Empty string") + self.key = int(key) % ASCI.size() + pass + + def encode_char(self, char: str, pos: int) -> str: + if ASCI.contains(char): + return ASCI.getchar((ASCI.order(char) ^ self.key) % ASCI.size()) + else: + return char + pass \ No newline at end of file diff --git a/vigenere.py b/vigenere.py new file mode 100644 index 0000000..c6dffff --- /dev/null +++ b/vigenere.py @@ -0,0 +1,38 @@ +import abc +from alphabet import Alphabet +from encoder_abstract import Encoder + + +class VigenereEncoder(Encoder): + def __init__(self, key: str): + if key == "": + raise Exception("Empty string") + self.key = key + pass + + def encode_char(self, char: str, pos: int) -> str: + if Alphabet.contains(char): + sum_code = Alphabet.order(char) + Alphabet.order(self.key[pos % len(self.key)]) + return Alphabet.getchar(sum_code % Alphabet.size()) + else: + return char + pass + + pass + + +class VigenereDecoder(Encoder): + + def __init__(self, key: str): + if key == "": + raise Exception("Empty string") + self.key = key + pass + + def encode_char(self, char: str, pos: int) -> str: + if Alphabet.contains(char): + sum_code = Alphabet.order(char) - Alphabet.order(self.key[pos % len(self.key)]) + return Alphabet.getchar(sum_code % Alphabet.size()) + else: + return char + pass