Beispiel #1
0
 def test_get_num_beyond_eos(self):
     """
     Test than trying to read beyond the end of the stream raises an 
     exception when calling get_num(...).
     """
     bitstream = BitStream()
     
     # Store a 64-bit integer in the stream.
     num = int('00000000000000110101011001010011'
               '00101100001000101000100110101111', 2)
     bitstream.put_num(num, 64)
     
     # Check that trying to read 65 bits from the beginning of the stream
     # raises NotEnoughBitsInStreamError
     bitstream.seek(0)
     self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 65)
     # An invalid read call should not move the position indicator.
     self.assertEquals(bitstream.get_current_pos(), 0)
     
     # Check that trying to read 33 bits from the middle of the stream 
     # (pos = 32) raises NotEnoughBitsInStreamError
     bitstream.seek(32)
     self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 33)
     # An invalid read call should not move the position indicator.
     self.assertEquals(bitstream.get_current_pos(), 32)
     
     # Check that trying to read a single bit while at the end of the stream
     # raises NotEnoughBitsInStreamError
     bitstream.seek(64)
     self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 1)
     # An invalid read call should not move the position indicator.
     self.assertEquals(bitstream.get_current_pos(), 64)
Beispiel #2
0
    def test_stress(self):
        """
        Stress test BitStream by writings lots of numbers of different bit 
        sizes and reading them back.
        """
        # num_writes = 10000
        num_writes = 1000
        min_bit_length = 1
        max_bit_length = 8192

        # Generate the numbers
        nums = []
        total_bit_length = 0
        for i in range(0, num_writes):
            bit_length = random.randint(min_bit_length, max_bit_length)
            total_bit_length += bit_length
            val = random.randint(0, 2**bit_length - 1)
            nums.append({"bit_length": bit_length, "value": val})

        # Write them to a BitStream object
        bitstream = BitStream()
        for num in nums:
            bitstream.put_num(num["value"], num["bit_length"])

        # Check the BitStream length and current position
        self.assertEquals(bitstream.get_length(), total_bit_length)
        self.assertEquals(bitstream.get_current_pos(), total_bit_length)

        # Read all numbers back
        bitstream.seek(0)
        for num in nums:
            self.assertEquals(bitstream.get_num(num["bit_length"]),
                              num["value"])
Beispiel #3
0
    def test_get_num_beyond_eos(self):
        """
        Test than trying to read beyond the end of the stream raises an 
        exception when calling get_num(...).
        """
        bitstream = BitStream()

        # Store a 64-bit integer in the stream.
        num = int(
            '00000000000000110101011001010011'
            '00101100001000101000100110101111', 2)
        bitstream.put_num(num, 64)

        # Check that trying to read 65 bits from the beginning of the stream
        # raises NotEnoughBitsInStreamError
        bitstream.seek(0)
        self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 65)
        # An invalid read call should not move the position indicator.
        self.assertEquals(bitstream.get_current_pos(), 0)

        # Check that trying to read 33 bits from the middle of the stream
        # (pos = 32) raises NotEnoughBitsInStreamError
        bitstream.seek(32)
        self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 33)
        # An invalid read call should not move the position indicator.
        self.assertEquals(bitstream.get_current_pos(), 32)

        # Check that trying to read a single bit while at the end of the stream
        # raises NotEnoughBitsInStreamError
        bitstream.seek(64)
        self.assertRaises(NotEnoughBitsInStreamError, bitstream.get_num, 1)
        # An invalid read call should not move the position indicator.
        self.assertEquals(bitstream.get_current_pos(), 64)
Beispiel #4
0
 def test_seek_basic(self):
     """
     Use seek to read data at different points in the bitstream.
     """
     bitstream = BitStream()
     
     # Store a 64-bit integer in the stream.
     num = int('00000000000000110101011001010011'
               '00101100001000101000100110101111', 2)
     bitstream.put_num(num, 64)
     
     # Get the 0th, 3rd, 5th and 6th bytes of the stream
     # First in forwards order
     bitstream.seek(0)
     self.assertEqual(bitstream.get_num(8),int('00000000',2))
     bitstream.seek(3*8)
     self.assertEqual(bitstream.get_num(8),int('01010011',2))
     bitstream.seek(5*8)
     self.assertEqual(bitstream.get_num(8),int('00100010',2))
     self.assertEqual(bitstream.get_num(8),int('10001001',2))
     # Then in backwards order
     bitstream.seek(6*8)
     self.assertEqual(bitstream.get_num(8),int('10001001',2))
     bitstream.seek(5*8)
     self.assertEqual(bitstream.get_num(8),int('00100010',2))
     bitstream.seek(3*8)
     self.assertEqual(bitstream.get_num(8),int('01010011',2))
     bitstream.seek(0)
     self.assertEqual(bitstream.get_num(8),int('00000000',2))
Beispiel #5
0
    def test_seek_basic(self):
        """
        Use seek to read data at different points in the bitstream.
        """
        bitstream = BitStream()

        # Store a 64-bit integer in the stream.
        num = int(
            '00000000000000110101011001010011'
            '00101100001000101000100110101111', 2)
        bitstream.put_num(num, 64)

        # Get the 0th, 3rd, 5th and 6th bytes of the stream
        # First in forwards order
        bitstream.seek(0)
        self.assertEqual(bitstream.get_num(8), int('00000000', 2))
        bitstream.seek(3 * 8)
        self.assertEqual(bitstream.get_num(8), int('01010011', 2))
        bitstream.seek(5 * 8)
        self.assertEqual(bitstream.get_num(8), int('00100010', 2))
        self.assertEqual(bitstream.get_num(8), int('10001001', 2))
        # Then in backwards order
        bitstream.seek(6 * 8)
        self.assertEqual(bitstream.get_num(8), int('10001001', 2))
        bitstream.seek(5 * 8)
        self.assertEqual(bitstream.get_num(8), int('00100010', 2))
        bitstream.seek(3 * 8)
        self.assertEqual(bitstream.get_num(8), int('01010011', 2))
        bitstream.seek(0)
        self.assertEqual(bitstream.get_num(8), int('00000000', 2))
