Example #1
0
 def test_get_fingerprint(self):
     """
     Test that PublicKey.get_fingerprint() always returns the same 
     fingerprint for the same PublicKey object and a different one for 
     another object.
     """
     # Create two public keys from the same cryptosystem
     public_key1 = self.cryptosystem.new_key_pair().public_key
     public_key2 = self.cryptosystem.new_key_pair().public_key
     
     # And a third from a different (insecure, 128bit) cryptosystem
     insecure_cryptosys = EGCryptoSystem.new(nbits=128)
     public_key3 = insecure_cryptosys.new_key_pair().public_key
     
     # Get each key's fingerprint
     pk1_fingerprint = public_key1.get_fingerprint()
     pk2_fingerprint = public_key2.get_fingerprint()
     pk3_fingerprint = public_key3.get_fingerprint()
     
     # Test that multiple calls to get_fingerprint() for the same PublicKey 
     # object always return the same fingerprint
     for i in range(0, 20):
         self.assertEqual(public_key1.get_fingerprint(), pk1_fingerprint)
         self.assertEqual(public_key2.get_fingerprint(), pk2_fingerprint)
         self.assertEqual(public_key3.get_fingerprint(), pk3_fingerprint)
         
     # Check that fingerprints from different PublicKey objects are 
     # different
     self.assertFalse(pk1_fingerprint == pk2_fingerprint)
     self.assertFalse(pk1_fingerprint == pk3_fingerprint)
     self.assertFalse(pk2_fingerprint == pk3_fingerprint)
Example #2
0
 def test_decryption_incompatible_cyphertext_error_cryptosys_bits(self):
     """
     Test that attempting to decrypt a ciphertext with a private key 
     generated using a cryptosystem of a different bit size (nbits) than the 
     one of the public key used to encrypt the ciphertext raises an 
     IncompatibleCiphertextError exception.
     """
     # For coverage completeness (A different error message is raised when 
     # the problem is an incompatible size for the cryptosystem than for any 
     # other incompatible private key).
     
     # Use the public key to encrypt the message
     ciphertext = self.public_key.encrypt_text(self.message)
     
     # Construct a new 128 bits (terribly unsafe) cryptosystem
     other_cryptosys = EGCryptoSystem.new(nbits=128)
     
     # Generate a new key pair using this cryptosystem and take its private 
     # key
     other_key_pair = other_cryptosys.new_key_pair()
     other_private_key = other_key_pair.private_key
     
     # This key should no be capable of decrypting the ciphertext and an 
     # error should be raised
     self.assertRaises(IncompatibleCiphertextError, 
                       other_private_key.decrypt_to_text, ciphertext)
def get_cryptosystem():
    """
    This function returns a predetermined EGCryptoSystem object.
    
    The EGCryptoSystem object is loaded from a resource file and guaranteed to 
    be always the same, at least for same execution of the test suite. The 
    cryptosystem is also cached in memory for quicker access.
    """
    global _cryptosys
    # Check if we have already loaded a cryptosystem for this test run
    if (_cryptosys == None):
        # If not, load it now:
        # Construct the path to the cryptosystem test resource file
        cryptosys_file = os.path.join(
            os.path.dirname(__file__),
            "TestThresholdEncryptionSetUp.resources",
            "test1024bits.pvcryptosys")

        # Load the cryptosystem from file
        _cryptosys = EGCryptoSystem.from_file(cryptosys_file)

    # Return the cached cryptosystem
    # (Note: this is the original reference, not a deepcopy, tests using the
    #  cryptosystem object should treat it as read-only to preserve isolation)
    return _cryptosys
    def test_commitmet_adding(self):    
        """
        Creates a new ThresholdEncryptionSetUp and try to add a trustee c
        commitment        
        """
        cryptosystem = get_cryptosystem()
        commitments = []     
           
        # Generate a new instance of ThresholdEncryptionSetUp
        tSetUp = ThresholdEncryptionSetUp(cryptosystem, 
                                          self.num_trustees, self.threshold)
                                         
        # Adding the keys from trustees
        for i in range(self.num_trustees):
            tSetUp.add_trustee_public_key(i, self.trustees[i].public_key)
        
        # Generate and add a valid commitment
        commitment = tSetUp.generate_commitment()   
        tSetUp.add_trustee_commitment(0, commitment)
        
        #The commitment for trustee 0 must be the same we just add
        self.assertEqual(tSetUp._trustees_commitments[0], commitment)
        
        # add_trustee_commitmnet must raise ValueError with invalid trustee  
        # values (-1 and self.num_trustees+1)
        self.assertRaises(ValueError, tSetUp.add_trustee_commitment,
                                     -1, commitment)
        self.assertRaises(ValueError, tSetUp.add_trustee_commitment, 
                                      self.num_trustees+1, commitment)
                                      
        # Change commitment.num_trustees value to try erros
        commitment.num_trustees = self.num_trustees +1
        # add_trustee_commitmnet must raise IncompatibleCommitmentError when
        # the commitment has diferent num_trustees value
        self.assertRaises(IncompatibleCommitmentError, 
                          tSetUp.add_trustee_commitment, 1, commitment)
                          

        # Change commitment.threshold value to try erros
        commitment.num_trustees = self.num_trustees
        commitment.threshold = self.threshold+1
        # add_trustee_commitmnet must raise IncompatibleCommitmentError when
        # the commitment has diferent theshold  value
        self.assertRaises(IncompatibleCommitmentError, 
                          tSetUp.add_trustee_commitment, 1, commitment)
                          
        # Test what happens with invalid cryptosystem
        # Create another cryptosystem
        second_cryptosys_file = os.path.join(os.path.dirname(__file__), 
                                      "TestThresholdEncryptionSetUp.resources",
                                      "test1024bits_second.pvcryptosys")
        # Load the cryptosystem from file
        second_cryptosys = EGCryptoSystem.from_file(second_cryptosys_file)
        commitment.threshold = self.threshold
        commitment.cryptosystem = second_cryptosys
        # Must raise IncompatibleCommitmentError with diferent cryptosys  
        self.assertRaises(IncompatibleCommitmentError, 
                          tSetUp.add_trustee_commitment, 1, commitment)
    def test_commitmet_adding(self):
        """
        Creates a new ThresholdEncryptionSetUp and try to add a trustee c
        commitment        
        """
        cryptosystem = get_cryptosystem()
        commitments = []

        # Generate a new instance of ThresholdEncryptionSetUp
        tSetUp = ThresholdEncryptionSetUp(cryptosystem, self.num_trustees,
                                          self.threshold)

        # Adding the keys from trustees
        for i in range(self.num_trustees):
            tSetUp.add_trustee_public_key(i, self.trustees[i].public_key)

        # Generate and add a valid commitment
        commitment = tSetUp.generate_commitment()
        tSetUp.add_trustee_commitment(0, commitment)

        #The commitment for trustee 0 must be the same we just add
        self.assertEqual(tSetUp._trustees_commitments[0], commitment)

        # add_trustee_commitmnet must raise ValueError with invalid trustee
        # values (-1 and self.num_trustees+1)
        self.assertRaises(ValueError, tSetUp.add_trustee_commitment, -1,
                          commitment)
        self.assertRaises(ValueError, tSetUp.add_trustee_commitment,
                          self.num_trustees + 1, commitment)

        # Change commitment.num_trustees value to try erros
        commitment.num_trustees = self.num_trustees + 1
        # add_trustee_commitmnet must raise IncompatibleCommitmentError when
        # the commitment has diferent num_trustees value
        self.assertRaises(IncompatibleCommitmentError,
                          tSetUp.add_trustee_commitment, 1, commitment)

        # Change commitment.threshold value to try erros
        commitment.num_trustees = self.num_trustees
        commitment.threshold = self.threshold + 1
        # add_trustee_commitmnet must raise IncompatibleCommitmentError when
        # the commitment has diferent theshold  value
        self.assertRaises(IncompatibleCommitmentError,
                          tSetUp.add_trustee_commitment, 1, commitment)

        # Test what happens with invalid cryptosystem
        # Create another cryptosystem
        second_cryptosys_file = os.path.join(
            os.path.dirname(__file__),
            "TestThresholdEncryptionSetUp.resources",
            "test1024bits_second.pvcryptosys")
        # Load the cryptosystem from file
        second_cryptosys = EGCryptoSystem.from_file(second_cryptosys_file)
        commitment.threshold = self.threshold
        commitment.cryptosystem = second_cryptosys
        # Must raise IncompatibleCommitmentError with diferent cryptosys
        self.assertRaises(IncompatibleCommitmentError,
                          tSetUp.add_trustee_commitment, 1, commitment)
