diff --git a/README.md b/README.md index 371eff0..5d8d67a 100644 --- a/README.md +++ b/README.md @@ -1,27 +1,19 @@ # Yorke -Yorke is an experimental encryption library and command-line tool. +Yorke is an experimental stream cipher library and command-line tool. # Usage -Encrypt a file using a randomly generated pad. The encrypted cipher text is -sent to STDOUT and the random pad is sent STDERR: +Encrypt a message with a random pad: -``` -cat plain.txt | yorke random_pad 1> cipher.txt 2> key.txt -cat plain.txt | yorke rp 1> cipher.txt 2> key.txt -``` - -Decrypt a file using a previously generated pad: - -``` -yorke file_xor cipher.txt key.txt > plain.txt -yorke fxor key.txt cipher.txt > plain.txt +```bash +yorke xor <(echo "secret message") <(cat /dev/urandom | tee key.txt) > cipher.txt +yorke xor cipher.txt key.txt ``` # Installation -``` +```bash git clone https://github.com/ldgr/yorke.git cd yorke python setup.py install @@ -29,13 +21,13 @@ python setup.py install # Testing -Library: -``` +Run library tests: +```bash pip install nose nosetests ``` -Command: -``` +Run command tests: +```bash ./test.sh ``` diff --git a/bin/yorke b/bin/yorke index 8419050..74c2562 100755 --- a/bin/yorke +++ b/bin/yorke @@ -1,52 +1,44 @@ #!/usr/bin/env python import argparse +import hashlib import sys -from yorke import RandomPad, Xor, byte_by_byte - - -def random_pad(args): - input = byte_by_byte(sys.stdin) - rp = RandomPad().encrypt_bytes(input) - sys.stdout.write(rp.cipher_text) - sys.stderr.write(rp.key_text) - - -def string_xor(args): - right = args.right or sys.stdin.read() - print Xor.str(Xor.strings(args.left, right)) +from yorke import Xor, byte_by_byte, recurrence_relation def file_xor(args): with open(args.left, 'r') as left_fd: - if args.right: - with open(args.right, 'r') as right_fd: - sys.stdout.write(Xor.str(Xor.files(left_fd, right_fd))) - else: - sys.stdout.write(Xor.str(Xor.files(left_fd, sys.stdin))) + with open(args.right, 'r') as right_fd: + sys.stdout.write(Xor.str(Xor.files(left_fd, right_fd))) + + +def sha256(args): + x0 = args.x0 + stream_generator = recurrence_relation(lambda x: hashlib.sha256(x).digest()) + try: + for output in stream_generator(x0): + sys.stdout.write(output) + except IOError: + return def init_parser(): parser = argparse.ArgumentParser() subparsers = parser.add_subparsers(help='Subcommand', dest='mode') - random_pad_parser = subparsers.add_parser('rp') - random_pad_parser = subparsers.add_parser('random_pad') - for input_type in ('string_', 'file_', 's', 'f'): - xor_parser = subparsers.add_parser('{}xor'.format(input_type)) - xor_parser.add_argument('left', type=str) - xor_parser.add_argument('right', nargs='?', type=str, default=None) - return parser + xor_parser = subparsers.add_parser('xor') + xor_parser.add_argument('left', type=str) + xor_parser.add_argument('right', nargs='?', type=str, default=None) -dispatch_table = dict( - random_pad=random_pad, - rp=random_pad, + sha256_parser = subparsers.add_parser('sha256') + sha256_parser.add_argument('x0', type=str) + + return parser - string_xor=string_xor, - sxor=string_xor, - file_xor=file_xor, - fxor=file_xor, +dispatch_table = dict( + xor=file_xor, + sha256=sha256, ) def main(): diff --git a/test.py b/test.py index a9dc10d..77c9161 100644 --- a/test.py +++ b/test.py @@ -1,17 +1,11 @@ from StringIO import StringIO from unittest import TestCase -from yorke import RandomPad, Xor +from yorke import Xor class TestXor(TestCase): - def test_strings(self): - str1 = '\xff\xfe\xfb' - str2 = '\xfb\xfe\xff' - xord = Xor.strings(str1, str2) - self.assertEqual(xord, [4, 0, 4]) - def test_bytestreams(self): stream1 = iter(map(ord, '\xff\xfe\xfb')) stream2 = iter(map(ord, '\xfb\xfe\xff')) @@ -32,24 +26,3 @@ def test_byte_list_to_string(self): def test_str_alias(self): self.assertEqual(Xor.str, Xor.byte_list_to_string) - - -class TestRandomPad(TestCase): - - def test_encrypt(self): - rp = RandomPad() - self.assertEqual(rp.key_bytes, []) - self.assertEqual(rp.cipher_bytes, []) - - plain = 'More secret than top secret' - self.assertEqual(rp, rp.encrypt_string(plain)) - self.assertEqual( - rp.cipher_bytes, - [ord(c) ^ b for c, b in zip(plain, rp.key_bytes)] - ) - self.assertEqual(rp.key_text, ''.join(map(chr, rp.key_bytes))) - self.assertEqual(rp.cipher_text, ''.join(map(chr, rp.cipher_bytes))) - self.assertEqual( - Xor.str(Xor.strings(rp.key_text, rp.cipher_text)), - plain - ) diff --git a/test.sh b/test.sh index 3bbdf7f..7b0cc6d 100755 --- a/test.sh +++ b/test.sh @@ -1,23 +1,13 @@ #!/usr/bin/env bash set -e -SECRET='More secret than top secret' - mkdir out || echo "directory ./out exists" cd out -printf "$SECRET" | yorke random_pad 1> cipher.txt 2> key.txt -printf "$SECRET" | yorke rp 1> cipher.txt 2> key.txt - -printf "$SECRET" > expected.txt - -yorke file_xor cipher.txt key.txt > plain.txt -diff <(xxd plain.txt) <(xxd expected.txt) - -yorke fxor key.txt cipher.txt > plain.txt -diff <(xxd plain.txt) <(xxd expected.txt) +echo 'More secret than top secret' > plain.txt -cat key.txt | yorke fxor cipher.txt > plain.txt -diff <(xxd plain.txt) <(xxd expected.txt) +yorke xor plain.txt <(cat /dev/urandom | tee key.txt) > cipher.txt +yorke xor cipher.txt key.txt > plain2.txt +diff <(xxd plain.txt) <(xxd plain2.txt) echo "Tests Ran Successfully" diff --git a/yorke.py b/yorke.py index 2f8cfe4..9097626 100644 --- a/yorke.py +++ b/yorke.py @@ -1,4 +1,3 @@ -from random import randint def byte_by_byte(file_descriptor): @@ -9,11 +8,25 @@ def byte_by_byte(file_descriptor): raise StopIteration +def recurrence_relation(mapping): + def stream_generator(x0, starting_offset=1): + x = x0 + for _ in xrange(starting_offset): + x = mapping(x) + while 1: + x = mapping(x) + yield x + return stream_generator + + class Xor(object): @classmethod - def strings(cls, left, right): - return [ord(l) ^ ord(r) for l, r in zip(left, right)] + def files(cls, left_fd, right_fd): + left_stream = byte_by_byte(left_fd) + right_stream = byte_by_byte(right_fd) + for byte in cls.bytestreams(left_stream, right_stream): + yield byte @classmethod def bytestreams(cls, left_stream, right_stream): @@ -22,40 +35,8 @@ def bytestreams(cls, left_stream, right_stream): right_byte = right_stream.next() yield left_byte ^ right_byte - @classmethod - def files(cls, left_fd, right_fd): - left_stream = byte_by_byte(left_fd) - right_stream = byte_by_byte(right_fd) - for byte in cls.bytestreams(left_stream, right_stream): - yield byte - @classmethod def byte_list_to_string(cls, byte_list): return ''.join(chr(b) for b in byte_list) str = byte_list_to_string - - -class RandomPad(object): - - def __init__(self): - self.key_bytes = [] - self.cipher_bytes = [] - - def encrypt_bytes(self, byte_stream): - for byte in byte_stream: - random_byte = randint(0, 255) - self.key_bytes.append(random_byte) - self.cipher_bytes.append(byte ^ random_byte) - return self - - def encrypt_string(self, plain_text): - return self.encrypt_bytes(map(ord, plain_text)) - - @property - def key_text(self): - return Xor.str(self.key_bytes) - - @property - def cipher_text(self): - return Xor.str(self.cipher_bytes)