Beispiel #6
0
 def test_stress(self):
     """
     Stress test BitStream by writings lots of numbers of different bit 
     sizes and reading them back.
     """
     # num_writes = 10000
     num_writes = 1000
     min_bit_length = 1
     max_bit_length = 8192
     
     # Generate the numbers
     nums = []
     total_bit_length = 0
     for i in range(0, num_writes):
         bit_length = random.randint(min_bit_length, max_bit_length)
         total_bit_length += bit_length
         val = random.randint(0, 2**bit_length - 1)
         nums.append({"bit_length" : bit_length, "value" : val})
         
     # Write them to a BitStream object
     bitstream = BitStream()
     for num in nums:
         bitstream.put_num(num["value"], num["bit_length"])
         
     # Check the BitStream length and current position
     self.assertEquals(bitstream.get_length(),total_bit_length)
     self.assertEquals(bitstream.get_current_pos(),total_bit_length)
     
     # Read all numbers back
     bitstream.seek(0)
     for num in nums:
         self.assertEquals(bitstream.get_num(num["bit_length"]),num["value"])
Beispiel #7
0
    def test_multiformat_write_multiformat_read(self):
        """
        This test writes numeric, byte and string data to a stream and then 
        reads the whole stream as binary, hex and base64 data, ensuring that 
        the output is the expected one in each case. 
        """
        # Write a number, 2 bytes and a string
        bitstream = BitStream()
        bitstream.put_num(10438341575639894917, 64)
        bitstream.put_byte(230)
        bitstream.put_byte(191)
        bitstream.put_string("ÄäÜüßTestЯБГДЖЙŁĄŻStringĘĆŃŚŹてす" \
                                 "とアイウエオカキク4234ケコサシスセソタチツテ")

        # Read in binary, hex and base64 formats
        expected_bits = \
            "1001000011011100011100000101101110111100001101010000101110000101" \
            "1110011010111111110000111000010011000011101001001100001110011100" \
            "1100001110111100110000111001111101010100011001010111001101110100" \
            "1101000010101111110100001001000111010000100100111101000010010100" \
            "1101000010010110110100001001100111000101100000011100010010000100" \
            "1100010110111011010100110111010001110010011010010110111001100111" \
            "1100010010011000110001001000011011000101100000111100010110011010" \
            "1100010110111001111000111000000110100110111000111000000110011001" \
            "1110001110000001101010001110111110111101101100011110111110111101" \
            "1011001011101111101111011011001111101111101111011011010011101111" \
            "1011110110110101111011111011110110110110111011111011110110110111" \
            "1110111110111101101110000011010000110010001100110011010011101111" \
            "1011110110111001111011111011110110111010111011111011110110111011" \
            "1110111110111101101111001110111110111101101111011110111110111101" \
            "1011111011101111101111011011111111101111101111101000000011101111" \
            "1011111010000001111011111011111010000010111011111011111010000011"
        expected_hex = \
            "90dc705bbc350b85e6bfc384c3a4c39cc3bcc39f54657374d0afd091d093d094" \
            "d096d099c581c484c5bb537472696e67c498c486c583c59ac5b9e381a6e38199" \
            "e381a8efbdb1efbdb2efbdb3efbdb4efbdb5efbdb6efbdb7efbdb834323334ef" \
            "bdb9efbdbaefbdbbefbdbcefbdbdefbdbeefbdbfefbe80efbe81efbe82efbe83"
        expected_base64 = \
            "kNxwW7w1C4Xmv8OEw6TDnMO8w59UZXN00K/QkdCT0JTQltCZxYHEhMW7U3RyaW5n" \
            "xJjEhsWDxZrFueOBpuOBmeOBqO+9se+9su+9s++9tO+9te+9tu+9t++9uDQyMzTv" \
            "vbnvvbrvvbvvvbzvvb3vvb7vvb/vvoDvvoHvvoLvvoM="

        bitstream.seek(0)
        self.assertEquals( \
                    bitstream.get_bit_dump_string(bitstream.get_length()),
                    expected_bits)
        bitstream.seek(0)
        self.assertEquals(
            bitstream.get_hex(bitstream.get_length()).lower(), expected_hex)
        bitstream.seek(0)
        self.assertEquals(bitstream.get_base64(bitstream.get_length()),
                          expected_base64)
Beispiel #8
0
 def test_multiformat_write_multiformat_read(self):
     """
     This test writes numeric, byte and string data to a stream and then 
     reads the whole stream as binary, hex and base64 data, ensuring that 
     the output is the expected one in each case. 
     """
     # Write a number, 2 bytes and a string
     bitstream = BitStream()
     bitstream.put_num(10438341575639894917, 64)
     bitstream.put_byte(230)
     bitstream.put_byte(191)
     bitstream.put_string("ÄäÜüßTestЯБГДЖЙŁĄŻStringĘĆŃŚŹてす" \
                              "とアイウエオカキク4234ケコサシスセソタチツテ")
     
     # Read in binary, hex and base64 formats
     expected_bits = \
         "1001000011011100011100000101101110111100001101010000101110000101" \
         "1110011010111111110000111000010011000011101001001100001110011100" \
         "1100001110111100110000111001111101010100011001010111001101110100" \
         "1101000010101111110100001001000111010000100100111101000010010100" \
         "1101000010010110110100001001100111000101100000011100010010000100" \
         "1100010110111011010100110111010001110010011010010110111001100111" \
         "1100010010011000110001001000011011000101100000111100010110011010" \
         "1100010110111001111000111000000110100110111000111000000110011001" \
         "1110001110000001101010001110111110111101101100011110111110111101" \
         "1011001011101111101111011011001111101111101111011011010011101111" \
         "1011110110110101111011111011110110110110111011111011110110110111" \
         "1110111110111101101110000011010000110010001100110011010011101111" \
         "1011110110111001111011111011110110111010111011111011110110111011" \
         "1110111110111101101111001110111110111101101111011110111110111101" \
         "1011111011101111101111011011111111101111101111101000000011101111" \
         "1011111010000001111011111011111010000010111011111011111010000011"
     expected_hex = \
         "90dc705bbc350b85e6bfc384c3a4c39cc3bcc39f54657374d0afd091d093d094" \
         "d096d099c581c484c5bb537472696e67c498c486c583c59ac5b9e381a6e38199" \
         "e381a8efbdb1efbdb2efbdb3efbdb4efbdb5efbdb6efbdb7efbdb834323334ef" \
         "bdb9efbdbaefbdbbefbdbcefbdbdefbdbeefbdbfefbe80efbe81efbe82efbe83"
     expected_base64 = \
         "kNxwW7w1C4Xmv8OEw6TDnMO8w59UZXN00K/QkdCT0JTQltCZxYHEhMW7U3RyaW5n" \
         "xJjEhsWDxZrFueOBpuOBmeOBqO+9se+9su+9s++9tO+9te+9tu+9t++9uDQyMzTv" \
         "vbnvvbrvvbvvvbzvvb3vvb7vvb/vvoDvvoHvvoLvvoM="
         
     bitstream.seek(0)
     self.assertEquals( \
                 bitstream.get_bit_dump_string(bitstream.get_length()),
                 expected_bits)
     bitstream.seek(0)
     self.assertEquals(bitstream.get_hex(bitstream.get_length()).lower(),
                       expected_hex)
     bitstream.seek(0)
     self.assertEquals(bitstream.get_base64(bitstream.get_length()),
                       expected_base64)