def run_tool(nbits, filename, name, description):
    """
	Runs the plonevote.gen_cryptosys tool and generates a new cryptosystem.
	"""

    # Define callbacks for the TaskMonitor for progress monitoring
    def cb_task_start(task):
        print task.task_name + ":"

    def cb_task_progress(task):
        sys.stdout.write(".")
        sys.stdout.flush()

    def cb_task_end(task):
        print ""

    # Create new TaskMonitor and register the callbacks
    taskmon = TaskMonitor()
    taskmon.add_on_task_start_callback(cb_task_start)
    taskmon.add_on_tick_callback(cb_task_progress)
    taskmon.add_on_task_end_callback(cb_task_end)

    # Generate a new cryptosystem of the requested size
    try:
        cryptosys = EGCryptoSystem.new(nbits, task_monitor=taskmon)
    except KeyLengthTooLowError:
        print "ERROR: The given bit size does not meet PloneVoteCryptoLib "\
           "minimum security requirements (too short)."
    except KeyLengthNonBytableError:
        print "ERROR: The given bit size must be a multiple of 8."

    # Save the cryptosystem to file
    print "\nSaving cryptosystem to %s..." % filename,

    cryptosys.to_file(name, description, filename)

    print "SAVED.\n"
Example #7
0
def get_cryptosystem():
    """
    This function returns a predetermined EGCryptoSystem object.
    
    The EGCryptoSystem object is loaded from a resource file and guaranteed to 
    be always the same, at least for same execution of the test suite. The 
    cryptosystem is also cached in memory for quicker access.
    """
    global _cryptosys
    # Check if we have already loaded a cryptosystem for this test run
    if(_cryptosys == None):
        # If not, load it now:
        # Construct the path to the cryptosystem test resource file
        cryptosys_file = os.path.join(os.path.dirname(__file__), 
                                      "TestBasicEncryption.resources",
                                      "test1024bits.pvcryptosys")
        
        # Load the cryptosystem from file
        _cryptosys = EGCryptoSystem.from_file(cryptosys_file)
    
    # Return the cached cryptosystem 
    # (Note: this is the original reference, not a deepcopy, tests using the 
    #  cryptosystem object should treat it as read-only to preserve isolation)
    return _cryptosys
def run_tool(nbits, filename, name, description):
	"""
	Runs the plonevote.gen_cryptosys tool and generates a new cryptosystem.
	"""
	# Define callbacks for the TaskMonitor for progress monitoring
	def cb_task_start(task):
		print task.task_name + ":"

	def cb_task_progress(task):
		sys.stdout.write(".")
		sys.stdout.flush()

	def cb_task_end(task):
		print ""
	
	# Create new TaskMonitor and register the callbacks
	taskmon = TaskMonitor()
	taskmon.add_on_task_start_callback(cb_task_start)
	taskmon.add_on_tick_callback(cb_task_progress)
	taskmon.add_on_task_end_callback(cb_task_end)
	
	# Generate a new cryptosystem of the requested size
	try:
		cryptosys = EGCryptoSystem.new(nbits, task_monitor = taskmon)
	except KeyLengthTooLowError:
		print "ERROR: The given bit size does not meet PloneVoteCryptoLib "\
			  "minimum security requirements (too short)."
	except KeyLengthNonBytableError:
		print "ERROR: The given bit size must be a multiple of 8."
	
	# Save the cryptosystem to file
	print "\nSaving cryptosystem to %s..." % filename,
	
	cryptosys.to_file(name, description, filename)
	
	print "SAVED.\n"
    def test_partial_decryption(self):
        """
        Create a ciphertext with the threshold public key and decrypt it and 
        create others ciphertext to prove IncompatibleCiphertextError
        """

        tprk = self.tSetUp.generate_private_key(0, self.trustees[0].private_key)
        text_to_encrypt_dir = os.path.join(os.path.dirname(__file__), 
                                           "TestThresholdPrivateKey.resources")
        text_to_encrypt = os.path.join(text_to_encrypt_dir, "text_to_encrypt")
        text_encrypted = self.tpkey.encrypt_text(text_to_encrypt)
        
        # Decrypt the file created with our public key must be fine
        tprk.generate_partial_decryption(text_encrypted)
        
        # Create another ThresholdEcryptuonSetUp with other 1024 bits
        # cryptosys to create a cypthertext that cant be decrypted
        second_cryptosys_file = os.path.join(os.path.dirname(__file__), 
                                      "TestThresholdEncryptionSetUp.resources",
                                      "test1024bits_second.pvcryptosys")
        # Load the cryptosystem from file
        second_cryptosys = EGCryptoSystem.from_file(second_cryptosys_file)      
        secondtSetUp = ThresholdEncryptionSetUp(second_cryptosys, 
                                          self.num_trustees, self.threshold)
         # Adding the keys from trustees for 2ndsetUp
        for i in range(self.num_trustees):
            secondtSetUp.add_trustee_public_key(i, self.trustees[i].public_key)
        secondcommitments = []
        # Generate commitmes for trustees for 2ndsetUp
        for i in range(self.num_trustees):
            secondcommitments.append(secondtSetUp.generate_commitment()) 
        # Adding the secont trustees  commitments 
        for i in range(self.num_trustees):
            secondtSetUp.add_trustee_commitment(i, secondcommitments[i])
        # Generate secon cryptosis publickey
        secondtpkey = secondtSetUp.generate_public_key()
        # Encrypt the file with the secon cryptosis publickey
        secondtext_encrypted = secondtpkey.encrypt_text(text_to_encrypt)
        
        
        # Try to decryp something created with other ThresholdEcryptuonSetUp 
        # must raise IncompatibleCiphertextError
        
        self.assertRaises(IncompatibleCiphertextError, 
                         tprk.generate_partial_decryption, secondtext_encrypted)


        # Create another ThresholdEcryptuonSetUp with other 512 bits
        # cryptosys to create a cypthertext that cant be decrypted
        third_cryptosys_file = os.path.join(os.path.dirname(__file__), 
                                      "TestThresholdEncryptionSetUp.resources",
                                      "test512bits.pvcryptosys")
        # Load the cryptosystem from file
        third_cryptosys = EGCryptoSystem.from_file(third_cryptosys_file)      
        thirdtSetUp = ThresholdEncryptionSetUp(third_cryptosys, 
                                          self.num_trustees, self.threshold)
         # Adding the keys from trustees for 2ndsetUp
        for i in range(self.num_trustees):
            thirdtSetUp.add_trustee_public_key(i, self.trustees[i].public_key)
        thirdcommitments = []
        # Generate commitmes for trustees for 2ndsetUp
        for i in range(self.num_trustees):
            thirdcommitments.append(thirdtSetUp.generate_commitment()) 
        # Adding the secont trustees  commitments 
        for i in range(self.num_trustees):
            thirdtSetUp.add_trustee_commitment(i, thirdcommitments[i])
        # Generate secon cryptosis publickey
        thirdtpkey = thirdtSetUp.generate_public_key()
        # Encrypt the file with the secon cryptosis publickey
        thirdtext_encrypted = thirdtpkey.encrypt_text(text_to_encrypt)
        
        
        # Try to decryp something created with other ThresholdEcryptuonSetUp 
        # must raise IncompatibleCiphertextError
        
        self.assertRaises(IncompatibleCiphertextError, 
                         tprk.generate_partial_decryption, thirdtext_encrypted)
