From 62abca7fce2b3c4e9b16b43209371bf3c225fb6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Mon, 20 Jun 2011 01:06:39 +0200 Subject: Added block padding to be able to work with leading zeroes, breaks all kind of stuff --- rsa/__init__.py | 33 +++++++++++++++++++++++++++------ rsa/transform.py | 4 +++- tests/test_binary.py | 37 +++++++++++++++++++++++++++++++++++++ tests/test_strings.py | 7 +++---- 4 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 tests/test_binary.py diff --git a/rsa/__init__.py b/rsa/__init__.py index d9999ae..7fda4e1 100755 --- a/rsa/__init__.py +++ b/rsa/__init__.py @@ -42,6 +42,19 @@ def decode64chops(string): return chops +def block_size(n): + '''Returns the block size in bytes, given the public key. + + The block size is determined by the 'n=p*q' component of the key. + ''' + + # Set aside 2 bits so setting of safebit won't overflow modulo n. + nbits = rsa.common.bit_size(n) - 2 + nbytes = nbits / 8 + + return nbytes + + def chopstring(message, key, n, int_op): """Chops the 'message' into integers that fit into n. @@ -56,13 +69,10 @@ def chopstring(message, key, n, int_op): Used by 'encrypt' and 'sign'. """ - msglen = len(message) - mbits = msglen * 8 - # Set aside 2 bits so setting of safebit won't overflow modulo n. - nbits = rsa.common.bit_size(n) - 2 + nbytes = block_size(n) - nbytes = nbits / 8 + msglen = len(message) blocks = msglen / nbytes if msglen % nbytes > 0: @@ -90,14 +100,25 @@ def gluechops(string, key, n, funcref): messageparts = [] chops = decode64chops(string) #Decode base64 strings into integer chops + + nbytes = block_size(n) for chop in chops: value = funcref(chop, key, n) #Decrypt each chop block = rsa.transform.int2bytes(value) + + # Pad block with 0-bytes until we have reached the block size + blocksize = len(block) + padsize = nbytes - blocksize + if padsize < 0: + raise ValueError('Block larger than block size (%i > %i)!' % + (blocksize, nbytes)) + elif padsize > 0: + block = '\x00' * padsize + block + messageparts.append(block) # Combine decrypted strings into a msg - return ''.join(messageparts) def encrypt(message, key): diff --git a/rsa/transform.py b/rsa/transform.py index 9cd1ef3..6719dcb 100644 --- a/rsa/transform.py +++ b/rsa/transform.py @@ -12,7 +12,9 @@ def bit_size(number): return int(math.ceil(math.log(number,2))) def bytes2int(bytes): - """Converts a list of bytes or a string to an integer + """Converts a list of bytes or an 8-bit string to an integer. + + When using unicode strings, encode it to some encoding like UTF8 first. >>> (((128 * 256) + 64) * 256) + 15 8405007 diff --git a/tests/test_binary.py b/tests/test_binary.py new file mode 100644 index 0000000..f770b72 --- /dev/null +++ b/tests/test_binary.py @@ -0,0 +1,37 @@ +'''Tests string operations.''' + +import struct +import unittest + +import rsa + +class BinaryTest(unittest.TestCase): + + def setUp(self): + (self.pub, self.priv) = rsa.newkeys(64) + + def test_enc_dec(self): + + message = struct.pack('>IIII', 0, 0, 0, 1) + 20 * '\x00' + print "\tMessage: %r" % message + + encrypted = rsa.encrypt(message, self.pub) + print "\tEncrypted: %r" % encrypted + + decrypted = rsa.decrypt(encrypted, self.priv) + print "\tDecrypted: %r" % decrypted + + self.assertEqual(message, decrypted) + + def test_sign_verify(self): + + message = struct.pack('>IIII', 0, 0, 0, 1) + 20 * '\x00' + print "\tMessage: %r" % message + + signed = rsa.sign(message, self.priv) + print "\tSigned: %r" % signed + + verified = rsa.verify(signed, self.pub) + print "\tVerified: %r" % verified + + self.assertEqual(message, verified) diff --git a/tests/test_strings.py b/tests/test_strings.py index c5803e4..8baa63d 100644 --- a/tests/test_strings.py +++ b/tests/test_strings.py @@ -11,8 +11,7 @@ class StringTest(unittest.TestCase): def test_enc_dec(self): - # TODO: test with unicode strings and non-ascii chars - message = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + message = u"Euro=\u20ac ABCDEFGHIJKLMNOPQRSTUVWXYZ".encode('utf-8') print "\tMessage: %s" % message encrypted = rsa.encrypt(message, self.pub) @@ -25,8 +24,8 @@ class StringTest(unittest.TestCase): def test_sign_verify(self): - # TODO: test with unicode strings and non-ascii chars - message = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + message = u"Euro=\u20ac ABCDEFGHIJKLMNOPQRSTUVWXYZ".encode('utf-8') + print "\tMessage: %s" % message signed = rsa.sign(message, self.priv) print "\tSigned: %s" % signed -- cgit v1.2.3