def shuffle_with_proof(self):
		"""
		Produce a verifiable shuffle of this ciphertext collection.
		
		This method returns a tuple containing a new CiphertextCollection 
		object, which encodes the same plaintexts as the current collection 
		but reencrypted and randomly permuted, and a zero-knowledge proof of 
		shuffling.
		
		The proof of shuffling can be used (via the ShuffleProof.verify(...) 
		method) to check that both collections contain encryptions of the same 
		set of plaintexts, under the same cryptosystem and public key. On the 
		other hand, neither the collections nor the proof give any information 
		regarding which ciphertext in the first collection corresponds to which 
		ciphertext in the second, shuffled, collection (hence why we say the 
		proof is done in zero-knowledge).
		
		(see Josh Benaloh, "Simple Verifiable Elections", 
		http://www.usenix.org/event/evt06/tech/full_papers/benaloh/benaloh.pdf 
		for more information.)
		
		Returns:
			(shuffled_collection, proof)::
				(CiphertextCollection, ShufflingProof)
				--
				
					shuffled_collection is a shuffled version of the current 
				collection, containing different ciphertexts but encoding the 
				same plaintexts as the current collection (in different order).
				
					proof is a zero-knowledge proof asserting that the current 
				collection and shuffled_collection are the equivalent 
				(that is, contain representations of the same plaintext in 
				equal numbers). This proof can be verified through the 
				verify(...) method of ShuffleProof itself.
		
		Throws:
			ValueError --
				If params.SHUFFLING_PROOF_SECURITY_PARAMETER is within an 
				invalid range.
		"""
		# Import CiphertextCollectionMapping and ShufflingProof
		
		## Note:
		# 	python has some well known issues with circular imports 
		#	(see http://effbot.org/zone/import-confusion.htm) so putting this 
		#	import statements at the top of our module in the usual way 
		#	will not work. 
		#
		#	Besides, CiphertextCollection as a class is conceptually at a lower 
		#	layer of our architecture than both these other classes. Only this 
		#	convenience method, designed to make the API easier to use from 
		#	outside of plonevotecryptolib, depends on those classes. So it 
		#	actually makes more sense to import these classes within this 
		#	method only.
		##
		from plonevotecryptolib.Mixnet.CiphertextCollectionMapping import CiphertextCollectionMapping
		from plonevotecryptolib.Mixnet.ShufflingProof import ShufflingProof
		
		# Create a mapping from the current collection into a random shuffling
		mapping = CiphertextCollectionMapping.new(self)
		
		# Apply the mapping to obtain the resulting shuffled collection
		try:
			shuffled_collection = mapping.apply(self)
		except IncompatibleCiphertextCollectionError:
			assert False, "IncompatibleCiphertextCollectionError may not be " \
						"raised when applying a mapping M created using " \
						"CiphertextCollectionMapping.new(C) to the same C."
		
		# Generate the zero-knowledge proof of shuffling
		try:
			proof = ShufflingProof.new(self, shuffled_collection, mapping)
		except InvalidCiphertextCollectionMappingError:
			assert False, "InvalidCiphertextCollectionMappingError may not be " \
						"raised when shuffled_collection was created from the " \
						"original collection (self) by applying the mapping."
		
		# Form tuple and return it
		return (shuffled_collection, proof)
    def shuffle_with_proof(self):
        """
		Produce a verifiable shuffle of this ciphertext collection.
		
		This method returns a tuple containing a new CiphertextCollection 
		object, which encodes the same plaintexts as the current collection 
		but reencrypted and randomly permuted, and a zero-knowledge proof of 
		shuffling.
		
		The proof of shuffling can be used (via the ShuffleProof.verify(...) 
		method) to check that both collections contain encryptions of the same 
		set of plaintexts, under the same cryptosystem and public key. On the 
		other hand, neither the collections nor the proof give any information 
		regarding which ciphertext in the first collection corresponds to which 
		ciphertext in the second, shuffled, collection (hence why we say the 
		proof is done in zero-knowledge).
		
		(see Josh Benaloh, "Simple Verifiable Elections", 
		http://www.usenix.org/event/evt06/tech/full_papers/benaloh/benaloh.pdf 
		for more information.)
		
		Returns:
			(shuffled_collection, proof)::
				(CiphertextCollection, ShufflingProof)
				--
				
					shuffled_collection is a shuffled version of the current 
				collection, containing different ciphertexts but encoding the 
				same plaintexts as the current collection (in different order).
				
					proof is a zero-knowledge proof asserting that the current 
				collection and shuffled_collection are the equivalent 
				(that is, contain representations of the same plaintext in 
				equal numbers). This proof can be verified through the 
				verify(...) method of ShuffleProof itself.
		
		Throws:
			ValueError --
				If params.SHUFFLING_PROOF_SECURITY_PARAMETER is within an 
				invalid range.
		"""
        # Import CiphertextCollectionMapping and ShufflingProof

        ## Note:
        # 	python has some well known issues with circular imports
        #	(see http://effbot.org/zone/import-confusion.htm) so putting this
        #	import statements at the top of our module in the usual way
        #	will not work.
        #
        #	Besides, CiphertextCollection as a class is conceptually at a lower
        #	layer of our architecture than both these other classes. Only this
        #	convenience method, designed to make the API easier to use from
        #	outside of plonevotecryptolib, depends on those classes. So it
        #	actually makes more sense to import these classes within this
        #	method only.
        ##
        from plonevotecryptolib.Mixnet.CiphertextCollectionMapping import CiphertextCollectionMapping
        from plonevotecryptolib.Mixnet.ShufflingProof import ShufflingProof

        # Create a mapping from the current collection into a random shuffling
        mapping = CiphertextCollectionMapping.new(self)

        # Apply the mapping to obtain the resulting shuffled collection
        try:
            shuffled_collection = mapping.apply(self)
        except IncompatibleCiphertextCollectionError:
            assert False, "IncompatibleCiphertextCollectionError may not be " \
               "raised when applying a mapping M created using " \
               "CiphertextCollectionMapping.new(C) to the same C."

        # Generate the zero-knowledge proof of shuffling
        try:
            proof = ShufflingProof.new(self, shuffled_collection, mapping)
        except InvalidCiphertextCollectionMappingError:
            assert False, "InvalidCiphertextCollectionMappingError may not be " \
               "raised when shuffled_collection was created from the " \
               "original collection (self) by applying the mapping."

        # Form tuple and return it
        return (shuffled_collection, proof)