Example #10
0
class PrivateKey:
    """
    An ElGamal private key object used for decryption.
    
    Attributes:
        cryptosystem::EGCryptoSystem    -- The ElGamal cryptosystem in which 
                                           this key is defined.
        public_key::PublicKey    -- The associated public key.
    """
    def __eq__(self, other):
        """
        Implements PrivateKey equality.
        """
        if (isinstance(other, PrivateKey)
                and (other.cryptosystem == self.cryptosystem)
                and (other.public_key == self.public_key)
                and (other._key == self._key)):
            return True
        else:
            return False

    def __ne__(self, other):
        """
        Implements PrivateKey inequality.
        """
        return not self.__eq__(other)

    def __init__(self, cryptosystem, private_key_value):
        """
        Creates a new private key. Should not be invoked directly.
        
        Instead of using this constructor from outside of PloneVoteCryptoLib, 
        please use the class methods EGCryptoSystem.new_key_pair() or 
        PrivateKey.from_file(file).
        
        Arguments:
            cryptosystem::EGCryptoSystem-- The ElGamal cryptosystem in which 
                                           this key is defined.
            private_key_value::long     -- The actual value of the private key.
        """
        public_key_value = pow(cryptosystem.get_generator(), private_key_value,
                               cryptosystem.get_prime())

        self.cryptosystem = cryptosystem
        self.public_key = PublicKey(cryptosystem, public_key_value)
        self._key = private_key_value

    def decrypt_to_bitstream(self, ciphertext, task_monitor=None, force=False):
        """
        Decrypts the given ciphertext into a bitstream.
        
        If the bitstream was originally encrypted with PublicKey.encrypt_X(), 
        then this method returns a bitstream following the format described 
        in Note 001 of the Ciphertext.py file:
            [size (64 bits) | message (size bits) | padding (X bits) ]
        
        Arguments:
            ciphertext::Ciphertext    -- An encrypted Ciphertext object.
            task_monitor::TaskMonitor    -- A task monitor for this task.
            force:bool    -- Set this to true if you wish to force a decryption 
                           attempt, even when the ciphertext's stored public key
                           fingerprint does not match that of the public key 
                           associated with this private key.
        
        Returns:
            bitstream::Bitstream    -- A bitstream containing the unencrypted 
                                       data.
        
        Throws:
            IncompatibleCiphertextError -- The given ciphertext does not appear 
                                           to be decryptable with the selected 
                                           private key.
        """
        # Check that the public key fingerprint stored in the ciphertext
        # matches the public key associated with this private key.
        if (not force):
            if (ciphertext.nbits != self.cryptosystem.get_nbits()):
                raise IncompatibleCiphertextError("The given ciphertext is " \
                        "not decryptable with the selected private key: " \
                        "incompatible cryptosystem/key sizes.")

            if (ciphertext.pk_fingerprint !=
                    self.public_key.get_fingerprint()):
                raise IncompatibleCiphertextError("The given ciphertext is " \
                        "not decryptable with the selected private key: " \
                        "public key fingerprint mismatch.")

        # We read and decrypt the ciphertext block by block
        # See "Handbook of Applied Cryptography" Algorithm 8.18
        bitstream = BitStream()

        block_size = self.cryptosystem.get_nbits() - 1
        prime = self.cryptosystem.get_prime()
        key = self._key

        # Check if we have a task monitor and register with it
        if (task_monitor != None):
            # One tick per block
            ticks = ciphertext.get_length()
            decrypt_task_mon = \
                task_monitor.new_subtask("Decrypt data", expected_ticks = ticks)

        for gamma, delta in ciphertext:
            assert max(gamma, delta) < 2**(block_size + 1), \
                "The ciphertext object includes blocks larger than the " \
                "expected block size."
            m = (pow(gamma, prime - 1 - key, prime) * delta) % prime
            bitstream.put_num(m, block_size)

            if (task_monitor != None): decrypt_task_mon.tick()

        return bitstream

    def decrypt_to_text(self, ciphertext, task_monitor=None, force=False):
        """
        Decrypts the given ciphertext into its text contents as a string
        
        This method assumes that the ciphertext contains an encrypted stream of 
        data in the format of Note 001 of the Ciphertext.py file, were message 
        contains string information (as opposed to a binary format).
            [size (64 bits) | message (size bits) | padding (X bits) ]
        
        Arguments:
            ciphertext::Ciphertext    -- An encrypted Ciphertext object, 
                                       containing data in the above format.
            task_monitor::TaskMonitor    -- A task monitor for this task.
            force:bool    -- Set to true if you wish to force a decryption 
                           attempt, even when the ciphertext's stored public 
                           key fingerprint does not match that of the public 
                           key associated with this private key.
        
        Returns:
            string::string    -- Decrypted message as a string.
        
        Throws:
            IncompatibleCiphertextError -- The given ciphertext does not appear 
                                           to be decryptable with the selected 
                                           private key.
        """
        bitstream = self.decrypt_to_bitstream(ciphertext, task_monitor, force)
        bitstream.seek(0)
        length = bitstream.get_num(64)
        return bitstream.get_string(length)

    def to_file(self, filename, SerializerClass=serialize.XMLSerializer):
        """
        Saves this private key to a file.
        
        Arguments:
            filename::string    -- The path to the file in which to store the 
                                   serialized PrivateKey object.
            SerializerClass::class --
                The class that provides the serialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate serialize_to_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        """
        # Create a new serializer object for the PrivateKey structure definition
        serializer = SerializerClass(PrivateKey_serialize_structure_definition)

        # Helper function to translate large numbers to their hexadecimal
        # string representation
        def num_to_hex_str(num):
            hex_str = hex(num)[2:]  # Remove leading '0x'
            if (hex_str[-1] == 'L'):
                hex_str = hex_str[0:-1]  # Remove trailing 'L'
            return hex_str

        # Generate a serializable data dictionary matching the definition:
        prime_str = num_to_hex_str(self.cryptosystem.get_prime())
        generator_str = num_to_hex_str(self.cryptosystem.get_generator())
        data = {
            "PloneVotePrivateKey": {
                "PrivateKey": num_to_hex_str(self._key),
                "CryptoSystemScheme": {
                    "nbits": str(self.cryptosystem.get_nbits()),
                    "prime": prime_str,
                    "generator": generator_str
                }
            }
        }

        # Use the serializer to store the data to file
        serializer.serialize_to_file(filename, data)

    @classmethod
    def from_file(cls, filename, SerializerClass=serialize.XMLSerializer):
        """
        Loads an instance of PrivateKey from the given file.
        
        Arguments:
            filename::string    -- The name of a file containing the private 
                                   key in serialized form.
            SerializerClass::class --
                The class that provides the deserialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate deserialize_from_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        
        Throws:
            InvalidPloneVoteCryptoFileError -- If the file is not a valid 
                                               PloneVoteCryptoLib stored 
                                               private key file.
        """
        # Create a new serializer object for the PrivateKey structure definition
        serializer = SerializerClass(PrivateKey_serialize_structure_definition)

        # Deserialize the PrivateKey instance from file
        try:
            data = serializer.deserialize_from_file(filename)
        except serialize.InvalidSerializeDataError, e:
            # Convert the exception to an InvalidPloneVoteCryptoFileError
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid private key. The " \
                "following error occurred while trying to deserialize the " \
                "file contents: %s" % (filename, str(e)))

        # Helper function to decode numbers from strings and
        # raise an exception if the string is not a valid number.
        # (value_name is used only to construct the exception string).
        def str_to_num(num_str, base, value_name):
            try:
                return int(num_str, base)
            except ValueError:
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid private key. The " \
                    "stored value for %s is not a valid integer in " \
                    "base %d representation." % (filename, value_name, base))

        # Get the values from the deserialized data
        inner_elems = data["PloneVotePrivateKey"]["CryptoSystemScheme"]
        nbits = str_to_num(inner_elems["nbits"], 10, "nbits")
        prime = str_to_num(inner_elems["prime"], 16, "prime")
        generator = str_to_num(inner_elems["generator"], 16, "generator")

        priv_key = str_to_num(data["PloneVotePrivateKey"]["PrivateKey"], 16,
                              "PrivateKey")

        # Check the loaded values
        if (not (1 <= priv_key <= prime - 2)):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid private key. The value " \
                "of the private key given in the file does not match the " \
                "indicated cryptosystem. Could the file be corrupt?" % filename)

        # Construct the cryptosystem object
        cryptosystem = EGCryptoSystem.load(nbits, prime, generator)

        # Construct and return the PrivateKey object
        return cls(cryptosystem, priv_key)
