fixed determination of pad/text/cipher lines by number

cat-town
Dan Buch 15 years ago
parent ca11fa5f32
commit 76437746a2

@ -1,6 +1,9 @@
import random import random
from itertools import izip
PAD_MODULO = 3 PAD_MODULO = 3
PADLINE_OFFSET = -2
TEXTLINE_OFFSET = -1
DEFAULT_PAD_WIDTH = 72 DEFAULT_PAD_WIDTH = 72
CIPHER_FLOOR = 1 CIPHER_FLOOR = 1
CIPHER_CEIL = 25 CIPHER_CEIL = 25
@ -53,35 +56,59 @@ def _as_line_chunks(chars, width=DEFAULT_PAD_WIDTH):
yield ''.join(chunk) yield ''.join(chunk)
def _padfill(text, pad): def _padfill(text, padlines):
padlines = pad.splitlines() padlines = [line.strip() for line in padlines if line.strip()]
textlines = _chunk_chars_into_lines(text, _get_textwidth(pad)) textlines = _chunk_chars_into_lines(text, _get_textwidth(padlines))
for lineno, padline in enumerate(padlines[:]): for textline, padline in izip(textlines, padlines):
if _is_padline(lineno) or _is_cipherline(lineno): yield padline
continue 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: else:
padlines[lineno] = textlines.pop(0) if textlines[1:] else '' cipherline = \
return padlines _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): 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): def _is_padline(lineno):
return not lineno % PAD_MODULO return _is_cipherline(lineno + abs(PADLINE_OFFSET))
def _is_txtline(lineno): def _is_textline(lineno):
return (lineno % 2 and not lineno % PAD_MODULO) return _is_cipherline(lineno + abs(TEXTLINE_OFFSET))
def _is_cipherline(lineno): def _is_cipherline(lineno):
return lineno % PAD_MODULO return not lineno % PAD_MODULO
def _mk_as_alpha(): def _mk_as_alpha_as_nums():
as_alpha = dict() as_alpha = {}
as_nums = {}
a_chr = ord('A') a_chr = ord('A')
past_j = False past_j = False
@ -92,7 +119,11 @@ def _mk_as_alpha():
as_alpha[key] = letter as_alpha[key] = letter
else: else:
past_j = True 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()

@ -23,40 +23,43 @@ class TestOneTimePad(unittest.TestCase):
'{1}, actual={2}'.format(self._padsize, '{1}, actual={2}'.format(self._padsize,
width, actual)) width, actual))
def test_two_out_of_every_three_lines_are_empty_on_new_pad(self): def test_is_padline(self):
pad = OT.create_pad_lines(2000) for lineno in PADLINES:
for lineno, line in enumerate(pad): self.assertTrue(OT._is_padline(lineno),
line = line.strip() 'line {0} is padline'.format(lineno))
if OT._is_padline(lineno): for lineno in TEXTLINES:
self.assertTrue(bool(len(line)), self.assertFalse(OT._is_padline(lineno),
'pad line {0} is non-empty'.format(lineno)) 'line {0} is not padline'.format(lineno))
elif OT._is_txtline(lineno): for lineno in CIPHERLINES:
self.assertFalse(bool(len(line)), self.assertFalse(OT._is_padline(lineno),
'text line {0} is empty'.format(lineno)) 'line {0} is not padline'.format(lineno))
elif OT._is_cipherline(lineno):
self.assertFalse(bool(len(line)), def test_is_textline(self):
'cipher line {0} is empty'.format(lineno)) for lineno in TEXTLINES:
else: self.assertTrue(OT._is_textline(lineno),
raise NotImplementedError(lineno) 'line {0} is textline'.format(lineno))
for lineno in PADLINES:
def test_padfill_leaves_every_third_line_empty(self): self.assertFalse(OT._is_textline(lineno),
msg = self.msg[:] 'line {0} is not textline'.format(lineno))
pad = OT.create_pad(len(msg)) for lineno in CIPHERLINES:
filled = OT._padfill(msg, pad) self.assertFalse(OT._is_textline(lineno),
self.assertTrue(bool(filled)) 'line {0} is not textline'.format(lineno))
for lineno, line in enumerate(filled):
line = line.strip() def test_is_cipherline(self):
if OT._is_cipherline(lineno): for lineno in CIPHERLINES:
self.assertFalse(bool(len(line)), self.assertTrue(OT._is_cipherline(lineno),
'line {0} is empty'.format(lineno)) 'line {0} is cipherline'.format(lineno))
elif OT._is_txtline(lineno): for lineno in PADLINES:
self.assertTrue(bool(len(line)), self.assertFalse(OT._is_cipherline(lineno),
'text line {0} is non-empty'.format(lineno)) 'line {0} is not cipherline'.format(lineno))
elif OT._is_padline(lineno): for lineno in TEXTLINES:
self.assertTrue(bool(len(line)), self.assertFalse(OT._is_cipherline(lineno),
'pad line {0} is non-empty'.format(lineno)) 'line {0} is not cipherline'.format(lineno))
else:
raise NotImplementedError(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__': if __name__ == '__main__':

Loading…
Cancel
Save