예제 #1
0
	def apply(self, ciphertext):
		"""
		Re-encrypts the given ciphertext with this re-encryption information.
		
		Arguments:
			ciphertext::Ciphertext -- The ciphertext to be re-encrypted.
		
		Returns:
			reencrypted_ciphertext::Ciphertext	-- A re-encryption of 
				ciphertext performed with this re-encryption information.
				
		Throws:
			IncompatibleCiphertextError	-- 
				If the ciphertext and this re-encryption information are not 
				compatible. Either because their length in blocks differs, or 
				because they don't have the same public key.
		"""
		# Check length compatibility
		if(ciphertext.get_length() != self.get_length()):
			raise IncompatibleCiphertextError("The given ciphertext is " \
				"incompatible with this re-encryption information object: " \
				"The two objects have different length. There are %d blocks "\
				"of ciphertext and %d blocks of re-encryption information." \
				% (ciphertext.get_length(), self.get_length()))
		
		# Check key compatibility
		if(ciphertext.pk_fingerprint != self.public_key.get_fingerprint()):
			raise IncompatibleCiphertextError("The given ciphertext is " \
				"incompatible with this re-encryption information object: " \
				"The public key used to encrypt the ciphertext is different " \
				"from the one used to generate this re-encryption " \
				"information object.")
		
		# Get nbits and p
		nbits = self.public_key.cryptosystem.get_nbits()
		prime = self.public_key.cryptosystem.get_prime()
		
		# For each block of the ciphertext, apply the corresponding block of 
		# re-encryption.
		reencrypted_ciphertext = Ciphertext(nbits, ciphertext.pk_fingerprint)
		for i in range(0, self.get_length()):
			gamma, delta = ciphertext[i]
			gr, yr = self[i]
			new_gamma = (gr * gamma) % prime
			new_delta = (yr * delta) % prime
			reencrypted_ciphertext.append(new_gamma, new_delta)
			
		return reencrypted_ciphertext
예제 #2
0
 def test_save_load_file(self):
     """
     Test that we can correctly save a PrivateKey to a file and load it 
     back. 
     """
     # Use the public key to encrypt the message
     ciphertext = self.public_key.encrypt_text(self.message)
     
     # Get a temporary file object using tempfile
     (file_object, file_path) = tempfile.mkstemp()
     
     # We close the file descriptor since we will not be using it, instead 
     # Ciphertext's methods take the filename and open/close the file as 
     # needed.
     # Note that using mkstemp() instead tempfile.TemporaryFile means the 
     # file remains in the filesystem even after it is closed.
     os.close(file_object)
     
     # Save the ciphertext using to_file(...)
     ciphertext.to_file(file_path)
     
     # Load it back using Ciphertext.from_file(...)
     recovered_ciphertext = Ciphertext.from_file(file_path)
     
     # Check that the recovered ciphertext is recognized as equal to the 
     # original ciphertext
     self.assertEqual(recovered_ciphertext, ciphertext)
     self.assertFalse(recovered_ciphertext != ciphertext)
     
     # then use the private key to decrypt the deserialized ciphertext
     recovered_message = \
         self.private_key.decrypt_to_text(recovered_ciphertext)
     
     # Check that the message was recovered correctly
     self.assertEqual(recovered_message, self.message)
     
     # Delete the temporary file
     os.remove(file_path)
예제 #3
0
def run_tool(key_file, in_file, out_file):
	"""
	Runs the plonevote.decrypt tool and decrypts in_file into out_file.
	"""
	# Load the private key
	print "Loading private key..."
	try:
		private_key = PrivateKey.from_file(key_file)
	except InvalidPloneVoteCryptoFileError, e:
		print "Invalid private key file (%s): %s" % (key_file, e.msg)
		sys.exit(2)
	
	# Open the input encrypted data
	print "Loading encrypted data..."
	try:
		ciphertext = Ciphertext.from_file(in_file)
	except InvalidPloneVoteCryptoFileError, e:
		print "Invalid PloneVote encrypted file (%s): %s" % (key_file, e.msg)
		sys.exit(2)
	
	# Define callbacks for the TaskMonitor for monitoring the decryption process
	if(len(in_file) <= 50):
		short_in_filename = in_file
	else:
		short_in_filename = os.path.split(in_file)[-1]
		if(len(short_in_filename) > 50):
			# Do ellipsis shortening
			short_in_filename = short_in_filename[0,20] + "..." + \
								short_in_filename[-20,-1]
	
	def cb_task_percent_progress(task):
예제 #4
0
 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
예제 #5
0
    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
예제 #6
0
def run_tool(key_file, in_file, out_file):
    """
	Runs the plonevote.decrypt tool and decrypts in_file into out_file.
	"""
    # Load the private key
    print "Loading private key..."
    try:
        private_key = PrivateKey.from_file(key_file)
    except InvalidPloneVoteCryptoFileError, e:
        print "Invalid private key file (%s): %s" % (key_file, e.msg)
        sys.exit(2)

    # Open the input encrypted data
    print "Loading encrypted data..."
    try:
        ciphertext = Ciphertext.from_file(in_file)
    except InvalidPloneVoteCryptoFileError, e:
        print "Invalid PloneVote encrypted file (%s): %s" % (key_file, e.msg)
        sys.exit(2)

    # Define callbacks for the TaskMonitor for monitoring the decryption process
    if (len(in_file) <= 50):
        short_in_filename = in_file
    else:
        short_in_filename = os.path.split(in_file)[-1]
        if (len(short_in_filename) > 50):
            # Do ellipsis shortening
            short_in_filename = short_in_filename[0,20] + "..." + \
                 short_in_filename[-20,-1]

    def cb_task_percent_progress(task):