Beispiel #1
0
    def test_hex_basic(self):
        """
        This method tests put_hex's and get_hex's basic functionality.
        """
        bitstream = BitStream()

        # Generate a random string of hex digits, ie: ('0'-'9'|'a'-'f'|'A'-'F')*
        valid_hex_digits = "0123456789abcdefABCDEF"
        num_digits = 50
        digits = ""
        for i in range(0, num_digits):
            digits += random.choice(valid_hex_digits)

        # Put those digits in the BitStream...
        bitstream.put_hex(digits)

        # ...and get them back
        bitstream.seek(0)
        read_digits = bitstream.get_hex(len(digits) *
                                        4)  # 1 hex digit == 4 bits

        # Check that the hexadecimal digits were recovered correctly
        # Note that case information may be lost. Comparison must be case
        # insensitive (ie. 'a9Bc' and 'A9bC' are equal)
        self.assertEqual(read_digits.lower(), digits.lower())
Beispiel #2
0
 def test_get_hex_invalid_length(self):
     """
     Test that trying to read a number of bits that is not a multiple of 4 
     as hex data raises an exception.
     """
     bitstream = BitStream()
     bitstream.put_hex("DfF7CE69fF5478A")  # 15 digits, 60 bits
     bitstream.seek(0)
     self.assertRaises(ValueError, bitstream.get_hex,
                       47)  # read 11.75 hex digits?
Beispiel #3
0
 def test_get_hex_invalid_length(self):
     """
     Test that trying to read a number of bits that is not a multiple of 4 
     as hex data raises an exception.
     """
     bitstream = BitStream()
     bitstream.put_hex("DfF7CE69fF5478A") # 15 digits, 60 bits
     bitstream.seek(0)
     self.assertRaises(ValueError, 
                       bitstream.get_hex, 47) # read 11.75 hex digits?
Beispiel #4
0
    def test_get_hex_zero_bits(self):
        """
        Test that reading zero bits from the stream as hex data results in 
        getting the empty string: \"\".
        """
        bitstream = BitStream()
        self.assertEquals(bitstream.get_hex(0), "")

        # Store some hex data in the stream.
        bitstream.put_hex("DfF7CE69fF5478A")  # 15 digits, 60 bits

        bitstream.seek(0)
        self.assertEquals(bitstream.get_hex(0), "")
Beispiel #5
0
 def test_get_hex_zero_bits(self):
     """
     Test that reading zero bits from the stream as hex data results in 
     getting the empty string: \"\".
     """
     bitstream = BitStream()
     self.assertEquals(bitstream.get_hex(0), "")
     
     # Store some hex data in the stream.
     bitstream.put_hex("DfF7CE69fF5478A") # 15 digits, 60 bits
     
     bitstream.seek(0)
     self.assertEquals(bitstream.get_hex(0), "")
Beispiel #6
0
    def test_get_hex_beyond_eos(self):
        """
        Test that trying to read beyond the end of the stream raises an 
        exception when calling get_hex(...).
        """
        bitstream = BitStream()

        self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_hex, 1)
        # Current position should not have been changed
        self.assertEquals(bitstream.get_current_pos(), 0)

        bitstream.put_hex("DfF7CE69fF5478A")  # 15 digits, 60 bits
        bitstream.seek(0)
        # Read beyond EOS
        self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_hex, 61)
        # Current position should not have been changed
        self.assertEquals(bitstream.get_current_pos(), 0)
Beispiel #7
0
 def test_get_hex_beyond_eos(self):
     """
     Test that trying to read beyond the end of the stream raises an 
     exception when calling get_hex(...).
     """
     bitstream = BitStream()
     
     self.assertRaises(NotEnoughBitsInStreamError, 
                       bitstream.get_hex, 1)
     # Current position should not have been changed
     self.assertEquals(bitstream.get_current_pos(),0)
     
     bitstream.put_hex("DfF7CE69fF5478A") # 15 digits, 60 bits
     bitstream.seek(0)
     # Read beyond EOS
     self.assertRaises(NotEnoughBitsInStreamError, 
                       bitstream.get_hex, 61)
     # Current position should not have been changed
     self.assertEquals(bitstream.get_current_pos(),0)