Example #11
0
    def from_file(cls, filename, SerializerClass=serialize.XMLSerializer):
        """
        Loads an instance of PrivateKey from the given file.
        
        Arguments:
            filename::string    -- The name of a file containing the private 
                                   key in serialized form.
            SerializerClass::class --
                The class that provides the deserialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate deserialize_from_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        
        Throws:
            InvalidPloneVoteCryptoFileError -- If the file is not a valid 
                                               PloneVoteCryptoLib stored 
                                               private key file.
        """
        # Create a new serializer object for the PrivateKey structure definition
        serializer = SerializerClass(PrivateKey_serialize_structure_definition)

        # Deserialize the PrivateKey instance from file
        try:
            data = serializer.deserialize_from_file(filename)
        except serialize.InvalidSerializeDataError as e:
            # Convert the exception to an InvalidPloneVoteCryptoFileError
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid private key. The " \
                "following error occurred while trying to deserialize the " \
                "file contents: %s" % (filename, str(e)))

        # Helper function to decode numbers from strings and
        # raise an exception if the string is not a valid number.
        # (value_name is used only to construct the exception string).
        def str_to_num(num_str, base, value_name):
            try:
                return int(num_str, base)
            except ValueError:
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid private key. The " \
                    "stored value for %s is not a valid integer in " \
                    "base %d representation." % (filename, value_name, base))

        # Get the values from the deserialized data
        inner_elems = data["PloneVotePrivateKey"]["CryptoSystemScheme"]
        nbits = str_to_num(inner_elems["nbits"], 10, "nbits")
        prime = str_to_num(inner_elems["prime"], 16, "prime")
        generator = str_to_num(inner_elems["generator"], 16, "generator")

        priv_key = str_to_num(data["PloneVotePrivateKey"]["PrivateKey"], 16,
                              "PrivateKey")

        # Check the loaded values
        if (not (1 <= priv_key <= prime - 2)):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid private key. The value " \
                "of the private key given in the file does not match the " \
                "indicated cryptosystem. Could the file be corrupt?" % filename)

        # Construct the cryptosystem object
        cryptosystem = EGCryptoSystem.load(nbits, prime, generator)

        # Construct and return the PrivateKey object
        return cls(cryptosystem, priv_key)
