def test_put_bitstream_copy_self(self): """ Test using the put_bitstream_copy method with the same BitStream object as origin and destination. """ bitstream = BitStream() # Generate a random string of bits, ie: ('0' | '1')* num_bits = 50 bits = "" for i in range(0, num_bits): bits += random.choice( ('0', '1')) # inefficient, but ok for a test. # Put those bits in the BitStream... bitstream.put_bit_dump_string(bits) # ... copy the bitstream into itself at any point: bitstream.seek(random.randint(0, 50)) bitstream.put_bitstream_copy(bitstream) # Check that the bitstream was unchanged by the previous operation: # (overwriting data with the same data is the same as doing nothing, # except that the current position is changed to the end of the stream) self.assertEquals(bitstream.get_length(), num_bits) self.assertEquals(bitstream.get_current_pos(), num_bits) bitstream.seek(0) read_bits = bitstream.get_bit_dump_string(bitstream.get_length()) self.assertEqual(read_bits, bits)
def test_put_bitstream_copy_self(self): """ Test using the put_bitstream_copy method with the same BitStream object as origin and destination. """ bitstream = BitStream() # Generate a random string of bits, ie: ('0' | '1')* num_bits = 50 bits = "" for i in range(0,num_bits): bits += random.choice(('0','1')) # inefficient, but ok for a test. # Put those bits in the BitStream... bitstream.put_bit_dump_string(bits) # ... copy the bitstream into itself at any point: bitstream.seek(random.randint(0,50)) bitstream.put_bitstream_copy(bitstream) # Check that the bitstream was unchanged by the previous operation: # (overwriting data with the same data is the same as doing nothing, # except that the current position is changed to the end of the stream) self.assertEquals(bitstream.get_length(),num_bits) self.assertEquals(bitstream.get_current_pos(),num_bits) bitstream.seek(0) read_bits = bitstream.get_bit_dump_string(bitstream.get_length()) self.assertEqual(read_bits, bits)
def test_put_bitstream_copy(self): """ Test the basic functionality of the put_bitstream_copy method. """ bitstream1 = BitStream() bitstream1.put_string("This is bitstream1") bitstream2 = BitStream() bitstream2.put_string("This is bitstream2") bitstream3 = BitStream() # bitstream3 remains empty bitstream4 = BitStream() bitstream4.put_string("This is bitstream4") # copy the full contents of bitstream2 to the end of bitstream1 bitstream2.seek(0) bitstream1.put_bitstream_copy(bitstream2) self.assertEquals(bitstream2.get_current_pos(), bitstream2.get_length()) # check the contents of bitstream1 bitstream1.seek(0) self.assertEquals(bitstream1.get_string(bitstream1.get_length()), "This is bitstream1This is bitstream2") # copy the full contents of bitstream3 (aka. nothing) to the end of # bitstream4 bitstream3.seek(0) bitstream4.put_bitstream_copy(bitstream3) # check the contents of bitstream4 bitstream4.seek(0) self.assertEquals(bitstream4.get_string(bitstream4.get_length()), "This is bitstream4") # copy the contents of bitstream4 from the position 8 onwards bitstream4.seek(8 * 8) bitstream1.put_bitstream_copy(bitstream4) self.assertEquals(bitstream4.get_current_pos(), bitstream4.get_length()) # check the contents of bitstream1 bitstream1.seek(0) self.assertEquals(bitstream1.get_string(bitstream1.get_length()), "This is bitstream1This is bitstream2bitstream4")
def test_put_bitstream_copy(self): """ Test the basic functionality of the put_bitstream_copy method. """ bitstream1 = BitStream() bitstream1.put_string("This is bitstream1") bitstream2 = BitStream() bitstream2.put_string("This is bitstream2") bitstream3 = BitStream() # bitstream3 remains empty bitstream4 = BitStream() bitstream4.put_string("This is bitstream4") # copy the full contents of bitstream2 to the end of bitstream1 bitstream2.seek(0) bitstream1.put_bitstream_copy(bitstream2) self.assertEquals(bitstream2.get_current_pos(), bitstream2.get_length()) # check the contents of bitstream1 bitstream1.seek(0) self.assertEquals(bitstream1.get_string(bitstream1.get_length()), "This is bitstream1This is bitstream2") # copy the full contents of bitstream3 (aka. nothing) to the end of # bitstream4 bitstream3.seek(0) bitstream4.put_bitstream_copy(bitstream3) # check the contents of bitstream4 bitstream4.seek(0) self.assertEquals(bitstream4.get_string(bitstream4.get_length()), "This is bitstream4") # copy the contents of bitstream4 from the position 8 onwards bitstream4.seek(8*8) bitstream1.put_bitstream_copy(bitstream4) self.assertEquals(bitstream4.get_current_pos(), bitstream4.get_length()) # check the contents of bitstream1 bitstream1.seek(0) self.assertEquals(bitstream1.get_string(bitstream1.get_length()), "This is bitstream1This is bitstream2bitstream4")
def encrypt_bitstream(self, bitstream, pad_to=None, task_monitor=None): """ Encrypts the given bitstream into a ciphertext object. Arguments: bitstream::BitStream-- A stream of bits to encrypt (see BitStream utility class). pad_to::int -- Minimum size (in bytes) of the resulting ciphertext. Data will be padded before encryption to match this size. task_monitor::TaskMonitor -- A task monitor for this task. Returns: ciphertext:Ciphertext -- A ciphertext object encapsulating the encrypted data. """ random = StrongRandom() ## PART 1 # First, format the bitstream as per Ciphertext.py Note 001, # previous to encryption. # [size (64 bits) | message (size bits) | padding (X bits) ] ## formated_bitstream = BitStream() # The first 64 encode the size of the actual data in bits SIZE_BLOCK_LENGTH = 64 size_in_bits = bitstream.get_length() if(size_in_bits >= 2**SIZE_BLOCK_LENGTH): raise ValueError("The size of the bitstream to encrypt is larger " \ "than 16 Exabits. The current format for " \ "PloneVote ciphertext only allows encrypting a " \ "maximum of 16 Exabits of information.") formated_bitstream.put_num(size_in_bits, SIZE_BLOCK_LENGTH) # We then copy the contents of the original bitstream bitstream.seek(0) formated_bitstream.put_bitstream_copy(bitstream) # Finally, we append random data until we reach the desired pad_to # length unpadded_length = formated_bitstream.get_length() if(pad_to != None and (pad_to * 8) > unpadded_length): full_length = pad_to * 8 else: full_length = unpadded_length padding_left = full_length - unpadded_length while(padding_left > 1024): padding_bits = random.randint(1, 2**1024) formated_bitstream.put_num(padding_bits,1024) padding_left -= 1024 if(padding_left > 0): padding_bits = random.randint(1, 2**padding_left) formated_bitstream.put_num(padding_bits, padding_left) padding_left = 0 ## PART 2 # We encrypt the formated bitsteam using ElGamal into a Ciphertext # object. # See "Handbook of Applied Cryptography" Algorithm 8.18 ## # block_size is the size of each block of bits to encrypt # since we can only encrypt messages in [0, p - 1] # we should use (nbits - 1) as the block size, where # 2**(nbits - 1) < p < 2**nbits block_size = self.cryptosystem.get_nbits() - 1 prime = self.cryptosystem.get_prime() generator = self.cryptosystem.get_generator() # We pull data from the bitstream one block at a time and encrypt it formated_bitstream.seek(0) ciphertext = \ Ciphertext(self.cryptosystem.get_nbits(), self.get_fingerprint()) plaintext_bits_left = formated_bitstream.get_length() # Check if we have a task monitor and register with it if(task_monitor != None): # We will do two tick()s per block to encrypt: one for generating # the gamma component of the ciphertext block and another for the # delta component (those are the two time intensive steps, # because of exponentiation). ticks = math.ceil((1.0 * plaintext_bits_left) / block_size) * 2 encrypt_task_mon = \ task_monitor.new_subtask("Encrypt data", expected_ticks = ticks) while(plaintext_bits_left > 0): # get next block (message, m, etc) to encrypt if(plaintext_bits_left >= block_size): block = formated_bitstream.get_num(block_size) plaintext_bits_left -= block_size else: block = formated_bitstream.get_num(plaintext_bits_left) # Encrypt as if the stream was filled with random data past its # end, this avoids introducing a 0's gap during decryption to # bitstream displacement = block_size - plaintext_bits_left block = block << displacement padding = random.randint(0, 2**displacement - 1) assert (padding / 2**displacement == 0), \ "padding should be at most displacement bits long" block = block | padding plaintext_bits_left = 0 # Select a random integer k, 1 <= k <= p − 2 k = random.randint(1, prime - 2) # Compute gamma and delta gamma = pow(generator, k, prime) if(task_monitor != None): encrypt_task_mon.tick() delta = (block * pow(self._key, k, prime)) % prime if(task_monitor != None): encrypt_task_mon.tick() # Add this encrypted data portion to the ciphertext object ciphertext.append(gamma, delta) # return the ciphertext object return ciphertext
def encrypt_bitstream(self, bitstream, pad_to=None, task_monitor=None): """ Encrypts the given bitstream into a ciphertext object. Arguments: bitstream::BitStream-- A stream of bits to encrypt (see BitStream utility class). pad_to::int -- Minimum size (in bytes) of the resulting ciphertext. Data will be padded before encryption to match this size. task_monitor::TaskMonitor -- A task monitor for this task. Returns: ciphertext:Ciphertext -- A ciphertext object encapsulating the encrypted data. """ random = StrongRandom() ## PART 1 # First, format the bitstream as per Ciphertext.py Note 001, # previous to encryption. # [size (64 bits) | message (size bits) | padding (X bits) ] ## formated_bitstream = BitStream() # The first 64 encode the size of the actual data in bits SIZE_BLOCK_LENGTH = 64 size_in_bits = bitstream.get_length() if(size_in_bits >= 2**SIZE_BLOCK_LENGTH): raise ValueError("The size of the bitstream to encrypt is larger " \ "than 16 Exabits. The current format for " \ "PloneVote ciphertext only allows encrypting a " \ "maximum of 16 Exabits of information.") formated_bitstream.put_num(size_in_bits, SIZE_BLOCK_LENGTH) # We then copy the contents of the original bitstream bitstream.seek(0) formated_bitstream.put_bitstream_copy(bitstream) # Finally, we append random data until we reach the desired pad_to # length unpadded_length = formated_bitstream.get_length() if(pad_to != None and (pad_to * 8) > unpadded_length): full_length = pad_to * 8 else: full_length = unpadded_length padding_left = full_length - unpadded_length while(padding_left > 1024): padding_bits = random.randint(1, 2**1024) formated_bitstream.put_num(padding_bits,1024) padding_left -= 1024 if(padding_left > 0): padding_bits = random.randint(1, 2**padding_left) formated_bitstream.put_num(padding_bits, padding_left) padding_left = 0 ## PART 2 # We encrypt the formated bitsteam using ElGamal into a Ciphertext # object. # See "Handbook of Applied Cryptography" Algorithm 8.18 ## # block_size is the size of each block of bits to encrypt # since we can only encrypt messages in [0, p - 1] # we should use (nbits - 1) as the block size, where # 2**(nbits - 1) < p < 2**nbits block_size = self.cryptosystem.get_nbits() - 1 prime = self.cryptosystem.get_prime() generator = self.cryptosystem.get_generator() # We pull data from the bitstream one block at a time and encrypt it formated_bitstream.seek(0) ciphertext = \ Ciphertext(self.cryptosystem.get_nbits(), self.get_fingerprint()) plaintext_bits_left = formated_bitstream.get_length() # Check if we have a task monitor and register with it if(task_monitor != None): # We will do two tick()s per block to encrypt: one for generating # the gamma component of the ciphertext block and another for the # delta component (those are the two time intensive steps, # because of exponentiation). ticks = math.ceil((1.0 * plaintext_bits_left) / block_size) * 2 encrypt_task_mon = \ task_monitor.new_subtask("Encrypt data", expected_ticks = ticks) while(plaintext_bits_left > 0): # get next block (message, m, etc) to encrypt if(plaintext_bits_left >= block_size): block = formated_bitstream.get_num(block_size) plaintext_bits_left -= block_size else: block = formated_bitstream.get_num(plaintext_bits_left) # Encrypt as if the stream was filled with random data past its # end, this avoids introducing a 0's gap during decryption to # bitstream displacement = block_size - plaintext_bits_left block = block << displacement padding = random.randint(0, 2**displacement - 1) assert (padding // 2**displacement == 0), \ "padding should be at most displacement bits long" block = block | padding plaintext_bits_left = 0 # Select a random integer k, 1 <= k <= p − 2 k = random.randint(1, prime - 2) # Compute gamma and delta gamma = pow(generator, k, prime) if(task_monitor != None): encrypt_task_mon.tick() delta = (block * pow(self._key, k, prime)) % prime if(task_monitor != None): encrypt_task_mon.tick() # Add this encrypted data portion to the ciphertext object ciphertext.append(gamma, delta) # return the ciphertext object return ciphertext