From 76437746a2e9d992d10ef909619df3af2665d3b6 Mon Sep 17 00:00:00 2001 From: Dan Buch Date: Fri, 27 Nov 2009 13:53:24 -0500 Subject: [PATCH] fixed determination of pad/text/cipher lines by number --- onetimepad.py | 65 +++++++++++++++++++++++++++++++----------- test_onetimepad.py | 71 ++++++++++++++++++++++++---------------------- 2 files changed, 85 insertions(+), 51 deletions(-) diff --git a/onetimepad.py b/onetimepad.py index ab0e120..62dfc0e 100644 --- a/onetimepad.py +++ b/onetimepad.py @@ -1,6 +1,9 @@ import random +from itertools import izip PAD_MODULO = 3 +PADLINE_OFFSET = -2 +TEXTLINE_OFFSET = -1 DEFAULT_PAD_WIDTH = 72 CIPHER_FLOOR = 1 CIPHER_CEIL = 25 @@ -53,35 +56,59 @@ def _as_line_chunks(chars, width=DEFAULT_PAD_WIDTH): yield ''.join(chunk) -def _padfill(text, pad): - padlines = pad.splitlines() - textlines = _chunk_chars_into_lines(text, _get_textwidth(pad)) - for lineno, padline in enumerate(padlines[:]): - if _is_padline(lineno) or _is_cipherline(lineno): - continue +def _padfill(text, padlines): + padlines = [line.strip() for line in padlines if line.strip()] + textlines = _chunk_chars_into_lines(text, _get_textwidth(padlines)) + for textline, padline in izip(textlines, padlines): + yield padline + yield textline + yield '' + + +def _cipherfill(padfilled_lines): + padline = textline = None + for i, line in enumerate(padfilled_lines): + if _is_padline(i): + padline = line + elif _is_textline(i): + textline = line else: - padlines[lineno] = textlines.pop(0) if textlines[1:] else '' - return padlines + cipherline = \ + _get_cipherline_from_padline_and_textline(padline, textline) + padfilled_lines[i] = cipherline + + return padfilled_lines + + +def _get_cipherline_from_padline_and_textline(padline, textline): + ret = [] + for padchar, textchar in izip(padline, textline): + idx = (_AS_NUMS[padchar] + _AS_NUMS[textchar]) % CIPHER_CEIL + ret.append(_AS_ALPHA[idx]) + return ''.join(ret) def _get_textwidth(text): - return max([len(line) for line in text.splitlines()]) + if isinstance(text, basestring): + text = text.splitlines() + return max([len(line) for line in text]) def _is_padline(lineno): - return not lineno % PAD_MODULO + return _is_cipherline(lineno + abs(PADLINE_OFFSET)) -def _is_txtline(lineno): - return (lineno % 2 and not lineno % PAD_MODULO) +def _is_textline(lineno): + return _is_cipherline(lineno + abs(TEXTLINE_OFFSET)) def _is_cipherline(lineno): - return lineno % PAD_MODULO + return not lineno % PAD_MODULO -def _mk_as_alpha(): - as_alpha = dict() +def _mk_as_alpha_as_nums(): + as_alpha = {} + as_nums = {} a_chr = ord('A') past_j = False @@ -92,7 +119,11 @@ def _mk_as_alpha(): as_alpha[key] = letter else: past_j = True - return as_alpha + + for key, val in as_alpha.iteritems(): + as_nums[val] = key + + return as_alpha, as_nums -_AS_ALPHA = _mk_as_alpha() +_AS_ALPHA, _AS_NUMS = _mk_as_alpha_as_nums() diff --git a/test_onetimepad.py b/test_onetimepad.py index a1f4d8f..b5b6faf 100644 --- a/test_onetimepad.py +++ b/test_onetimepad.py @@ -23,40 +23,43 @@ class TestOneTimePad(unittest.TestCase): '{1}, actual={2}'.format(self._padsize, width, actual)) - def test_two_out_of_every_three_lines_are_empty_on_new_pad(self): - pad = OT.create_pad_lines(2000) - for lineno, line in enumerate(pad): - line = line.strip() - if OT._is_padline(lineno): - self.assertTrue(bool(len(line)), - 'pad line {0} is non-empty'.format(lineno)) - elif OT._is_txtline(lineno): - self.assertFalse(bool(len(line)), - 'text line {0} is empty'.format(lineno)) - elif OT._is_cipherline(lineno): - self.assertFalse(bool(len(line)), - 'cipher line {0} is empty'.format(lineno)) - else: - raise NotImplementedError(lineno) - - def test_padfill_leaves_every_third_line_empty(self): - msg = self.msg[:] - pad = OT.create_pad(len(msg)) - filled = OT._padfill(msg, pad) - self.assertTrue(bool(filled)) - for lineno, line in enumerate(filled): - line = line.strip() - if OT._is_cipherline(lineno): - self.assertFalse(bool(len(line)), - 'line {0} is empty'.format(lineno)) - elif OT._is_txtline(lineno): - self.assertTrue(bool(len(line)), - 'text line {0} is non-empty'.format(lineno)) - elif OT._is_padline(lineno): - self.assertTrue(bool(len(line)), - 'pad line {0} is non-empty'.format(lineno)) - else: - raise NotImplementedError(lineno) + def test_is_padline(self): + for lineno in PADLINES: + self.assertTrue(OT._is_padline(lineno), + 'line {0} is padline'.format(lineno)) + for lineno in TEXTLINES: + self.assertFalse(OT._is_padline(lineno), + 'line {0} is not padline'.format(lineno)) + for lineno in CIPHERLINES: + self.assertFalse(OT._is_padline(lineno), + 'line {0} is not padline'.format(lineno)) + + def test_is_textline(self): + for lineno in TEXTLINES: + self.assertTrue(OT._is_textline(lineno), + 'line {0} is textline'.format(lineno)) + for lineno in PADLINES: + self.assertFalse(OT._is_textline(lineno), + 'line {0} is not textline'.format(lineno)) + for lineno in CIPHERLINES: + self.assertFalse(OT._is_textline(lineno), + 'line {0} is not textline'.format(lineno)) + + def test_is_cipherline(self): + for lineno in CIPHERLINES: + self.assertTrue(OT._is_cipherline(lineno), + 'line {0} is cipherline'.format(lineno)) + for lineno in PADLINES: + self.assertFalse(OT._is_cipherline(lineno), + 'line {0} is not cipherline'.format(lineno)) + for lineno in TEXTLINES: + self.assertFalse(OT._is_cipherline(lineno), + 'line {0} is not cipherline'.format(lineno)) + + +PADLINES = (1, 4, 7, 10, 13, 16, 19, 22, 25) +TEXTLINES = (2, 5, 8, 11, 14, 17, 20, 23, 26) +CIPHERLINES = (3, 6, 9, 12, 15, 18, 21, 24, 27) if __name__ == '__main__':