Beispiel #8
0
 def test_hex_basic(self):
     """
     This method tests put_hex's and get_hex's basic functionality.
     """
     bitstream = BitStream()
     
     # Generate a random string of hex digits, ie: ('0'-'9'|'a'-'f'|'A'-'F')*
     valid_hex_digits = "0123456789abcdefABCDEF"
     num_digits = 50        
     digits = ""
     for i in range(0,num_digits):
         digits += random.choice(valid_hex_digits)
         
     # Put those digits in the BitStream...
     bitstream.put_hex(digits)
     
     # ...and get them back
     bitstream.seek(0)
     read_digits = bitstream.get_hex(len(digits)*4) # 1 hex digit == 4 bits
     
     # Check that the hexadecimal digits were recovered correctly
     # Note that case information may be lost. Comparison must be case 
     # insensitive (ie. 'a9Bc' and 'A9bC' are equal)
     self.assertEqual(read_digits.lower(), digits.lower())
Beispiel #9
0
	def verify(self, original_collection, shuffled_collection):
		"""
		Verifies that original_collection and shuffled_collection are 
		equivalent as proven by this ShufflingProof object.
		
		If this method returns true, then we have proof that both collections 
		contain encryptions of the same collection of plaintexts, save for the 
		negligible probability (for a correct security parameter configured in 
		params.py) that the zero-knowledge proof has been faked. Otherwise we 
		gain no information about the two collections, other than they are not 
		shown to be equivalent by this particular proof.
		
		ShufflingProof is a zero-knowledge proof: the result of this 
		verification or the information within the ShufflingProof object for 
		which two collections pass this verification, provide no information as 
		to the association of particular ciphertexts in original_collection  
		with particular ciphertexts of shuffled_collection.
		
		Arguments:
			original_collection::CiphertextCollection	--
				The original collection of ciphertexts.
			shuffled_collection::CiphertextCollection	--
				Another collection for which we wish to know if the current 
				ShufflingProof object demonstrates equivalence with 
				original_collection.
		
		Returns:
			result::bool	-- True if this proof shows both collections to be 
							   equivalent.
							   False otherwise.
		"""
		# Get the security parameter P with which the proof was originally 
		# created. This is reflect in the length of self._collections and 
		# self._mappings.
		assert len(self._collections) == len(self._mappings), \
			"The length of the private properties self._collections and " \
			"self._mappings of ShufflingProof must always be the same."
		security_parameter = len(self._collections)
		
		
		# Get the security parameter specified in params
		minimum_allowed_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(minimum_allowed_security_parameter < 1 or 
		   minimum_allowed_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)
					
		# Check that the security parameter for which the proof was generated 
		# is correct and meets the security standards declared in params.py
		if(security_parameter < minimum_allowed_security_parameter or
		   security_parameter < 1 or 
		   security_parameter > 256):
		   	raise InvalidShuffilingProofError("The security parameter for " \
					"(which the current proof was created is %d. This is " \
					"(either an incorrect value (outside of the [1,256] " \
					"(range) or not secure enough to meet the standards set " \
					"(in the configuration of params.py (< %d). The proof " \
					"(is thus invalid." \
					% (security_parameter, 
					  minimum_allowed_security_parameter))
					
		# Generate the challenge 
		challenge = \
			self._generate_challenge(original_collection, shuffled_collection)
		
		# Verify that the challenge corresponds to the stored one
		if(challenge != self._challenge):
			return False
		
		# Get the challenge as a BitStream for easier manipulation
		challenge_bits = BitStream()
		challenge_bits.put_hex(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):
			bit = challenge_bits.get_num(1)
			if(bit == 0):
				# verify that self._mapping[i] maps original_collection into
				# self._collections[i]
				if(not self._mappings[i].verify(original_collection, 
											   self._collections[i])):
					return False
					
			elif(bit == 1):
				# verify that self._mapping[i] self._collections[i] into
				# self._collections[i]
				if(not self._mappings[i].verify(self._collections[i], 
											   shuffled_collection)):
					return False
			else:
				assert False, "We took a single bit, its value must be either "\
						"0 or 1."
		
		# If we made it so far, the proof is correct
		# (each mapping is in accordance to the challenge and valid)
		return True
Beispiel #10
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