Beispiel #9
0
 def test_get_num_zero_bits(self):
     """
     Test that reading zero bits from the stream as a number results in 
     getting the number 0.
     """
     bitstream = BitStream()
     self.assertEquals(bitstream.get_num(0), 0)
     
     # Store a 64-bit integer in the stream.
     num = int('00000000000000110101011001010011'
               '00101100001000101000100110101111', 2)
     bitstream.put_num(num, 64)
     
     bitstream.seek(0)
     self.assertEquals(bitstream.get_num(0), 0)
Beispiel #10
0
    def test_get_num_zero_bits(self):
        """
        Test that reading zero bits from the stream as a number results in 
        getting the number 0.
        """
        bitstream = BitStream()
        self.assertEquals(bitstream.get_num(0), 0)

        # Store a 64-bit integer in the stream.
        num = int(
            '00000000000000110101011001010011'
            '00101100001000101000100110101111', 2)
        bitstream.put_num(num, 64)

        bitstream.seek(0)
        self.assertEquals(bitstream.get_num(0), 0)
Beispiel #11
0
 def _encrypted_data_as_bitstream(self):
     """
     Returns the contents of this ciphertext as a BitStream object.
     
     This includes only the encrypted data (gamma and delta components), not 
     the nbits and public key fingerprint metadata.
     
     The components are encoded alternating as follows:
         [gamma[0], delta[0], gamma[1], delta[1], ...]
     with each component represented as a nbits long number.
     
     Returns:
         bitstream::BitStream    -- The gamma and delta components of this 
                                    ciphertext as a bitstream.
     """
     bitstream = BitStream()
     for i in range(0, self.get_length()):
         bitstream.put_num(self.gamma[i], self.nbits)
         bitstream.put_num(self.delta[i], self.nbits)
     return bitstream
Beispiel #12
0
 def _encrypted_data_as_bitstream(self):
     """
     Returns the contents of this ciphertext as a BitStream object.
     
     This includes only the encrypted data (gamma and delta components), not 
     the nbits and public key fingerprint metadata.
     
     The components are encoded alternating as follows:
         [gamma[0], delta[0], gamma[1], delta[1], ...]
     with each component represented as a nbits long number.
     
     Returns:
         bitstream::BitStream    -- The gamma and delta components of this 
                                    ciphertext as a bitstream.
     """
     bitstream = BitStream()
     for i in range(0, self.get_length()):
         bitstream.put_num(self.gamma[i], self.nbits)
         bitstream.put_num(self.delta[i], self.nbits)
     return bitstream
Beispiel #13
0
 def test_seek_beyond_eos(self):
     """
     Test that seeking beyond the end of the bitstream results in an 
     exception raised.
     """
     bitstream = BitStream()
     self.assertRaises(SeekOutOfRangeError, bitstream.seek, 1) # empty stream
     # An invalid seek(...) call should not move the position indicator.
     self.assertEqual(bitstream.get_current_pos(), 0)
     
     # Store a 64-bit integer in the stream.
     num = int('00000000000000110101011001010011'
               '00101100001000101000100110101111', 2)
     bitstream.put_num(num, 64)
     
     # Test seeking past 64 bits
     self.assertRaises(SeekOutOfRangeError, bitstream.seek, 65)
     self.assertRaises(SeekOutOfRangeError, bitstream.seek, 
                       random.randint(66,2**128))
                       
     # An invalid seek(...) call should not move the position indicator.
     self.assertEqual(bitstream.get_current_pos(), 64)
Beispiel #14
0
    def test_num_basic(self):
        """
        Test basic put_num and get_num behavior
        """
        bitstream = BitStream()

        # Some parameters
        num_sizes = [4, 8, 16, 32, 64, 128, 256, 2839]
        num_writes = 10
        nums = []

        # Add num_writes integers of each size to nums:
        for i in range(0, len(num_sizes)):
            max_val = 2**num_sizes[i] - 1
            nums.append([])
            for j in range(0, num_writes):
                # append a random number between 0 and max_val, inclusive
                nums[i].append(random.randint(0, max_val))

        # Write all values in nums to the stream
        for i in range(0, len(num_sizes)):
            for j in range(0, num_writes):
                bitstream.put_num(nums[i][j], num_sizes[i])

        # Go back to start of the stream
        bitstream.seek(0)

        # Sanity check:
        expected_length = 0
        for num_size in num_sizes:
            expected_length += num_size * num_writes
        self.assertEqual(bitstream.get_length(), expected_length)

        # Read them back and compare
        for i in range(0, len(num_sizes)):
            for j in range(0, num_writes):
                n = bitstream.get_num(num_sizes[i])
                self.assertEqual(n, nums[i][j])
Beispiel #15
0
    def test_num_read_part(self):
        """
        Test that we can read only some bits of a large number and interpret 
        them as a shorter number with the expected value.
        """
        bitstream = BitStream()

        # Store a 64-bit integer in the stream.
        num = int(
            '00000000000000110101011001010011'
            '00101100001000101000100110101111', 2)
        bitstream.put_num(num, 64)

        # Get it as 8-bit numbers
        bitstream.seek(0)
        self.assertEqual(bitstream.get_num(8), int('00000000', 2))
        self.assertEqual(bitstream.get_num(8), int('00000011', 2))
        self.assertEqual(bitstream.get_num(8), int('01010110', 2))
        self.assertEqual(bitstream.get_num(8), int('01010011', 2))
        self.assertEqual(bitstream.get_num(8), int('00101100', 2))
        self.assertEqual(bitstream.get_num(8), int('00100010', 2))
        self.assertEqual(bitstream.get_num(8), int('10001001', 2))
        self.assertEqual(bitstream.get_num(8), int('10101111', 2))

        # Get it as 16-bit numbers
        bitstream.seek(0)
        self.assertEqual(bitstream.get_num(16), int('0000000000000011', 2))
        self.assertEqual(bitstream.get_num(16), int('0101011001010011', 2))
        self.assertEqual(bitstream.get_num(16), int('0010110000100010', 2))
        self.assertEqual(bitstream.get_num(16), int('1000100110101111', 2))

        # Get it as 32-bit numbers
        bitstream.seek(0)
        self.assertEqual(bitstream.get_num(32),
                         int('00000000000000110101011001010011', 2))
        self.assertEqual(bitstream.get_num(32),
                         int('00101100001000101000100110101111', 2))