Example #12
0
class ThresholdPrivateKey:
    """
    A private key generated in a threshold encryption scheme.
    
    Multiple threshold private keys are required in order to decrypt a 
    ciphertext encrypted in a threshold encryption scheme. Because of this, the 
    interface and usage of this class is significantly different from that of 
    PrivateKey (which is why this class is not a subclass of PrivateKey).
    
    Note that multiple threshold private keys are associated with each 
    threshold public key, one for each trustee. This again in contrast with 
    simple private/public keys which are paired.
    
    Attributes:
        cryptosystem::EGCryptoSystem    -- The ElGamal cryptosystem in which 
                                           this key is defined.
        num_trustees::int    -- Total number of trustees in the threshold scheme.
                               (the n in "k of n"-decryption)
        threshold::int    -- Minimum number of trustees required to decrypt 
                           threshold  encrypted messages. 
                           (the k in "k of n"-decryption)
        public_key::ThresholdPublicKey    -- The threshold public key to which 
                                           this threshold private key is 
                                           associated.
    """
    def __init__(self, cryptosystem, num_trustees, threshold,
                 threshold_public_key, private_key_value):
        """
        Creates a new threshold private key. Should not be invoked directly.
        
        Instead of using this constructor from outside of PloneVoteCryptoLib, 
        please use ThresholdEncryptionSetUp.generate_private_key() or 
        ThresholdEncryptionSetUp.generate_key_pair().
        
        Arguments:
            (see class attributes for cryptosystem, num_trustees and threshold)
            threshold_public_key::ThresholdPublicKey    -- 
                                The threshold public key to which this 
                                threshold private key is associated.
            private_key_value::long        -- The actual value of the private key
                            (P(j) for trustee j, see ThresholdEncryptionSetUp)
        """
        self.cryptosystem = cryptosystem
        self.num_trustees = num_trustees
        self.threshold = threshold
        self.public_key = threshold_public_key
        self._key = private_key_value

    def generate_partial_decryption(self,
                                    ciphertext,
                                    task_monitor=None,
                                    force=False):
        """
        Generates a partial decryption for the given ciphertext.
        
        Arguments:
            ciphertext::Ciphertext    -- An encrypted Ciphertext object.
            task_monitor::TaskMonitor    -- A task monitor for this task.
            force:bool    -- Set this to true if you wish to force a decryption 
                           attempt, even when the ciphertext's stored public key
                           fingerprint does not match that of the public key 
                           associated with this private key.
        
        Returns:
            partial_decryption::PartialDecryption    -- A partial decryption of 
                                                       the given ciphertext 
                                                       generated with this 
                                                       threshold private key.
        
        Throws:
            IncompatibleCiphertextError -- The given ciphertext does not appear 
                                           to be decryptable with the selected 
                                           private key.
        """
        # Check that the public key fingerprint stored in the ciphertext
        # matches the public key associated with this private key.
        if (not force):
            if (ciphertext.nbits != self.cryptosystem.get_nbits()):
                raise IncompatibleCiphertextError("The given ciphertext is " \
                        "not decryptable with the selected private key: " \
                        "incompatible cryptosystem/key sizes.")

            if (ciphertext.pk_fingerprint !=
                    self.public_key.get_fingerprint()):
                raise IncompatibleCiphertextError("The given ciphertext is " \
                        "not decryptable with the selected private key: " \
                        "public key fingerprint mismatch.")

        nbits = self.cryptosystem.get_nbits()
        prime = self.cryptosystem.get_prime()
        generator = self.cryptosystem.get_generator()
        key = self._key

        # Remember that prime is of the form p = 2*q + 1, with q prime.
        # (By construction, see EGCryptoSystem)
        q = (prime - 1) / 2

        # We will need a random number generator for the proofs of partial
        # decryption.
        random = StrongRandom()

        # New empty partial decryption
        partial_decryption = PartialDecryption(nbits)

        # Check if we have a task monitor and register with it
        if (task_monitor != None):
            # One tick per block
            ticks = ciphertext.get_length()
            partial_decrypt_task_mon = \
                task_monitor.new_subtask("Generate partial decryption",
                                         expected_ticks = ticks)

        # For each (gamma,delta) component in the ciphertext, generate one
        # partial decryption block (with proof):
        for gamma, delta in ciphertext:

            # To calculate the value of the block, elevate gamma to the
            # threshold private key. That is block.value = g^{rP(i)} for each
            # nbits block of original plaintext.
            value = pow(gamma, key, prime)

            # Generate the partial decryption proof for the block as a
            # Zero-Knowledge Discrete Logarithm Equality Test for
            # log_{g}(g^{2P(j)}) == log_{gamma}(block^2)
            # (See PartialDecryptionBlockProof and [TODO: Add reference] for
            # more information.)

            # Select a random s in Z_{q}^{*}
            s = random.randint(1, q - 1)

            # a = g^{s} mod p
            a = pow(generator, s, prime)

            # b = gamma^{s} mod p
            b = pow(gamma, s, prime)

            # c is SHA256(a, b, g^{2*P(j)}, block.value) the challenge
            # (We must use g^{2*P(j)} and not g^{P(j)}, because the first is
            # considered as the partial public key of trustee j and the value
            # of the later is unavailable at decryption combination time).
            sha256 = Crypto.Hash.SHA256.new()
            sha256.update(hex(a))
            sha256.update(hex(b))
            sha256.update(hex(pow(generator, 2 * key, prime)))
            sha256.update(hex(value))
            c = int(sha256.hexdigest(), 16)

            # t = s + 2P(j)*c mod p-1 (P(j): trustee j's threshold private key)
            # (p - 1 since it is in the exponent and we are already adding the 2
            # factor in 2P(j))
            t = (s + 2 * key * c) % (prime - 1)

            # Generate the PartialDecryptionBlockProof as (a, b, t)
            proof = PartialDecryptionBlockProof(a, b, t)

            # Generate the block as (value, proof) and add it to the partial
            # decryption object.
            block = PartialDecryptionBlock(value, proof)
            partial_decryption.add_partial_decryption_block(block)

            # Update task progress
            if (task_monitor != None): partial_decrypt_task_mon.tick()

        return partial_decryption

    def to_file(self, filename, SerializerClass=serialize.XMLSerializer):
        """
        Saves this threshold private key to a file.
        
        Arguments:
            filename::string    -- The path to the file in which to store the 
                                   serialized ThresholdPrivateKey object.
            SerializerClass::class --
                The class that provides the serialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate serialize_to_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        """
        # Create a new serializer object for the PrivateKey structure definition
        serializer = \
             SerializerClass(ThresholdPrivateKey_serialize_structure_definition)

        # Helper function to translate large numbers to their hexadecimal
        # string representation
        def num_to_hex_str(num):
            hex_str = hex(num)[2:]  # Remove leading '0x'
            if (hex_str[-1] == 'L'):
                hex_str = hex_str[0:-1]  # Remove trailing 'L'
            return hex_str

        # Generate a serializable data dictionary matching the definition:
        prime_str = num_to_hex_str(self.cryptosystem.get_prime())
        generator_str = num_to_hex_str(self.cryptosystem.get_generator())

        verification_data_list = []
        for i in range(self.num_trustees):
            verification_pk_data = {
                "key": num_to_hex_str(self.public_key._partial_public_keys[i]),
                "trustee": str(i)
            }
            verification_data_list.append(verification_pk_data)

        data = {
            "PloneVoteThresholdPrivateKey": {
                "PrivateKey": num_to_hex_str(self._key),
                "CryptoSystemScheme": {
                    "nbits": str(self.cryptosystem.get_nbits()),
                    "prime": prime_str,
                    "generator": generator_str
                },
                "ThresholdKeyInfo": {
                    "NumTrustees": str(self.num_trustees),
                    "Threshold": str(self.threshold),
                    "ThresholdPublicKey": num_to_hex_str(self.public_key._key),
                    "PartialPublicKey": verification_data_list
                }
            }
        }

        # Use the serializer to store the data to file
        serializer.serialize_to_file(filename, data)

    @classmethod
    def from_file(cls, filename, SerializerClass=serialize.XMLSerializer):
        """
        Loads an instance of ThresholdPrivateKey from the given file.
        
        Arguments:
            filename::string    -- The name of a file containing the threshold 
                                   public key in serialized form.
            SerializerClass::class --
                The class that provides the deserialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate deserialize_from_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        
        Throws:
            InvalidPloneVoteCryptoFileError -- If the file is not a valid 
                                               PloneVoteCryptoLib stored 
                                               public key file.
        """
        # Create a new serializer object for the PublicKey structure definition
        serializer = SerializerClass(
            ThresholdPrivateKey_serialize_structure_definition)

        # Deserialize the ThresholdPrivateKey instance from file
        try:
            data = serializer.deserialize_from_file(filename)
        except serialize.InvalidSerializeDataError, e:
            # Convert the exception to an InvalidPloneVoteCryptoFileError
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid threshold private key. " \
                "The following error occurred while trying to deserialize " \
                "the file contents: %s" % (filename, str(e)))

        # Helper function to decode numbers from strings and
        # raise an exception if the string is not a valid number.
        # (value_name is used only to construct the exception string).
        def str_to_num(num_str, base, value_name):
            try:
                return int(num_str, base)
            except ValueError:
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid threshold private " \
                    "key. The stored value for %s is not a valid integer in " \
                    "base %d representation." % (filename, value_name, base))

        # Get the values from the deserialized data
        inner_elems = data["PloneVoteThresholdPrivateKey"][
            "CryptoSystemScheme"]
        nbits = str_to_num(inner_elems["nbits"], 10, "nbits")
        prime = str_to_num(inner_elems["prime"], 16, "prime")
        generator = str_to_num(inner_elems["generator"], 16, "generator")

        prv_key = str_to_num(
            data["PloneVoteThresholdPrivateKey"]["PrivateKey"], 16,
            "PrivateKey")
        threshold_info = \
                        data["PloneVoteThresholdPrivateKey"]["ThresholdKeyInfo"]
        num_trustees = \
                    str_to_num(threshold_info["NumTrustees"], 10, "NumTrustees")
        threshold = \
                    str_to_num(threshold_info["Threshold"], 10, "Threshold")
        pub_key = str_to_num(threshold_info["ThresholdPublicKey"], 16,
                             "PublicKey")
        pp_keys = threshold_info["PartialPublicKey"]
        partial_public_keys = [None for o in pp_keys]
        for pp_key in pp_keys:
            trustee = str_to_num(pp_key["trustee"], 10, "trustee")
            key_val = str_to_num(pp_key["key"], 16, "key")
            partial_public_keys[trustee] = key_val

        # Check the loaded values
        if (not (1 <= prv_key <= prime - 2)):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid threshold private key."\
                "The value of the private key given in the file does" \
                "not match the indicated cryptosystem. Could the file be" \
                "corrupt?" % filename)

        if (not (1 <= pub_key <= prime - 2)):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid threshold private key."\
                "The value of the threshold public key given in the file does" \
                "not match the indicated cryptosystem. Could the file be" \
                "corrupt?" % filename)

        for pp_key in partial_public_keys:
            if (not (1 <= pp_key <= prime - 2)):
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid threshold private "\
                    "key. The value of at least one of the partial public  " \
                    "keys given in the file does not match the indicated " \
                    "cryptosystem.  Could the file be corrupt?" % filename)

        # Construct the cryptosystem object
        cryptosystem = EGCryptoSystem.load(nbits, prime, generator)

        # Contruct the Threshold Public Key
        threshold_public_key = ThresholdPublicKey(cryptosystem, num_trustees,
                                                  threshold, pub_key,
                                                  partial_public_keys)

        # Construct and return the ThresholdPrivateKey object

        return cls(cryptosystem, num_trustees, threshold, threshold_public_key,
                   prv_key)
