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