Beispiel #16
0
 def test_num_basic(self):
     """
     Test basic put_num and get_num behavior
     """
     bitstream = BitStream()
     
     # Some parameters
     num_sizes = [4,8,16,32,64,128,256,2839]
     num_writes = 10
     nums = []
     
     # Add num_writes integers of each size to nums:
     for i in range(0,len(num_sizes)):
         max_val = 2**num_sizes[i] - 1
         nums.append([])
         for j in range(0,num_writes):
             # append a random number between 0 and max_val, inclusive
             nums[i].append(random.randint(0, max_val))
              
     # Write all values in nums to the stream
     for i in range(0,len(num_sizes)):
         for j in range(0,num_writes):
             bitstream.put_num(nums[i][j], num_sizes[i])
             
     # Go back to start of the stream
     bitstream.seek(0)
     
     # Sanity check:
     expected_length = 0
     for num_size in num_sizes: expected_length += num_size * num_writes
     self.assertEqual(bitstream.get_length(), expected_length)
             
     # Read them back and compare
     for i in range(0,len(num_sizes)):
         for j in range(0,num_writes):
             n = bitstream.get_num(num_sizes[i])
             self.assertEqual(n, nums[i][j])
Beispiel #17
0
    def test_seek_beyond_eos(self):
        """
        Test that seeking beyond the end of the bitstream results in an 
        exception raised.
        """
        bitstream = BitStream()
        self.assertRaises(SeekOutOfRangeError, bitstream.seek,
                          1)  # empty stream
        # An invalid seek(...) call should not move the position indicator.
        self.assertEqual(bitstream.get_current_pos(), 0)

        # Store a 64-bit integer in the stream.
        num = int(
            '00000000000000110101011001010011'
            '00101100001000101000100110101111', 2)
        bitstream.put_num(num, 64)

        # Test seeking past 64 bits
        self.assertRaises(SeekOutOfRangeError, bitstream.seek, 65)
        self.assertRaises(SeekOutOfRangeError, bitstream.seek,
                          random.randint(66, 2**128))

        # An invalid seek(...) call should not move the position indicator.
        self.assertEqual(bitstream.get_current_pos(), 64)
Beispiel #18
0
 def test_num_read_part(self):
     """
     Test that we can read only some bits of a large number and interpret 
     them as a shorter number with the expected value.
     """
     bitstream = BitStream()
     
     # Store a 64-bit integer in the stream.
     num = int('00000000000000110101011001010011'
               '00101100001000101000100110101111', 2)
     bitstream.put_num(num, 64)
     
     # Get it as 8-bit numbers
     bitstream.seek(0)
     self.assertEqual(bitstream.get_num(8),int('00000000',2))
     self.assertEqual(bitstream.get_num(8),int('00000011',2))
     self.assertEqual(bitstream.get_num(8),int('01010110',2))
     self.assertEqual(bitstream.get_num(8),int('01010011',2))
     self.assertEqual(bitstream.get_num(8),int('00101100',2))
     self.assertEqual(bitstream.get_num(8),int('00100010',2))
     self.assertEqual(bitstream.get_num(8),int('10001001',2))
     self.assertEqual(bitstream.get_num(8),int('10101111',2))
     
     # Get it as 16-bit numbers
     bitstream.seek(0)
     self.assertEqual(bitstream.get_num(16),int('0000000000000011',2))
     self.assertEqual(bitstream.get_num(16),int('0101011001010011',2))
     self.assertEqual(bitstream.get_num(16),int('0010110000100010',2))
     self.assertEqual(bitstream.get_num(16),int('1000100110101111',2))
     
     # Get it as 32-bit numbers
     bitstream.seek(0)
     self.assertEqual(bitstream.get_num(32),
                      int('00000000000000110101011001010011',2))
     self.assertEqual(bitstream.get_num(32),
                      int('00101100001000101000100110101111',2))
Beispiel #19
0
 def test_num_write_part(self):
     """
     Test that we can write a large number piece by piece and read it as 
     whole number.
     """
     bitstream = BitStream()
     
     # Store a 64-bit integer in the stream.
     num = int('00000000000000110101011001010011'
               '00101100001000101000100110101111', 2)
     
     # ... in 8-bit, 16-bit and 32-bit increments        
     bitstream.put_num(int('00000000',2), 8)
     bitstream.put_num(int('0000001101010110',2), 16)
     bitstream.put_num(int('01010011001011000010001010001001',2), 32)        
     bitstream.put_num(int('10101111',2), 8)
     
     # Get it as a single 64-bit number
     bitstream.seek(0)
     self.assertEqual(bitstream.get_num(64),num)
Beispiel #20
0
    def test_num_write_part(self):
        """
        Test that we can write a large number piece by piece and read it as 
        whole number.
        """
        bitstream = BitStream()

        # Store a 64-bit integer in the stream.
        num = int(
            '00000000000000110101011001010011'
            '00101100001000101000100110101111', 2)

        # ... in 8-bit, 16-bit and 32-bit increments
        bitstream.put_num(int('00000000', 2), 8)
        bitstream.put_num(int('0000001101010110', 2), 16)
        bitstream.put_num(int('01010011001011000010001010001001', 2), 32)
        bitstream.put_num(int('10101111', 2), 8)

        # Get it as a single 64-bit number
        bitstream.seek(0)
        self.assertEqual(bitstream.get_num(64), num)