Example #13
0
class PublicKey:
    """
    An ElGamal public key object used for encryption.
    
    Attributes:
        cryptosystem::EGCryptoSystem    -- The ElGamal cryptosystem in which 
                                           this key is defined.
    """
    def get_fingerprint(self):
        """
        Gets a fingerprint of the current public key.
        
        This fingerprint should be stored with any text encrypted with this 
        public key, in order to facilitate checking compatibility with a 
        particular key pair for future decryption or manipulation.
        
        Returns:
            fingerprint::string -- A SHA256 hexdigest providing a fingerprint 
                                   of the current public key.
        """
        fingerprint = Crypto.Hash.SHA256.new()
        fingerprint.update(hex(self.cryptosystem.get_nbits()))
        fingerprint.update(hex(self.cryptosystem.get_prime()))
        fingerprint.update(hex(self.cryptosystem.get_generator()))
        fingerprint.update(hex(self._key))
        return fingerprint.hexdigest()

    def __eq__(self, other):
        """
        Implements PublicKey equality.
        """
        if (isinstance(other, PublicKey)
                and (other.cryptosystem == self.cryptosystem)
                and (other._key == self._key)):
            return True
        else:
            return False

    def __ne__(self, other):
        """
        Implements PublicKey inequality.
        """
        return not self.__eq__(other)

    def __init__(self, cryptosystem, public_key_value):
        """
        Creates a new public key. Should not be invoked directly.
        
        Instead of using this constructor from outside of PloneVoteCryptoLib, 
        please use the class methods EGCryptoSystem.new_key_pair() or 
        PublicKey.from_file(file).
        
        Arguments:
            cryptosystem::EGCryptoSystem-- The ElGamal cryptosystem in which 
                                           this key is defined.
            public_key_value::long        -- The actual value of the public key
                                           (g^a mod p, where a is the priv. key)
        """
        self.cryptosystem = cryptosystem
        self._key = public_key_value

    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_text(self, text, pad_to=None, task_monitor=None):
        """
        Encrypts the given string into a ciphertext object.
        
        Arguments:
            text::string            -- A string to encrypt.
            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.
        """
        bitstream = BitStream()
        bitstream.put_string(text)
        return self.encrypt_bitstream(bitstream, pad_to, task_monitor)

    def to_file(self, filename, SerializerClass=serialize.XMLSerializer):
        """
        Saves this public key to a file.
        
        Arguments:
            filename::string    -- The path to the file in which to store the 
                                   serialized PublicKey object.
            SerializerClass::class --
                The class that provides the serialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate serialize_to_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        """
        # Create a new serializer object for the PublicKey structure definition
        serializer = SerializerClass(PublicKey_serialize_structure_definition)

        # Helper function to translate large numbers to their hexadecimal
        # string representation
        def num_to_hex_str(num):
            hex_str = hex(num)[2:]  # Remove leading '0x'
            if (hex_str[-1] == 'L'):
                hex_str = hex_str[0:-1]  # Remove trailing 'L'
            return hex_str

        # Generate a serializable data dictionary matching the definition:
        prime_str = num_to_hex_str(self.cryptosystem.get_prime())
        generator_str = num_to_hex_str(self.cryptosystem.get_generator())
        data = {
            "PloneVotePublicKey": {
                "PublicKey": num_to_hex_str(self._key),
                "CryptoSystemScheme": {
                    "nbits": str(self.cryptosystem.get_nbits()),
                    "prime": prime_str,
                    "generator": generator_str
                }
            }
        }

        # Use the serializer to store the data to file
        serializer.serialize_to_file(filename, data)

    @classmethod
    def from_file(cls, filename, SerializerClass=serialize.XMLSerializer):
        """
        Loads an instance of PublicKey from the given file.
        
        Arguments:
            filename::string    -- The name of a file containing the public 
                                   key in serialized form.
            SerializerClass::class --
                The class that provides the deserialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate deserialize_from_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        
        Throws:
            InvalidPloneVoteCryptoFileError -- If the file is not a valid 
                                               PloneVoteCryptoLib stored 
                                               public key file.
        """
        # Create a new serializer object for the PublicKey structure definition
        serializer = SerializerClass(PublicKey_serialize_structure_definition)

        # Deserialize the PublicKey instance from file
        try:
            data = serializer.deserialize_from_file(filename)
        except serialize.InvalidSerializeDataError, e:
            # Convert the exception to an InvalidPloneVoteCryptoFileError
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid public key. The " \
                "following error occurred while trying to deserialize the " \
                "file contents: %s" % (filename, str(e)))

        # Verify that we are dealing with a single public key and not a
        # threshold public key. In the later case, call
        # ThresholdPublicKey.from_file on the given file, instead of this
        # method.
        if (data["PloneVotePublicKey"].has_key("ThresholdKeyInfo")):
            from plonevotecryptolib.Threshold.ThresholdPublicKey import \
                                              ThresholdPublicKey
            return ThresholdPublicKey.from_file(filename, SerializerClass)

        # Helper function to decode numbers from strings and
        # raise an exception if the string is not a valid number.
        # (value_name is used only to construct the exception string).
        def str_to_num(num_str, base, value_name):
            try:
                return int(num_str, base)
            except ValueError:
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid public key. The " \
                    "stored value for %s is not a valid integer in " \
                    "base %d representation." % (filename, value_name, base))

        # Get the values from the deserialized data
        inner_elems = data["PloneVotePublicKey"]["CryptoSystemScheme"]
        nbits = str_to_num(inner_elems["nbits"], 10, "nbits")
        prime = str_to_num(inner_elems["prime"], 16, "prime")
        generator = str_to_num(inner_elems["generator"], 16, "generator")

        pub_key = str_to_num(data["PloneVotePublicKey"]["PublicKey"], 16,
                             "PublicKey")

        # Check the loaded values
        if (not (1 <= pub_key <= prime - 2)):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid public key. The value " \
                "of the public key given in the file does not match the " \
                "indicated cryptosystem. Could the file be corrupt?" % filename)

        # Construct the cryptosystem object
        cryptosystem = EGCryptoSystem.load(nbits, prime, generator)

        # Construct and return the PublicKey object
        return cls(cryptosystem, pub_key)
    def test_partial_decryption(self):
        """
        Create a ciphertext with the threshold public key and decrypt it and 
        create others ciphertext to prove IncompatibleCiphertextError
        """

        tprk = self.tSetUp.generate_private_key(0,
                                                self.trustees[0].private_key)
        text_to_encrypt_dir = os.path.join(
            os.path.dirname(__file__), "TestThresholdPrivateKey.resources")
        text_to_encrypt = os.path.join(text_to_encrypt_dir, "text_to_encrypt")
        text_encrypted = self.tpkey.encrypt_text(text_to_encrypt)

        # Decrypt the file created with our public key must be fine
        tprk.generate_partial_decryption(text_encrypted)

        # Create another ThresholdEcryptuonSetUp with other 1024 bits
        # cryptosys to create a cypthertext that cant be decrypted
        second_cryptosys_file = os.path.join(
            os.path.dirname(__file__),
            "TestThresholdEncryptionSetUp.resources",
            "test1024bits_second.pvcryptosys")
        # Load the cryptosystem from file
        second_cryptosys = EGCryptoSystem.from_file(second_cryptosys_file)
        secondtSetUp = ThresholdEncryptionSetUp(second_cryptosys,
                                                self.num_trustees,
                                                self.threshold)
        # Adding the keys from trustees for 2ndsetUp
        for i in range(self.num_trustees):
            secondtSetUp.add_trustee_public_key(i, self.trustees[i].public_key)
        secondcommitments = []
        # Generate commitmes for trustees for 2ndsetUp
        for i in range(self.num_trustees):
            secondcommitments.append(secondtSetUp.generate_commitment())
        # Adding the secont trustees  commitments
        for i in range(self.num_trustees):
            secondtSetUp.add_trustee_commitment(i, secondcommitments[i])
        # Generate secon cryptosis publickey
        secondtpkey = secondtSetUp.generate_public_key()
        # Encrypt the file with the secon cryptosis publickey
        secondtext_encrypted = secondtpkey.encrypt_text(text_to_encrypt)

        # Try to decryp something created with other ThresholdEcryptuonSetUp
        # must raise IncompatibleCiphertextError

        self.assertRaises(IncompatibleCiphertextError,
                          tprk.generate_partial_decryption,
                          secondtext_encrypted)

        # Create another ThresholdEcryptuonSetUp with other 512 bits
        # cryptosys to create a cypthertext that cant be decrypted
        third_cryptosys_file = os.path.join(
            os.path.dirname(__file__),
            "TestThresholdEncryptionSetUp.resources",
            "test512bits.pvcryptosys")
        # Load the cryptosystem from file
        third_cryptosys = EGCryptoSystem.from_file(third_cryptosys_file)
        thirdtSetUp = ThresholdEncryptionSetUp(third_cryptosys,
                                               self.num_trustees,
                                               self.threshold)
        # Adding the keys from trustees for 2ndsetUp
        for i in range(self.num_trustees):
            thirdtSetUp.add_trustee_public_key(i, self.trustees[i].public_key)
        thirdcommitments = []
        # Generate commitmes for trustees for 2ndsetUp
        for i in range(self.num_trustees):
            thirdcommitments.append(thirdtSetUp.generate_commitment())
        # Adding the secont trustees  commitments
        for i in range(self.num_trustees):
            thirdtSetUp.add_trustee_commitment(i, thirdcommitments[i])
        # Generate secon cryptosis publickey
        thirdtpkey = thirdtSetUp.generate_public_key()
        # Encrypt the file with the secon cryptosis publickey
        thirdtext_encrypted = thirdtpkey.encrypt_text(text_to_encrypt)

        # Try to decryp something created with other ThresholdEcryptuonSetUp
        # must raise IncompatibleCiphertextError

        self.assertRaises(IncompatibleCiphertextError,
                          tprk.generate_partial_decryption,
                          thirdtext_encrypted)
