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 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(self, n_bits, security_strength = None, prediction_resistance = None, additional_input = ''): 'Generate bits from the entropy engine.' # # If we have a DRBG then use it if self.drbg: status, drbg_bits = self.drbg.generate( n_bits, security_strength, prediction_resistance, additional_input) # The DRBG, once instantiated, should never fail if status != 'SUCCESS' and status != 'RESEED_FAILED': return status, "DRBG failed" # If we are combining the DRBG with raw input then get raw bits if self.raw: status, raw_bits = self.raw.get_entropy_input( security_strength, n_bits, n_bits, prediction_resistance) # Failure here is allowable, because we still have the DRBG if status != 'SUCCESS': logging.debug( "Using drbg only. %s, %s", status, raw_bits) self.total_bytes += len(drbg_bits) return 'DRBG_ONLY', drbg_bits # If we have both sources working then XOR them together comb_bits = utilities.binstr_xor(drbg_bits, raw_bits) self.total_bytes += len(comb_bits) return 'SUCCESS', comb_bits # If we only have a DRBG, then return just those bits else: self.total_bytes += len(drbg_bits) return 'SUCCESS', drbg_bits # If we have no DRBG then we must have a raw entropy source elif self.raw: status, raw_bits = self.raw.get_entropy_input( security_strength, n_bits, n_bits, prediction_resistance) # If this fails with no DRBG to back it up, return an error if status != 'SUCCESS': return status, "Raw source failed" # Otherwise return the raw bits self.total_bytes += len(raw_bits) return 'SUCCESS', raw_bits # If we have neither DRBG nor raw source, we cannot generate bits return 'ERROR', "Neither DRBG nor raw source available"
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 update(self, data): ''' CTR_DRBG_Update as specified in NIST SP800-90A, Section 10.2.1.2, page 52. Updates the internal state using the provided data.' ''' if (self.seedlen & 7) or (self.keylen & 7) or (self.outlen & 7): raise NotImplementedError( 'Only 0 mod 8 is supported for key/block sizes.') if type(data) is not str: raise ValueError('CTR_DRBG_Update requires string input.') seedlenbytes = self.seedlen / 8 if len(data) != seedlenbytes: raise ValueError( ('CTR_DRBG_Update requires exactly seedlen of data ' '(received %d bytes, expected %d).') % (len(data), seedlenbytes)) # Step 1 temp = '' # Step 2 while len(temp) < seedlenbytes: # Step 2.1 self.V = utilities.binstr_increment(self.V, self.outlen) # Step 2.2 output_block = self.block_encrypt(self.key, self.V) # Step 2.3 temp = temp + output_block # Step 3-4, the XOR function automatically truncates to match # input lengths temp = utilities.binstr_xor(data, temp) # Step 5 self.key = temp[:self.keylen / 8] # Step 6 self.V = temp[self.keylen / 8:][:self.outlen / 8] # Step 7 self.scheduled = False return