Beispiel #21
0
    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_bitstream(self, task_monitor=None):
        """
		Decrypt the ciphertext to a bitstream, using the partial decryptions.
		
		At least (threshold) correctly generated partial decryptions for the 
		ciphertext must be registered with this instance in order for 
		decryption to succeed.
		
		Arguments:
			task_monitor::TaskMonitor	-- A task monitor for this task.
		
		Returns:
			bitstream::Bitstream	-- A bitstream containing the unencrypted 
									   data.
									   
		Throws:
			InsuficientPartialDecryptionsError	-- If there aren't enough 
									partial decryptions registered with this 
									object to perform combined decryption.
		"""
        # Get the indexes of all trustees for which we have a registered
        # partial decryption. We use 1 based indexes here.
        trustee_indexes = []
        for trustee in range(1, self._num_trustees + 1):
            decryption = self._trustees_partial_decryptions[trustee - 1]
            if (decryption != None):
                trustee_indexes.append(trustee)

        # Check that we have enough trustees.
        if (len(trustee_indexes) < self._threshold):
            raise InsuficientPartialDecryptionsError("Not enough partial " \
              "decryptions have been registered with this object to " \
              "create a combined decryption. Registered partial " \
              "decryptions: %d. Required partial decryptions " \
              "(threshold): %d." \
              % (len(trustee_indexes), self._threshold))

        # We only need threshold trustees, exactly. Select those at random.
        random.shuffle(trustee_indexes)
        trustee_indexes = trustee_indexes[0:self._threshold]

        # We get the number of bits and prime for the cryptosystem
        nbits = self.cryptosystem.get_nbits()
        prime = self.cryptosystem.get_prime()
        #  prime = 2q + 1 with q prime by construction (see EGCryptoSystem).
        q = (prime - 1) // 2

        # See PublicKey.encrypt_bitstream for why we use nbits - 1 as the block
        # size.
        block_size = self.cryptosystem.get_nbits() - 1

        # We initialize our bitstream
        bitstream = BitStream()

        # We pre-calculate the lagrange coefficients for the trustees in Z_{q}
        # for x = 0, to avoid doing so for each block of ciphertext.
        # See below for an explanation of the use of lagrange coefficients.
        lagrange_coeffs = [None for i in range(0, self._num_trustees + 1)]
        for trustee in trustee_indexes:
            lagrange_coeffs[trustee] = \
             _lagrange_coefficient(trustee_indexes, trustee, 0, q)

        # For each block of partial decryption/(gamma, delta) pair of ciphertext
        for b_index in range(0, self._ciphertext.get_length()):

            # Each partial decryption block is of the form g^{rP(i)}, where
            # (gamma, delta) = (g^r, m*g^{r2P(0)}).
            #
            # We must first interpolate val=g^{r2P(0)} from the g^{rP(i)}'s.
            # Interpolation of val (LaTeX):
            # $g^{r2P(0)}=g^{\sum_{i\in I}r2P(i)\lambda_{i}(0)}=
            # \prod_{i\in I}\left(g^{rP\left(i\right)}\right)^{2\lambda_{i}(0)}$
            # where \lambda_{i}(0) are the Lagrange Coefficients.
            #
            # Note that the polynomials are in the field Z_{q} where q is such
            # that p = 2*q + 1 and prime. This allows us to use lagrange
            # interpolation. However, in order for the whole g^{...} values to
            # be equal mod p, we need to have the exponents be equal mod (p-1)
            # (rather than mod q), so we multiply by 2. Remember that this
            # means that 2P(0) is our private key for the threshold encryption
            # (although it never gets created by any of the parties), and the
            # reason why g^2P(0) is the threshold public key.
            #
            # See (TODO: Add reference) for the full explanation

            gamma, delta = self._ciphertext[b_index]
            val = 1
            for trustee in trustee_indexes:
                p_decryption = self._trustees_partial_decryptions[trustee - 1]

                # Get the value (g^{rP(i)}) of the partial decryption block.
                #  Remember that each PartialDecryptionBlock is an object
                #  containing both the value of the block and its proof of
                #  partial decryption
                pd_block = p_decryption[b_index].value

                # We get \lambda_{i}(0) in the field Z_{q} for the trustees
                l_coeff = lagrange_coeffs[trustee]

                # We assert that the \lambda_{i}(0) was pre-calculated.
                assert (l_coeff != None), "lagrange coefficients for " \
                        "trustees in trustee_indexes " \
                        "should have been pre-calculated."

                # factor: $\left(g^{rP\left(i\right)}\right)^{2\lambda_{i}(0)}$
                factor = pow(pd_block, 2 * l_coeff, prime)

                val = (val * factor) % prime

            # We decrypt a block of message as m = delta/val = delta*(val)^{-1}.
            # (val)^{-1} the inverse of val in Z_{p}
            inv_val = pow(val, prime - 2, prime)
            m = (delta * inv_val) % prime

            # ... and add it to the bitstream.
            bitstream.put_num(m, block_size)

        # Return the decrypted bitstream
        return bitstream
Beispiel #23
0
 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
Beispiel #24
0
    def generate_commitment(self):
        """
		Generate a ThresholdEncryptionCommitment towards the threshold scheme.
		
		Returns:
			commitment::ThresholdEncryptionCommitment
		
		Throws:
			ThresholdEncryptionSetUpStateError -- If public keys are not loaded.
		"""
        # 0. Verify that all public keys are available for 1-to-1 encryption.
        for trustee in range(0, self._num_trustees):
            pk = self._trustees_simple_public_keys[trustee]
            if (pk == None):
                raise ThresholdEncryptionSetUpStateError(
                 "generate_commitment() must only be called after all the " \
                 "trustees' public keys have been registered with this " \
                 "ThresholdEncryptionSetUp instance. Missing public key " \
                 "for trustee %d." % trustee)

        # 1. Construct a new random polynomial of degree (threshold - 1)
        # Note: A polynomial of degree (t - 1) is determined by any t
        # (distinct) points.
        degree = self._threshold - 1
        nbits = self.cryptosystem.get_nbits()
        prime = self.cryptosystem.get_prime()
        generator = self.cryptosystem.get_generator()

        #  All calculations inside the polynomial are performed modulus q
        #  where q is such that p = 2*q + 1 (q is prime because of how we
        #  construct p when generating an EGCryptoSystem).
        #  We do this, because the values of the polynomial coefficients or its
        #  value at a certain point are always used as the exponent of elements
        #  in Z_{p}^{*}, and we know:
        #  a = b mod (p - 1) => g^a = g^b mod p
        #  (because g^{a-b} = g^{x(p-1)} = (g^x)^{p-1} = 1 mod p)
        #  We use q and not p - 1, because Z_{q} is a field (because q is
        #  prime), while the same would not be true for p-1, and we need the
        #  polynomials to be on a field in order to perform Lagrange
        #  Interpolation (see ThresholdDecryptionCombinator.decrypt_to_X()).
        #  This also means that 2*P(0) will be the threshold private key
        #  (never actually seen directly by the parties), and g^(2*P(0))
        #  the threshold public key.
        #  For the full explanation, see: (TODO: Add reference)

        q = (prime - 1) / 2
        polynomial = \
         CoefficientsPolynomial.new_random_polynomial(q, degree)

        # 2. Generate the public "coefficients" (actually g^coefficient for
        # each coefficient of the polynomial).
        public_coeficients = []
        for coeff in polynomial.get_coefficients():
            public_coeficients.append(pow(generator, coeff, prime))

        # 3. Generate the partial private keys for each trustee.
        # The partial private key for trustee j is P_{i}(j+1), with i the
        # trustee generating the commitment, its full private key is the sum
        # of the P_{i}(j+1) values generated by all trustees (including its own).
        # We must add 1 to the trustee index, since we want the trustee
        # indexes in the polynomial to start on 1, while our lists are 0
        # based.
        # (We must never reveal P(0) as this is the complete private key
        # of the full threshold scheme)

        # IMPORTANT: We encrypt each partial private key so that only its
        # intended recipient may read it.
        enc_partial_priv_keys = []
        for trustee in range(0, self._num_trustees):
            ppriv_key = polynomial(trustee + 1)  # P_{i}(j)
            trustee_pk = self._trustees_simple_public_keys[trustee]

            # Note that trustee public keys need not use the same cryptosystem
            # as the threshold encryption. In fact, they might not even have
            # the same bit length.
            bitstream = BitStream()
            bitstream.put_num(ppriv_key, nbits)
            ciphertext = trustee_pk.encrypt_bitstream(bitstream)
            enc_partial_priv_keys.append(ciphertext)

        # 4. Construct a ThresholdEncryptionCommitment object storing this
        # commitment and return it.
        return ThresholdEncryptionCommitment(self.cryptosystem,
                                             self._num_trustees,
                                             self._threshold,
                                             public_coeficients,
                                             enc_partial_priv_keys)