Example #15
0
# -*- coding: utf-8 -*-
# ToDo: Turn this into an actual test case / doctest

from __future__ import absolute_import
from __future__ import print_function
from plonevotecryptolib.EGCryptoSystem import EGCryptoSystem as egcs
from plonevotecryptolib.Threshold.ThresholdEncryptionSetUp import ThresholdEncryptionSetUp as tesu
from plonevotecryptolib.Threshold.ThresholdDecryptionCombinator import ThresholdDecryptionCombinator
from six.moves import range

cs = egcs.new()

class Trustee:
	def __init__(self, cs):
		kp = cs.new_key_pair()
		self.private_key = kp.private_key
		self.public_key = kp.public_key
		self.commitment = None
		self.tesu_fingerprint = None
		self.threshold_public_key = None
		self.threshold_private_key = None


trustees = [Trustee(cs) for i in range(0,5)]

tesetup = tesu(cs, 5, 3)

for i in range(0,5):
	tesetup.add_trustee_public_key(i, trustees[i].public_key)

for i in range(0,5):
Example #16
0
# -*- coding: utf-8 -*-
# ToDo: Turn this into an actual test case / doctest

from plonevotecryptolib.EGCryptoSystem import EGCryptoSystem as egcs
from plonevotecryptolib.Threshold.ThresholdEncryptionSetUp import ThresholdEncryptionSetUp as tesu
from plonevotecryptolib.Threshold.ThresholdDecryptionCombinator import ThresholdDecryptionCombinator

cs = egcs.new()

class Trustee:
	def __init__(self, cs):
		kp = cs.new_key_pair()
		self.private_key = kp.private_key
		self.public_key = kp.public_key
		self.commitment = None
		self.tesu_fingerprint = None
		self.threshold_public_key = None
		self.threshold_private_key = None


trustees = [Trustee(cs) for i in range(0,5)]

tesetup = tesu(cs, 5, 3)

for i in range(0,5):
	tesetup.add_trustee_public_key(i, trustees[i].public_key)

for i in range(0,5):
	trustees[i].commitment = tesetup.generate_commitment()

