I am trying to implement the hash_df function defined as part of the NIST HashDRBG standard specified here https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-90Ar1.pdf section 10.3.1.
The given algorithm for hash_df from the above document is:
Hash_df (input_string, no_of_bits_to_return):
1. input_string: The string to be hashed.
2. no_of_bits_to_return: The number of bits to be returned by Hash_df. The maximum length (max_number_of_bits) is implementation dependent, but shall be less than or equal to (255 × outlen). no_of_bits_to_return is represented as a 32-bit integer.
Output:
1. status: The status returned from Hash_df. The status will indicate SUCCESS or ERROR_FLAG.
2. requested_bits: The result of performing the Hash_df.
Hash_df Process:
1. temp = the Null string.
2. len = ceiling divide (no_of_bits_to_return / outlen)
3. counter = 0x01. Comment: An 8-bit binary value representing the
integer "1".
4. For i = 1 to len do
Comment : In step 4.1, no_of_bits_to_return is used as a 32-bit string.
4.1 temp = temp || Hash (counter || no_of_bits_to_return || input_string).
4.2 counter = counter + 1.
5. requested_bits = leftmost (temp, no_of_bits_to_return).
6. Return (SUCCESS, requested_bits).
This hash_df is then used in the instantiation function defined in section 10.1.1.2 of the same document (note that nist uses || as concatenation):
Hash_DRBG_Instantiate_algorithm (entropy_input, nonce, personalization_string,
security_strength):
1. entropy_input: The string of bits obtained from the randomness source.
2. nonce: A string of bits as specified in Section 8.6.7.
3. personalization_string: The personalization string received from the consuming application. Note that the length of the personalization_string may be zero.
4. security_strength: The security strength for the instantiation. This parameter is optional for Hash_DRBG, since it is not used.
Output:
1. initial_working_state: The initial values for V, C, and reseed_counter (see Section 10.1.1.1).
Hash_DRBG Instantiate Process:
1. seed_material = entropy_input || nonce ||personalization_string.
2. seed = Hash_df (seed_material, seedlen).
3. V = seed.
4. C = Hash_df ((0x00 || V), seedlen). Comment: Precede V with a byte of zeros.
5. reseed_counter = 1.
6. Return (V, C, reseed_counter).
Given that, I came up with the following hash_df and instantiation function:
def int_to_bytes(x: int) -> bytes:
return x.to_bytes((x.bit_length() + 7) // 8, 'big')
def int_from_bytes(xbytes: bytes) -> int:
return int.from_bytes(xbytes, 'big')
def ceildiv(a, b):
return -(a // -b)
class hashDrbg:
V = b''
C = b''
reseed_counter = 0
zer = int_to_bytes(0)
one = int_to_bytes(1)
two = int_to_bytes(2)
three = int_to_bytes(3)
def __init__(self):
pass
def hash_df(self, input_string, no_of_bits_to_return):
temp = b''
length = ceildiv(no_of_bits_to_return, 256) # 256 is the outlen for sha 256
counter = int_to_bytes(1)
for i in range(1, length + 1):
m_digest = hashlib.sha256()
m_digest.update(counter + int_to_bytes(no_of_bits_to_return) + input_string)
temp = temp + m_digest.digest()
counter = int_from_bytes(counter)
counter += 1
counter = int_to_bytes(counter)
requested_bits = temp[0:no_of_bits_to_return // 8]
return requested_bits
def instantiate(self, entropy, nonce, personalization_string, security_strength):
# these 4 lines are to do nonce + 1, not sure if needed or not
# nonce = int(nonce, 16)
# nonce += 1
# nonce = int_to_bytes(nonce)
# nonce = nonce.hex()
seed_material = entropy + nonce + personalization_string
seed = self.hash_df(bytes.fromhex(seed_material), 440)
self.V = seed
self.C = self.hash_df(self.zer + self.V, 440)
self.reseed_counter = 1
I have found these test vectors https://raw.githubusercontent.com/coruus/nist-testvectors/master/csrc.nist.gov/groups/STM/cavp/documents/drbg/drbgtestvectors/drbgvectors_pr_false/Hash_DRBG.txt which show the intermediate values of every step along the way. Currently, I am only concerned with getting the instantiation/hash_df step correct before moving on to reseed and generate. With the following input values for instantiate (from those test vectors):
EntropyInput = 6c623aea73bc8a59e28c6cd9c7c7ec8ca2e75190bd5dcae5978cf0c199c23f4f
Nonce = e55db067a0ed537e66886b7cda02f772
PersonalizationString = 1e59d798810083d1ff848e90b25c9927e3dfb55a0888b0339566a9f9ca7542dc
I should get the following internal values:
V = 3fb73388bd7b779aa94ff1738bfc7b80ff907a1755589e3a7646db08df608f58e3ff3b660abc591932490a5a03f79ebc6de8e655848d99
C = 48723f992acce55207e3882d69ba89684d083da32dc2e2d9fc171423c27f2024701d273447e56585607dc13d3964ae35030b6e4683988c
reseed counter = 1
But I end up with the following values:
hash_drbg = hashDrbg()
# instantiate
hash_drbg.instantiate(entropy_input, instantiation_nonce, perso_string, 128)
print("V = " + hash_drbg.V.hex())
print("C = " + hash_drbg.C.hex())
print("reseed counter = " + str(hash_drbg.reseed_counter))
V = c2b3ed040bd5d636e010c664c2952d3eaa7c071c8ef8758c77a1fd517126c8717450f32f493530c4c973e668deedcd8a3e5d1fedfdbb5b
C = 0cb62b53bf805576040f1ccbb91465cb68a21d427320bb148cf6f73c35d2cc98a6ecbf502cf4f8c5c55241c8ef9add384cea38cd7613b3
reseed counter = 1
So my question is, where am I going wrong in the above functions that is causing me to get incorrect results?
Aucun commentaire:
Enregistrer un commentaire