Beispiel #25
0
 def encrypt_bitstream(self, bitstream, pad_to=None, task_monitor=None):
     """
     Encrypts the given bitstream into a ciphertext object.
     
     Arguments:
         bitstream::BitStream-- A stream of bits to encrypt 
                                (see BitStream utility class).
         pad_to::int            -- Minimum size (in bytes) of the resulting 
                                ciphertext. Data will be padded before 
                                encryption to match this size.
         task_monitor::TaskMonitor    -- A task monitor for this task.
     
     Returns:
         ciphertext:Ciphertext    -- A ciphertext object encapsulating the 
                                    encrypted data.        
     """
     random = StrongRandom()
     
     ## PART 1
     # First, format the bitstream as per Ciphertext.py Note 001,
     # previous to encryption.
     #     [size (64 bits) | message (size bits) | padding (X bits) ]
     ##
     formated_bitstream = BitStream()
     
     # The first 64 encode the size of the actual data in bits
     SIZE_BLOCK_LENGTH = 64
     size_in_bits = bitstream.get_length()
     
     if(size_in_bits >= 2**SIZE_BLOCK_LENGTH):
         raise ValueError("The size of the bitstream to encrypt is larger " \
                          "than 16 Exabits. The current format for  " \
                          "PloneVote ciphertext only allows encrypting a  " \
                          "maximum of 16 Exabits of information.")
     
     formated_bitstream.put_num(size_in_bits, SIZE_BLOCK_LENGTH)
     
     # We then copy the contents of the original bitstream
     bitstream.seek(0)
     formated_bitstream.put_bitstream_copy(bitstream)
     
     # Finally, we append random data until we reach the desired pad_to 
     # length
     unpadded_length = formated_bitstream.get_length()
     if(pad_to != None and (pad_to * 8) > unpadded_length):
         full_length = pad_to * 8
     else:
         full_length = unpadded_length
     
     padding_left = full_length - unpadded_length
     
     while(padding_left > 1024):
         padding_bits = random.randint(1, 2**1024)
         formated_bitstream.put_num(padding_bits,1024)
         padding_left -= 1024
     
     if(padding_left > 0):
         padding_bits = random.randint(1, 2**padding_left)
         formated_bitstream.put_num(padding_bits, padding_left)
         padding_left = 0
     
     ## PART 2
     # We encrypt the formated bitsteam using ElGamal into a Ciphertext 
     # object.
     # See "Handbook of Applied Cryptography" Algorithm 8.18
     ##
     
     # block_size is the size of each block of bits to encrypt
     # since we can only encrypt messages in [0, p - 1]
     # we should use (nbits - 1) as the block size, where 
     # 2**(nbits - 1) < p < 2**nbits
     
     block_size = self.cryptosystem.get_nbits() - 1
     prime = self.cryptosystem.get_prime()
     generator = self.cryptosystem.get_generator()
     
     # We pull data from the bitstream one block at a time and encrypt it
     formated_bitstream.seek(0)
     ciphertext = \
         Ciphertext(self.cryptosystem.get_nbits(), self.get_fingerprint()) 
     
     plaintext_bits_left = formated_bitstream.get_length()
     
     # Check if we have a task monitor and register with it
     if(task_monitor != None):
         # We will do two tick()s per block to encrypt: one for generating 
         # the gamma component of the ciphertext block and another for the 
         # delta component (those are the two time intensive steps, 
         # because of exponentiation). 
         ticks = math.ceil((1.0 * plaintext_bits_left) / block_size) * 2
         encrypt_task_mon = \
             task_monitor.new_subtask("Encrypt data", expected_ticks = ticks)
     
     while(plaintext_bits_left > 0):
     
         # get next block (message, m, etc) to encrypt
         if(plaintext_bits_left >= block_size):
             block = formated_bitstream.get_num(block_size)
             plaintext_bits_left -= block_size
         else:
             block = formated_bitstream.get_num(plaintext_bits_left)
             # Encrypt as if the stream was filled with random data past its 
             # end, this avoids introducing a 0's gap during decryption to 
             # bitstream
             displacement = block_size - plaintext_bits_left
             block = block << displacement
             padding = random.randint(0, 2**displacement - 1)
             assert (padding / 2**displacement == 0), \
                         "padding should be at most displacement bits long"
             block = block | padding
             plaintext_bits_left = 0
         
         # Select a random integer k, 1 <= k <= p − 2
         k = random.randint(1, prime - 2)
         
         # Compute gamma and delta
         gamma = pow(generator, k, prime)
         if(task_monitor != None): encrypt_task_mon.tick()
         
         delta = (block * pow(self._key, k, prime)) % prime
         if(task_monitor != None): encrypt_task_mon.tick()
         
         # Add this encrypted data portion to the ciphertext object
         ciphertext.append(gamma, delta)
     
     # return the ciphertext object
     return ciphertext
