diff --git a/onetimepad.py b/onetimepad.py index 62dfc0e..50cbe9b 100644 --- a/onetimepad.py +++ b/onetimepad.py @@ -7,6 +7,7 @@ TEXTLINE_OFFSET = -1 DEFAULT_PAD_WIDTH = 72 CIPHER_FLOOR = 1 CIPHER_CEIL = 25 +NULLCHAR = '.' def encode(msg, pad): @@ -52,7 +53,7 @@ def _as_line_chunks(chars, width=DEFAULT_PAD_WIDTH): yield ''.join(chunk) chunk = [] if len(chunk) < width: - chunk += (['.'] * (width - len(chunk))) + chunk += ([NULLCHAR] * (width - len(chunk))) yield ''.join(chunk) @@ -66,24 +67,38 @@ def _padfill(text, padlines): 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: - cipherline = \ - _get_cipherline_from_padline_and_textline(padline, textline) - padfilled_lines[i] = cipherline - - return padfilled_lines + lineno = i + 1 + if _is_cipherline(lineno): + padline = padfilled_lines[i - abs(PADLINE_OFFSET)] + textline = padfilled_lines[i - abs(TEXTLINE_OFFSET)] + yield padline + yield textline + yield _cipherline_from_padline_and_textline(padline, textline) -def _get_cipherline_from_padline_and_textline(padline, textline): +def _cipherline_from_padline_and_textline(padline, textline): ret = [] for padchar, textchar in izip(padline, textline): - idx = (_AS_NUMS[padchar] + _AS_NUMS[textchar]) % CIPHER_CEIL + if textchar == NULLCHAR: + ret.append(NULLCHAR) + continue + charnum = _AS_NUMS[padchar] + _AS_NUMS[textchar] + idx = charnum if charnum <= CIPHER_CEIL else charnum % CIPHER_CEIL + ret.append(_AS_ALPHA[idx]) + return ''.join(ret) + + +def _textline_from_cipherline_and_padline(cipherline, padline): + ret = [] + for ciphercar, padchar in izip(cipherline, padline): + if ciphercar == NULLCHAR: + ret.append(NULLCHAR) + continue + charnum = _AS_NUMS[ciphercar] - _AS_NUMS[padchar] + idx = charnum if charnum <= CIPHER_CEIL else charnum % CIPHER_CEIL + if idx < 0: + idx = CIPHER_CEIL + idx ret.append(_AS_ALPHA[idx]) return ''.join(ret) diff --git a/test_onetimepad.py b/test_onetimepad.py index b5b6faf..b6c78ca 100644 --- a/test_onetimepad.py +++ b/test_onetimepad.py @@ -56,11 +56,24 @@ class TestOneTimePad(unittest.TestCase): self.assertFalse(OT._is_cipherline(lineno), 'line {0} is not cipherline'.format(lineno)) + def test_make_cipherline_from_padline_and_textline(self): + actual = OT._cipherline_from_padline_and_textline(TEST_PADLINE, + TEST_TEXTLINE) + self.assertEqual(TEST_CIPHERLINE, actual) + + def test_make_textline_from_cipherline_and_padline(self): + actual = OT._textline_from_cipherline_and_padline(TEST_CIPHERLINE, + TEST_PADLINE) + self.assertEqual(TEST_TEXTLINE, actual) + 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) +TEST_PADLINE = 'HUCHUGGHOBUXADDHOHPGQBKXQKAOLRL' +TEST_TEXTLINE = 'THISCAKEISUNSATISFACTORYTOMEYES' +TEST_CIPHERLINE = 'BCMAXHRNXUPLTEXRGOQKKQBWKYNTKWD' if __name__ == '__main__': unittest.main()