Skip to content

RIPEMD

RIPEMD-160

Example

import hashlib, binascii

ripemd160_hash = hashlib.new('ripemd160', b'hello')

print("ripemd-160('hello') =", binascii.hexlify(ripemd160_hash.digest()))
#ripemd-160('hello') = b'108f07b8382412612c048d07d13f814118445acd'

Implementation

from cryptopals_lib import *

class ripemd():
	"""docstring for ripemd"""
	def __init__(self):
		self.state = [0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0]
		self.buffer_indexes = [0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,
							   7,4,13,1,10,6,15,3,12,0,9,5,2,14,11,8,
							   3,10,14,4,9,15,8,1,2,7,0,6,13,11,5,12,
							   1,9,11,10,0,8,12,4,13,3,7,15,14,5,6,2,
							   4,0,5,9,7,12,2,10,14,1,3,8,11,6,15,13,
							   5,14,7,0,9,2,11,4,13,6,15,8,1,10,3,12,
							   6,11,3,7,0,13,5,10,14,15,8,12,4,9,1,2,
							   15,5,1,3,7,14,6,9,11,8,12,2,10,0,4,13,
							   8,6,4,1,3,11,15,0,5,12,2,13,9,7,10,14,
							   12,15,10,4,1,5,8,7,6,2,13,14,0,3,9,11]

		self.rotate_index = [11,14,15,12,5,8,7,9,11,13,14,15,6,7,9,8,
							 7,6,8,13,11,9,7,15,7,12,15,9,11,7,13,12,
							 11,13,6,7,14,9,13,15,14,8,13,6,5,12,7,5,
							 11,12,14,15,14,15,9,8,9,14,5,6,8,6,5,12,
							 9,15,5,11,6,8,13,12,5,12,13,14,11,8,5,6,
							 8,9,9,11,13,15,15,5,7,7,8,11,14,14,12,6,
							 9,13,15,7,12,8,9,11,7,7,12,7,6,15,13,11,
							 9,7,15,11,8,6,6,14,12,13,5,14,13,13,7,5,
							 15,5,8,11,14,14,6,14,6,9,12,9,12,5,15,8,
							 8,5,12,9,12,5,14,6,8,13,6,5,15,13,11,11]

		self.round_varables = [0x00000000, 0x5A827999, 0x6ED9EBA1, 0x8F1BBCDC, 0xA953FD4E,
							   0x50A28BE6, 0x5C4DD124, 0x6D703EF3, 0x7A6D76E9, 0x00000000]

		self.block_size = 64

		self.message_end = 0x80


	def _set_message(self, message):
		# Convert to bytes if not already
		byte_message = bytearray(message)

		# Get Length shifted by 8 and limit to 64bit int
		input_length_data = len(byte_message)

		# Append 0x80 to the end of the message as a end of message byte
		byte_message.append(self.message_end)

		# Pad the data until the number of bits are equal devisable by the rate.
		# Not including the 8 bytes for the length added at the end
		while (len(byte_message) + 8) % self.block_size != 0:
			byte_message.append(0x00)

		# Append the length data to the message. But first convert to bits
		byte_message += int_to_bytes_length(input_length_data << 3, 8, False)
		# Convert to the number of bits

		return byte_message

	def hash(self, message):
		# Setup message with padding and length data
		byte_message = self._set_message(message)

		# Opperate on each of the block_size 64 bit chunks
		for chunk in to_blocks(byte_message, self.block_size):
			#print(f"Chunk: {chunk}, {len(chunk)}")
			self._hash_message_chunk(chunk)

		# Convert Intagers to Byte string
		return intarray_to_bytes(self.state, 4)

		
	def hash_digest(self, message):
		return self.hash(message).hex()

	def _round_opperation(self, round_idx, temp_buffers, block_buffers):
		round_number = round_idx // 16

		#Round Opperation depends on the Round Number
		if round_number == 0 or round_number == 9:
			#Do round Opperation 
			#Round 1 and 10: x ^ y ^ z
			temp = temp_buffers[0] + (temp_buffers[1] ^ temp_buffers[2] ^ temp_buffers[3]) + self.round_varables[round_number]

		elif round_number == 1 or round_number == 8:
			#Do round Opperations
			#Round 2 and 9: (x & y) | (((~x) % 0x100000000) & z)
			temp = temp_buffers[0] + ((temp_buffers[1] & temp_buffers[2]) | asint32(~temp_buffers[1]) & temp_buffers[3]) + self.round_varables[round_number]

		elif round_number == 2 or round_number == 7:
			#Do round Opperations
			#Round 3 and 8: (x | ((~y) % 0x100000000)) ^ z
			temp = temp_buffers[0] + ((temp_buffers[1] | asint32(~temp_buffers[2])) ^ temp_buffers[3]) + self.round_varables[round_number]

		elif round_number == 3 or round_number == 6:
			#Do round Opperations
			#Round 4 and 7: (x & z) | (((~z) % 0x100000000) & y)
			temp = temp_buffers[0] + ((temp_buffers[1] & temp_buffers[3]) | asint32(~temp_buffers[3]) & temp_buffers[2]) + self.round_varables[round_number]

		elif round_number == 4 or round_number == 5:
			#Do round Opperations
			#Round 5 and 6: x ^ (y | ((~z) % 0x100000000))
			temp = temp_buffers[0] + (temp_buffers[1] ^ (temp_buffers[2] | asint32(~temp_buffers[3]))) + self.round_varables[round_number]

		else:
			raise Exception("Inviald Round")

		#Set the Two buffers
		#print(round_number, temp, block_buffers[self.buffer_indexes[round_idx]] )
		temp_buffers[0] = asint32(shift_rotate_left(asint32(temp + block_buffers[self.buffer_indexes[round_idx]]), self.rotate_index[round_idx]) + temp_buffers[4])
		temp_buffers[2] = shift_rotate_left(temp_buffers[2], 10)

		#Rotate tempbuffers a,b,c,d,e -> e,a,b,c,d
		return [temp_buffers[4], temp_buffers[0], temp_buffers[1], temp_buffers[2], temp_buffers[3]]

	def _hash_message_chunk(self, block):
		#Convert Blocks to 32bit ints
		block_buffers = bytes_to_intarray(block, 4)

		#Clone Internal Buffers
		temp_buffers = self.state[:]

		#Do Inital 5 rounds
		for idx in range(16*5):
			#print(temp_buffers)
			temp_buffers = self._round_opperation(idx, temp_buffers, block_buffers)

		#Safe the Output and reset the temp buffers
		half_round_ouputs = temp_buffers 
		temp_buffers = self.state[:]

		#Do Final 5 rounds
		for idx in range(16*5, 16*10):
			temp_buffers = self._round_opperation(idx, temp_buffers, block_buffers)


		#print(f"state:             {self.state}")
		#print(f"half_round_ouputs: {half_round_ouputs}")
		#print(f"temp_buffers:      {temp_buffers }")
		
		#Set new internal buffers
		temp = asint32(self.state[1] + half_round_ouputs[2] + temp_buffers[3])
		self.state[1] = asint32(self.state[2] + half_round_ouputs[3] + temp_buffers[4])
		self.state[2] = asint32(self.state[3] + half_round_ouputs[4] + temp_buffers[0])
		self.state[3] = asint32(self.state[4] + half_round_ouputs[0] + temp_buffers[1])
		self.state[4] = asint32(self.state[0] + half_round_ouputs[1] + temp_buffers[2])
		self.state[0] = temp

if __name__ == '__main__':
	new = ripemd()

	print(new.hash_digest(b'hello this is a test'))
	#f51960af7dd4813a587ab26388ddab3b28d1f7b4