for i in range(0,5):
Example #17
0
class ThresholdPublicKey(PublicKey):
    """
    A public key generated in a threshold encryption scheme.
    
    This class is compatible with the PublicKey class and inherits from it.
    It adds some metadata about the threshold encryption scheme and changes its 
    format on file slightly, but otherwise it presents the same methods and 
    properties that PublicKey and can be used to encrypt data without any 
    knowledge of the threshold decryption or key set-up process.
    
    Attributes:
        cryptosystem::EGCryptoSystem    -- The ElGamal cryptosystem in which 
                                           this key is defined.
        num_trustees::int    -- Total number of trustees in the threshold scheme.
                               (the n in "k of n"-decryption)
        threshold::int    -- Minimum number of trustees required to decrypt 
                           threshold  encrypted messages. 
                           (the k in "k of n"-decryption)
    """
    def get_fingerprint(self):
        # We override this PublicKey method to add partial public keys to the
        # input of the hash function to create the fingerprint.
        fingerprint = Crypto.Hash.SHA256.new()
        fingerprint.update(hex(self.cryptosystem.get_nbits()))
        fingerprint.update(hex(self.cryptosystem.get_prime()))
        fingerprint.update(hex(self.cryptosystem.get_generator()))
        fingerprint.update(hex(self._key))
        for partial_public_key in self._partial_public_keys:
            fingerprint.update(hex(partial_public_key))
        return fingerprint.hexdigest()

    def get_partial_public_key(self, trustee):
        """
        Retrieve the partial public key for the given trustee.
        
        The partial public key for trustee i is g^P(i). This value is used for 
        verification of the partial decryptions created by said trustee.
        
        Instead of using this values from outside of PloneVoteCryptoLib, 
        please use ThresholdDecryptionCombinator to verify and combine partial 
        decryptions.
        
        Arguments:
            trustee::int    -- The number of the trustee for which we wish to 
                               obtain the partial public key.
        """
        return self._partial_public_keys[trustee]

    def __init__(self, cryptosystem, num_trustees, threshold, public_key_value,
                 verification_partial_public_keys):
        """
        Creates a new threshold public key. Should not be invoked directly.
        
        Instead of using this constructor from outside of PloneVoteCryptoLib, 
        please use ThresholdEncryptionSetUp.generate_public_key().
        
        Arguments:
            (see class attributes for cryptosystem, num_trustees and threshold)
            public_key_value::long        -- The actual value of the public key
                                (g^2P(0) mod p, see ThresholdEncryptionSetUp)
            verification_partial_public_keys::long[]
                    -- A series of "partial public keys" (g^P(i) for each 
                       trustee i), used for partial decryption verification.
                       Note that the key for trustee i must be on index i-1 of
                       the array.
        """
        PublicKey.__init__(self, cryptosystem, public_key_value)

        # Some checks:
        if (threshold > num_trustees):
            raise ValueError("Invalid parameters for the threshold public key:"\
                             " threshold must be smaller than the total number"\
                             " of trustees. Got num_trustees=%d, threshold=%d" \
                             % (num_trustees, threshold))

        if (len(verification_partial_public_keys) != num_trustees):
            raise ValueError("Invalid parameters for the threshold public key:"\
                             " a verification partial public for each trustee "\
                             "must be included.")

        self.num_trustees = num_trustees
        self.threshold = threshold
        self._partial_public_keys = verification_partial_public_keys

    def to_file(self, filename, SerializerClass=serialize.XMLSerializer):
        """
        Saves this threshold public key to a file.
        
        Arguments:
            filename::string    -- The path to the file in which to store the 
                                   serialized ThresholdPublicKey object.
            SerializerClass::class --
                The class that provides the serialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate serialize_to_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        """
        # Create a new serializer object for the PublicKey structure definition
        serializer = SerializerClass(PublicKey_serialize_structure_definition)

        # Helper function to translate large numbers to their hexadecimal
        # string representation
        def num_to_hex_str(num):
            hex_str = hex(num)[2:]  # Remove leading '0x'
            if (hex_str[-1] == 'L'):
                hex_str = hex_str[0:-1]  # Remove trailing 'L'
            return hex_str

        # Generate a serializable data dictionary matching the definition:
        prime_str = num_to_hex_str(self.cryptosystem.get_prime())
        generator_str = num_to_hex_str(self.cryptosystem.get_generator())

        verification_data_list = []
        for i in range(self.num_trustees):
            verification_pk_data = {
                "key": num_to_hex_str(self._partial_public_keys[i]),
                "trustee": str(i)
            }
            verification_data_list.append(verification_pk_data)

        data = {
            "PloneVotePublicKey": {
                "PublicKey": num_to_hex_str(self._key),
                "CryptoSystemScheme": {
                    "nbits": str(self.cryptosystem.get_nbits()),
                    "prime": prime_str,
                    "generator": generator_str
                },
                "ThresholdKeyInfo": {
                    "NumTrustees": str(self.num_trustees),
                    "Threshold": str(self.threshold),
                    "PartialPublicKey": verification_data_list
                }
            }
        }

        # Use the serializer to store the data to file
        serializer.serialize_to_file(filename, data)

    @classmethod
    def from_file(cls, filename, SerializerClass=serialize.XMLSerializer):
        """
        Loads an instance of ThresholdPublicKey from the given file.
        
        Arguments:
            filename::string    -- The name of a file containing the threshold 
                                   public key in serialized form.
            SerializerClass::class --
                The class that provides the deserialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate deserialize_from_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        
        Throws:
            InvalidPloneVoteCryptoFileError -- If the file is not a valid 
                                               PloneVoteCryptoLib stored 
                                               public key file.
        """
        # Create a new serializer object for the PublicKey structure definition
        serializer = SerializerClass(PublicKey_serialize_structure_definition)

        # Deserialize the ThresholdPublicKey instance from file
        try:
            data = serializer.deserialize_from_file(filename)
        except serialize.InvalidSerializeDataError, e:
            # Convert the exception to an InvalidPloneVoteCryptoFileError
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid threshold public key. " \
                "The following error occurred while trying to deserialize " \
                "the file contents: %s" % (filename, str(e)))

        # Verify that we are dealing with a threshold public key and not a
        # single public key.
        if(not \
           data["PloneVotePublicKey"].has_key("ThresholdKeyInfo")):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid threshold public key. " \
                "Instead it contains a single (non-threshold) public key" \
                % filename)

        # Helper function to decode numbers from strings and
        # raise an exception if the string is not a valid number.
        # (value_name is used only to construct the exception string).
        def str_to_num(num_str, base, value_name):
            try:
                return int(num_str, base)
            except ValueError:
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid threshold public " \
                    "key. The stored value for %s is not a valid integer in " \
                    "base %d representation." % (filename, value_name, base))

        # Get the values from the deserialized data
        inner_elems = data["PloneVotePublicKey"]["CryptoSystemScheme"]
        nbits = str_to_num(inner_elems["nbits"], 10, "nbits")
        prime = str_to_num(inner_elems["prime"], 16, "prime")
        generator = str_to_num(inner_elems["generator"], 16, "generator")

        pub_key = str_to_num(data["PloneVotePublicKey"]["PublicKey"], 16,
                             "PublicKey")

        threshold_info = data["PloneVotePublicKey"]["ThresholdKeyInfo"]

        num_trustees = \
                    str_to_num(threshold_info["NumTrustees"], 10, "NumTrustees")
        threshold = \
                    str_to_num(threshold_info["Threshold"], 10, "Threshold")

        pp_keys = threshold_info["PartialPublicKey"]
        partial_public_keys = [None for o in pp_keys]
        for pp_key in pp_keys:
            trustee = str_to_num(pp_key["trustee"], 10, "trustee")
            key_val = str_to_num(pp_key["key"], 16, "key")
            partial_public_keys[trustee] = key_val

        # Check the loaded values
        if (not (1 <= pub_key <= prime - 2)):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid public key. The value " \
                "of the public key given in the file does not match the " \
                "indicated cryptosystem. Could the file be corrupt?" % filename)

        for pp_key in partial_public_keys:
            if (not (1 <= pp_key <= prime - 2)):
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid public key. The " \
                    "value of at least one of the partial public keys given " \
                    "in the file does not match the indicated cryptosystem. " \
                    "Could the file be corrupt?" % filename)

        # Construct the cryptosystem object
        cryptosystem = EGCryptoSystem.load(nbits, prime, generator)

        # Construct and return the PublicKey object
        return cls(cryptosystem, num_trustees, threshold, pub_key,
                   partial_public_keys)
    def from_file(cls, filename, SerializerClass=serialize.XMLSerializer):
        """
        Loads an instance of ThresholdPrivateKey from the given file.
        
        Arguments:
            filename::string    -- The name of a file containing the threshold 
                                   public key in serialized form.
            SerializerClass::class --
                The class that provides the deserialization. XMLSerializer by 
                default. Must inherit from serialize.BaseSerializer and provide 
                an adequate deserialize_from_file method.
                Note that often the same class used to serialize the data must 
                be used to deserialize it.
                (see utilities/serialize.py documentation for more information)
        
        Throws:
            InvalidPloneVoteCryptoFileError -- If the file is not a valid 
                                               PloneVoteCryptoLib stored 
                                               public key file.
        """
        # Create a new serializer object for the PublicKey structure definition
        serializer = SerializerClass(
            ThresholdPrivateKey_serialize_structure_definition)

        # Deserialize the ThresholdPrivateKey instance from file
        try:
            data = serializer.deserialize_from_file(filename)
        except serialize.InvalidSerializeDataError as e:
            # Convert the exception to an InvalidPloneVoteCryptoFileError
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid threshold private key. " \
                "The following error occurred while trying to deserialize " \
                "the file contents: %s" % (filename, str(e)))

        # Helper function to decode numbers from strings and
        # raise an exception if the string is not a valid number.
        # (value_name is used only to construct the exception string).
        def str_to_num(num_str, base, value_name):
            try:
                return int(num_str, base)
            except ValueError:
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid threshold private " \
                    "key. The stored value for %s is not a valid integer in " \
                    "base %d representation." % (filename, value_name, base))

        # Get the values from the deserialized data
        inner_elems = data["PloneVoteThresholdPrivateKey"][
            "CryptoSystemScheme"]
        nbits = str_to_num(inner_elems["nbits"], 10, "nbits")
        prime = str_to_num(inner_elems["prime"], 16, "prime")
        generator = str_to_num(inner_elems["generator"], 16, "generator")

        prv_key = str_to_num(
            data["PloneVoteThresholdPrivateKey"]["PrivateKey"], 16,
            "PrivateKey")
        threshold_info = \
                        data["PloneVoteThresholdPrivateKey"]["ThresholdKeyInfo"]
        num_trustees = \
                    str_to_num(threshold_info["NumTrustees"], 10, "NumTrustees")
        threshold = \
                    str_to_num(threshold_info["Threshold"], 10, "Threshold")
        pub_key = str_to_num(threshold_info["ThresholdPublicKey"], 16,
                             "PublicKey")
        pp_keys = threshold_info["PartialPublicKey"]
        partial_public_keys = [None for o in pp_keys]
        for pp_key in pp_keys:
            trustee = str_to_num(pp_key["trustee"], 10, "trustee")
            key_val = str_to_num(pp_key["key"], 16, "key")
            partial_public_keys[trustee] = key_val

        # Check the loaded values
        if (not (1 <= prv_key <= prime - 2)):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid threshold private key."\
                "The value of the private key given in the file does" \
                "not match the indicated cryptosystem. Could the file be" \
                "corrupt?" % filename)

        if (not (1 <= pub_key <= prime - 2)):
            raise InvalidPloneVoteCryptoFileError(filename,
                "File \"%s\" does not contain a valid threshold private key."\
                "The value of the threshold public key given in the file does" \
                "not match the indicated cryptosystem. Could the file be" \
                "corrupt?" % filename)

        for pp_key in partial_public_keys:
            if (not (1 <= pp_key <= prime - 2)):
                raise InvalidPloneVoteCryptoFileError(filename,
                    "File \"%s\" does not contain a valid threshold private "\
                    "key. The value of at least one of the partial public  " \
                    "keys given in the file does not match the indicated " \
                    "cryptosystem.  Could the file be corrupt?" % filename)

        # Construct the cryptosystem object
        cryptosystem = EGCryptoSystem.load(nbits, prime, generator)

        # Contruct the Threshold Public Key
        threshold_public_key = ThresholdPublicKey(cryptosystem, num_trustees,
                                                  threshold, pub_key,
                                                  partial_public_keys)

        # Construct and return the ThresholdPrivateKey object

        return cls(cryptosystem, num_trustees, threshold, threshold_public_key,
                   prv_key)