Example #3
0
	def new(cls, original_collection, shuffled_collection, mapping):
		"""
		Constructs a new proof of equivalence between original_collection and 
		shuffled_collection.
		
		This method should not be used outside of plonevotecryptolib.Mixnet. 
		Consider using CiphertextCollection.shuffle_with_proof() instead.
		
		The given CiphertextCollectionMapping must be a valid mapping between 
		original_collection and shuffled_collection.
		
		Arguments:
			original_collection::CiphertextCollection --
				The original collection to be shuffled.
			shuffled_collection::CiphertextCollection --
				The shuffled collection resulting from applying mapping to 
				original_collection.
			mapping::CiphertextCollectionMapping --
				The mapping between original_collection and shuffled_collection.
		
		Returns:
			proof::ShufflingProof --
				The zero-knowledge proof of equivalence between 
				original_collection and shuffled_collection.
		
		Throws:
			InvalidCiphertextCollectionMappingError --
				If mapping is not a valid mapping between original_collection 
				and shuffled_collection.
			ValueError --
				If params.SHUFFLING_PROOF_SECURITY_PARAMETER is within an 
				invalid range.
		"""
		# Check that we have a valid mapping between the two collections:
		if(not mapping.verify(original_collection, shuffled_collection)):
			raise InvalidCiphertextCollectionMappingError( \
					"mapping was expected to be a CiphertextCollectionMapping "\
					"object representing a valid mapping between "\
					"original_collection and shuffled_collection. However, "\
					"mapping.verify(original_collection, shuffled_collection) "\
					"returns False. A zero-knowledge proof of equivalence "\
					"between two shuffled collections cannot be constructed "\
					"without first having a valid mapping between them.")
		
		# Get the security parameter P
		security_parameter = params.SHUFFLING_PROOF_SECURITY_PARAMETER
		
		# Check that the security parameter is <= 256, since that is the most 
		# bits of challenge we can currently have. Also check that P is at least 1
		if(security_parameter < 1 or security_parameter > 256):
			raise ValueError("Security parameter for shuffling proof " \
					"(params.SHUFFLING_PROOF_SECURITY_PARAMETER) is out of " \
					"range. The security parameter must be between 1 and 256, "\
					"its current value is %d. If you have set " \
					"CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER in the file " \
					"params.py, please correct this value or set it to None, " \
					"in order for the security parameter to be decided based " \
					"on the global SECURITY_LEVEL of plonevotecryptolib. It " \
					"is recommended that " \
					"CUSTOM_SHUFFLING_PROOF_SECURITY_PARAMETER be always set " \
					"to None for deployment operation of plonevotecryptolib." \
					% security_parameter)
		
		# Construct a new empty proof
		proof = ShufflingProof()
		
		# Populate proof._collections with P random shuffles of 
		# original_collection. We save each mapping for now in proof._mappings.
		# (ie. every mapping in proof._mappings[i] will initially be from the 
		# original collection into proof._collections[i])
		for i in range(0, security_parameter):
			## print "Generating collection #%d" % i # replace with TaskMonitor API calls
			new_mapping = CiphertextCollectionMapping.new(original_collection)
			proof._mappings.append(new_mapping)
			proof._collections.append(new_mapping.apply(original_collection))
		
		# Generate the challenge 
		proof._challenge = \
			proof._generate_challenge(original_collection, shuffled_collection)
		
		# Get the challenge as a BitStream for easier manipulation
		challenge_bits = BitStream()
		challenge_bits.put_hex(proof._challenge)
		challenge_bits.seek(0)	# back to the beginning of the stream
		
		# For each of the first P bits in the stream
		for i in range(0, security_parameter):
			## print "Processing challenge bit %d" % i # replace with TaskMonitor API calls
			bit = challenge_bits.get_num(1)
			if(bit == 0):
				# Do nothing.
				# proof._mappings[i] is already a mapping from 
				# original_collection unto proof._collections[i]
				pass
			elif(bit == 1):
				# Change proof._mappings[i] to be a mapping from 
				# proof._collections[i] unto shuffled_collection, using 
				# CiphertextCollectionMapping.rebase(...)
				
				# rebase(O->D, O->C_{i}) => C_{i}->D
				rebased_mapping = mapping.rebase(proof._mappings[i])
				
				# Replace O->C_{i} with C_{i}->D
				proof._mappings[i] = rebased_mapping
			else:
				assert False, "We took a single bit, its value must be either "\
						"0 or 1."
			
		# return the proof object
		return proof