Beispiel #26
0
    def encrypt_bitstream(self, bitstream, pad_to=None, task_monitor=None):
        """
        Encrypts the given bitstream into a ciphertext object.

        Arguments:
            bitstream::BitStream-- A stream of bits to encrypt
                                   (see BitStream utility class).
            pad_to::int            -- Minimum size (in bytes) of the resulting
                                   ciphertext. Data will be padded before
                                   encryption to match this size.
            task_monitor::TaskMonitor    -- A task monitor for this task.

        Returns:
            ciphertext:Ciphertext    -- A ciphertext object encapsulating the
                                       encrypted data.
        """
        random = StrongRandom()

        ## PART 1
        # First, format the bitstream as per Ciphertext.py Note 001,
        # previous to encryption.
        #     [size (64 bits) | message (size bits) | padding (X bits) ]
        ##
        formated_bitstream = BitStream()

        # The first 64 encode the size of the actual data in bits
        SIZE_BLOCK_LENGTH = 64
        size_in_bits = bitstream.get_length()

        if(size_in_bits >= 2**SIZE_BLOCK_LENGTH):
            raise ValueError("The size of the bitstream to encrypt is larger " \
                             "than 16 Exabits. The current format for  " \
                             "PloneVote ciphertext only allows encrypting a  " \
                             "maximum of 16 Exabits of information.")

        formated_bitstream.put_num(size_in_bits, SIZE_BLOCK_LENGTH)

        # We then copy the contents of the original bitstream
        bitstream.seek(0)
        formated_bitstream.put_bitstream_copy(bitstream)

        # Finally, we append random data until we reach the desired pad_to
        # length
        unpadded_length = formated_bitstream.get_length()
        if(pad_to != None and (pad_to * 8) > unpadded_length):
            full_length = pad_to * 8
        else:
            full_length = unpadded_length

        padding_left = full_length - unpadded_length

        while(padding_left > 1024):
            padding_bits = random.randint(1, 2**1024)
            formated_bitstream.put_num(padding_bits,1024)
            padding_left -= 1024

        if(padding_left > 0):
            padding_bits = random.randint(1, 2**padding_left)
            formated_bitstream.put_num(padding_bits, padding_left)
            padding_left = 0

        ## PART 2
        # We encrypt the formated bitsteam using ElGamal into a Ciphertext
        # object.
        # See "Handbook of Applied Cryptography" Algorithm 8.18
        ##

        # block_size is the size of each block of bits to encrypt
        # since we can only encrypt messages in [0, p - 1]
        # we should use (nbits - 1) as the block size, where
        # 2**(nbits - 1) < p < 2**nbits

        block_size = self.cryptosystem.get_nbits() - 1
        prime = self.cryptosystem.get_prime()
        generator = self.cryptosystem.get_generator()

        # We pull data from the bitstream one block at a time and encrypt it
        formated_bitstream.seek(0)
        ciphertext = \
            Ciphertext(self.cryptosystem.get_nbits(), self.get_fingerprint())

        plaintext_bits_left = formated_bitstream.get_length()

        # Check if we have a task monitor and register with it
        if(task_monitor != None):
            # We will do two tick()s per block to encrypt: one for generating
            # the gamma component of the ciphertext block and another for the
            # delta component (those are the two time intensive steps,
            # because of exponentiation).
            ticks = math.ceil((1.0 * plaintext_bits_left) / block_size) * 2
            encrypt_task_mon = \
                task_monitor.new_subtask("Encrypt data", expected_ticks = ticks)

        while(plaintext_bits_left > 0):

            # get next block (message, m, etc) to encrypt
            if(plaintext_bits_left >= block_size):
                block = formated_bitstream.get_num(block_size)
                plaintext_bits_left -= block_size
            else:
                block = formated_bitstream.get_num(plaintext_bits_left)
                # Encrypt as if the stream was filled with random data past its
                # end, this avoids introducing a 0's gap during decryption to
                # bitstream
                displacement = block_size - plaintext_bits_left
                block = block << displacement
                padding = random.randint(0, 2**displacement - 1)
                assert (padding // 2**displacement == 0), \
                            "padding should be at most displacement bits long"
                block = block | padding
                plaintext_bits_left = 0

            # Select a random integer k, 1 <= k <= p − 2
            k = random.randint(1, prime - 2)

            # Compute gamma and delta
            gamma = pow(generator, k, prime)
            if(task_monitor != None): encrypt_task_mon.tick()

            delta = (block * pow(self._key, k, prime)) % prime
            if(task_monitor != None): encrypt_task_mon.tick()

            # Add this encrypted data portion to the ciphertext object
            ciphertext.append(gamma, delta)

        # return the ciphertext object
        return ciphertext
	def generate_commitment(self):
		"""
		Generate a ThresholdEncryptionCommitment towards the threshold scheme.
		
		Returns:
			commitment::ThresholdEncryptionCommitment
		
		Throws:
			ThresholdEncryptionSetUpStateError -- If public keys are not loaded.
		"""
		# 0. Verify that all public keys are available for 1-to-1 encryption.
		for trustee in range(0, self._num_trustees):
			pk = self._trustees_simple_public_keys[trustee]
			if(pk == None):
				raise ThresholdEncryptionSetUpStateError(
					"generate_commitment() must only be called after all the " \
					"trustees' public keys have been registered with this " \
					"ThresholdEncryptionSetUp instance. Missing public key " \
					"for trustee %d." % trustee)
		
		# 1. Construct a new random polynomial of degree (threshold - 1)
		# Note: A polynomial of degree (t - 1) is determined by any t 
		# (distinct) points.
		degree = self._threshold - 1
		nbits = self.cryptosystem.get_nbits()
		prime = self.cryptosystem.get_prime()
		generator = self.cryptosystem.get_generator()
		
		#  All calculations inside the polynomial are performed modulus q
		#  where q is such that p = 2*q + 1 (q is prime because of how we 
		#  construct p when generating an EGCryptoSystem).
		#  We do this, because the values of the polynomial coefficients or its 
		#  value at a certain point are always used as the exponent of elements 
		#  in Z_{p}^{*}, and we know:
		#  a = b mod (p - 1) => g^a = g^b mod p
		#  (because g^{a-b} = g^{x(p-1)} = (g^x)^{p-1} = 1 mod p)
		#  We use q and not p - 1, because Z_{q} is a field (because q is 
		#  prime), while the same would not be true for p-1, and we need the 
		#  polynomials to be on a field in order to perform Lagrange 
		#  Interpolation (see ThresholdDecryptionCombinator.decrypt_to_X()). 
		#  This also means that 2*P(0) will be the threshold private key 
		#  (never actually seen directly by the parties), and g^(2*P(0)) 
		#  the threshold public key.
		#  For the full explanation, see: (TODO: Add reference)
		
		q = (prime - 1) / 2
		polynomial = \
			CoefficientsPolynomial.new_random_polynomial(q, degree)
		
		# 2. Generate the public "coefficients" (actually g^coefficient for 
		# each coefficient of the polynomial).
		public_coeficients = []
		for coeff in polynomial.get_coefficients():
			public_coeficients.append(pow(generator, coeff, prime)) 
		
		# 3. Generate the partial private keys for each trustee.
		# The partial private key for trustee j is P_{i}(j+1), with i the   
		# trustee generating the commitment, its full private key is the sum 
		# of the P_{i}(j+1) values generated by all trustees (including its own).
		# We must add 1 to the trustee index, since we want the trustee 
		# indexes in the polynomial to start on 1, while our lists are 0 
		# based.
		# (We must never reveal P(0) as this is the complete private key
		# of the full threshold scheme)
		
		# IMPORTANT: We encrypt each partial private key so that only its 
		# intended recipient may read it.
		enc_partial_priv_keys = []
		for trustee in range(0, self._num_trustees):
			ppriv_key = polynomial(trustee + 1) # P_{i}(j)
			trustee_pk = self._trustees_simple_public_keys[trustee]
			
			# Note that trustee public keys need not use the same cryptosystem 
			# as the threshold encryption. In fact, they might not even have 
			# the same bit length.
			bitstream = BitStream()
			bitstream.put_num(ppriv_key, nbits)
			ciphertext = trustee_pk.encrypt_bitstream(bitstream)
			enc_partial_priv_keys.append(ciphertext)
		
		# 4. Construct a ThresholdEncryptionCommitment object storing this 
		# commitment and return it.
		return ThresholdEncryptionCommitment(self.cryptosystem, 
			self._num_trustees, self._threshold, public_coeficients, 
			enc_partial_priv_keys)
	def decrypt_to_bitstream(self, task_monitor=None):
		"""
		Decrypt the ciphertext to a bitstream, using the partial decryptions.
		
		At least (threshold) correctly generated partial decryptions for the 
		ciphertext must be registered with this instance in order for 
		decryption to succeed.
		
		Arguments:
			task_monitor::TaskMonitor	-- A task monitor for this task.
		
		Returns:
			bitstream::Bitstream	-- A bitstream containing the unencrypted 
									   data.
									   
		Throws:
			InsuficientPartialDecryptionsError	-- If there aren't enough 
									partial decryptions registered with this 
									object to perform combined decryption.
		"""
		# Get the indexes of all trustees for which we have a registered
		# partial decryption. We use 1 based indexes here.
		trustee_indexes = []
		for trustee in range(1, self._num_trustees + 1):
			decryption = self._trustees_partial_decryptions[trustee - 1]
			if(decryption != None):
				trustee_indexes.append(trustee)
		
		# Check that we have enough trustees.
		if (len(trustee_indexes) < self._threshold):
			raise InsuficientPartialDecryptionsError("Not enough partial " \
					"decryptions have been registered with this object to " \
					"create a combined decryption. Registered partial " \
					"decryptions: %d. Required partial decryptions " \
					"(threshold): %d." \
					% (len(trustee_indexes), self._threshold))
					
		# We only need threshold trustees, exactly. Select those at random.
		random.shuffle(trustee_indexes)
		trustee_indexes = trustee_indexes[0:self._threshold]
		
		# We get the number of bits and prime for the cryptosystem
		nbits = self.cryptosystem.get_nbits()
		prime = self.cryptosystem.get_prime()
		#  prime = 2q + 1 with q prime by construction (see EGCryptoSystem).
		q = (prime - 1) / 2
		
		# See PublicKey.encrypt_bitstream for why we use nbits - 1 as the block 
		# size.
		block_size = self.cryptosystem.get_nbits() - 1
		
		# We initialize our bitstream
		bitstream = BitStream()
		
		# We pre-calculate the lagrange coefficients for the trustees in Z_{q}
		# for x = 0, to avoid doing so for each block of ciphertext.
		# See below for an explanation of the use of lagrange coefficients.
		lagrange_coeffs = [None for i in range(0,self._num_trustees + 1)]
		for trustee in trustee_indexes:
			lagrange_coeffs[trustee] = \
				_lagrange_coefficient(trustee_indexes, trustee, 0, q)
		
		# For each block of partial decryption/(gamma, delta) pair of ciphertext
		for b_index in range(0, self._ciphertext.get_length()):
			
			# Each partial decryption block is of the form g^{rP(i)}, where 
			# (gamma, delta) = (g^r, m*g^{r2P(0)}).
			#
			# We must first interpolate val=g^{r2P(0)} from the g^{rP(i)}'s.
			# Interpolation of val (LaTeX):
			# $g^{r2P(0)}=g^{\sum_{i\in I}r2P(i)\lambda_{i}(0)}=
			# \prod_{i\in I}\left(g^{rP\left(i\right)}\right)^{2\lambda_{i}(0)}$
			# where \lambda_{i}(0) are the Lagrange Coefficients.
			#
			# Note that the polynomials are in the field Z_{q} where q is such 
			# that p = 2*q + 1 and prime. This allows us to use lagrange 
			# interpolation. However, in order for the whole g^{...} values to 
			# be equal mod p, we need to have the exponents be equal mod (p-1) 
			# (rather than mod q), so we multiply by 2. Remember that this 
			# means that 2P(0) is our private key for the threshold encryption 
			# (although it never gets created by any of the parties), and the 
			# reason why g^2P(0) is the threshold public key.
			#
			# See (TODO: Add reference) for the full explanation
			
			gamma, delta = self._ciphertext[b_index]
			val = 1
			for trustee in trustee_indexes:
				p_decryption = self._trustees_partial_decryptions[trustee - 1]
				
				# Get the value (g^{rP(i)}) of the partial decryption block.
				#  Remember that each PartialDecryptionBlock is an object 
				#  containing both the value of the block and its proof of 
				#  partial decryption
				pd_block = p_decryption[b_index].value
				
				# We get \lambda_{i}(0) in the field Z_{q} for the trustees
				l_coeff = lagrange_coeffs[trustee]
				
				# We assert that the \lambda_{i}(0) was pre-calculated.
				assert (l_coeff != None), "lagrange coefficients for " \
										  "trustees in trustee_indexes " \
										  "should have been pre-calculated."
				
				# factor: $\left(g^{rP\left(i\right)}\right)^{2\lambda_{i}(0)}$
				factor = pow(pd_block, 2*l_coeff, prime)
				
				val = (val*factor) % prime
			
			# We decrypt a block of message as m = delta/val = delta*(val)^{-1}.
			# (val)^{-1} the inverse of val in Z_{p}
			inv_val = pow(val, prime - 2, prime)
			m = (delta*inv_val) % prime
			
			# ... and add it to the bitstream.
			bitstream.put_num(m, block_size)
		
		# Return the decrypted bitstream
		return bitstream