def reseed_algorithm(self, entropy_input, additional_input):
        '''
        CTR_DRBG_Reseed_algorithm as specified in NIST SP800-90A,
        Section 10.2.1.4, page 55.
        Reseeds either with (10.2.1.4.2) or without (10.2.1.4.1)
        a derivation function.
        '''

        if self.derivation:
            # 10.2.1.4.2, Step 1
            seed_material = entropy_input + additional_input
            seed_material = utilities.binstr_zeropad(
                seed_material, self.seedlen)
            # 10.2.1.4.2, Step 2
            status, seed_material = self.block_cipher_df(
                seed_material, self.seedlen)
            if status != 'SUCCESS':
                return 'ERROR', 'block_cipher_df returned [%s]' % status
        else:
            # 10.2.1.4.1, Step 1-2
            additional_input = utilities.binstr_zeropad(
                additional_input, self.seedlen)
            # 10.2.1.4.1, Step 3
            seed_material = utilities.binstr_xor(
                entropy_input, additional_input)

        # 10.2.1.4.1, Step 4 / 10.2.1.4.2, Step 3
        self.update(seed_material)

        # 10.2.1.4.1, Step 5 / 10.2.1.4.2, Step 4
        self.counter = 1

        # 10.2.1.4.1, Step 6 / 10.2.1.4.2, Step 5
        return
    def generate_algorithm(self, n_bits, additional_input = None):
        '''
        CTR_DRGB_Generate_algorithm, as specified in NIST SP800-90A,
        Section 10.2.1.5, page 56.
        Generate bits either with (10.2.1.5.2) or without (10.2.1.5.2)
        a derivation function.
        '''

        # Step 1
        if self.counter > self.max_interval and not self.reseed_failed:
            return ('RESEED', '')

        # Step 2
        if additional_input:
            if self.derivation:
                # 10.2.1.5.2, Step 2.1
                status, additional_input = self.block_cipher_df(
                    additional_input, self.seedlen)
                if status != 'SUCCESS':
                    return 'ERROR', 'block_cipher_df returned [%s]' % status
            else:
                # 10.2.1.5.1, Step 2.1-2.2
                additional_input = utilities.binstr_zeropad(
                    additional_input, self.seedlen)
            # 10.2.1.5.1, Step 2.3 / 10.2.1.5.2, Step 2.2
            self.update(additional_input)
        else:
            additional_input = utilities.binstr_zeropad('', self.seedlen)

        # Step 3
        temp = ''

        # Step 4
        while len(temp) < (n_bits + 7) / 8:
            # Step 4.1
            self.V = utilities.binstr_increment(self.V, self.outlen)
            # Step 4.2
            output_block = self.block_encrypt(self.key, self.V)
            # Step 4.3
            temp = temp + output_block

        # Step 5
        returned_bits = utilities.binstr_leftmost(temp, n_bits)

        # Step 6
        self.update(additional_input)

        # Step 7
        self.counter += 1

        # Step 8
        if not self.reseed_failed:
            return ('SUCCESS', returned_bits)
        else:
            return ('RESEED', returned_bits)
    def instantiate_algorithm(self, entropy_input, nonce,
                              personalization, security_strength):
        '''
        CTR_DRBG_Instatntiate_algorithm as specified in NIST SP800-90A
        Section 10.2.1.3, page 53.
        Instantiates either with (10.2.1.3.2) or without (10.2.1.3.1)
        a derivation function.
        '''

        # Set some parameters based on the security strength
        self.keylen     = self.cipher['keylengths'][self.security_strength]
        self.outlen     = self.cipher['block size']
        self.seedlen    = self.keylen + self.outlen

        if not personalization:
            personalization = ''

        # When a derivation function is used (mandatory unless full entropy)
        if self.derivation:
            # 10.2.1.3.2, Step 1
            seed_material = entropy_input + nonce + personalization
            seed_material = utilities.binstr_zeropad(
                seed_material, self.seedlen)
            # 10.2.1.3.2, Step 2
            status, seed_material = self.block_cipher_df(
                seed_material, self.seedlen)
            if status != 'SUCCESS':
                raise ValueError('block_cipher_df returned [%s]' % status)

        # When full entropy is available and a derivation function is not used
        else:
            # 10.2.1.3.1, Step 1-2
            personalization = utilities.binstr_zeropad(
                personalization, self.seedlen)
            # 10.2.1.3.1, Step 3
            seed_material = utilities.binstr_xor(
                entropy_input, personalization)

        # 10.2.1.3.1, Step 4 / 10.2.1.3.2, Step 3
        self.key = utilities.binstr_zeropad('', self.keylen)

        # 10.2.1.3.1, Step 5 / 10.2.1.3.2, Step 4
        self.V   = utilities.binstr_zeropad('', self.outlen)

        # 10.2.1.3.1, Step 6 / 10.2.1.3.2, Step 5
        self.update(seed_material)

        # 10.2.1.3.1, Step 7 / 10.2.1.3.2, Step 6
        self.counter = 1

        # 10.2.1.3.1, Step 8 / 10.2.1.3.2, Step 7
        return
    def bcc(self, key, data):
        '''
        Block Cipher Chaining as specified in NIST SP800-90A,
        Section 10.4.3, page 70
        '''

        # Step 1
        chaining_value = utilities.binstr_zeropad('', self.outlen)

        # Step 2
        nn = len(data) * 8 / self.outlen

        # Step 3
        # See below within the loop

        # Step 4
        for ii in range(nn):
            # Step 3 again
            block = data[ii * self.outlen / 8:(ii + 1) * self.outlen / 8]
            # Step 4.1
            input_block = utilities.binstr_xor(chaining_value, block)
            # Step 4.2
            chaining_value = self.block_encrypt(key, input_block)

        # Step 5
        output_block = chaining_value

        # Step 6
        return output_block
    def block_cipher_df(self, input_string, n_bits):
        '''
        Block_Encrypt as specified in NIST SP800-90A,
        Section 10.4.2, page 68
        '''

        # Step 1
        if n_bits > 512:
            return 'ERROR', None

        # Step 2
        L = struct.pack('>I', len(input_string))

        # Step 3
        N = struct.pack('>I', n_bits / 8)

        # Step 4
        S = L + N + input_string + chr(0x80)

        # Step 5
        last_block_filled = len(S) % (self.outlen / 8)
        last_block_pad    = (self.outlen / 8) - last_block_filled
        S = S + (chr(0) * last_block_pad)

        # Step 6
        temp = ''

        # Step 7
        ii = 0

        # Step 8
        K = ''.join(chr(kk) for kk in range(0x20))[:self.keylen / 8]

        # Step 9
        while len(temp) < (self.keylen + self.outlen) / 8:
            # Step 9.1
            IV = utilities.binstr_zeropad(struct.pack('>I', ii), self.outlen)
            # Step 9.2
            temp = temp + self.bcc(K, IV + S)
            # Step 9.3
            ii += 1

        # Step 10
        K = temp[:self.keylen / 8]

        # Step 11
        X = temp[self.keylen / 8:(self.keylen + self.outlen) / 8]

        # Step 12
        temp = ''

        # Step 13
        while len(temp) < n_bits / 8:
            # Step 13.1
            X = self.block_encrypt(K, X)
            # Step 13.2
            temp = temp + X

        # Step 14
        requested_bits = temp[:n_bits / 8]

        # Step 15
        return 'SUCCESS', requested_bits