The purpose of this task to code and validate an implementation of the MD5 Message Digest Algorithm by coding the algorithm directly (not using a call to a built-in or external hashing library). For details of the algorithm refer to MD5 on Wikipedia or the MD5 definition in IETF RFC (1321).

 The implementation needs to implement the key functionality namely producing a correct message digest for an input string. It is not necessary to mimic all of the calling modes such as adding to a digest one block at a time over subsequent calls. 
 In addition to coding and verifying your implementation, note any challenges your language presented implementing the solution, implementation choices made, or limitations of your solution. 
 Solutions on this page should implement MD5 directly and NOT use built in (MD5) functions, call outs to operating system calls or library routines written in other languages as is common in the original MD5 task.
 The following are acceptable:
 An original implementation from the specification, reference implementation, or pseudo-code
 A translation of a correct implementation from another language
 A library routine in the same language; however, the source must be included here.


The solutions shown here will provide practical illustrations of bit manipulation, unsigned integers, working with little-endian data. Additionally, the task requires an attention to details such as boundary conditions since being out by even 1 bit will produce dramatically different results. Subtle implementation bugs can result in some hashes being correct while others are wrong. Not only is it critical to get the individual sub functions working correctly, even small errors in padding, endianness, or data layout will result in failure.

RFC 1321
                            hash code <== string 
   0xd41d8cd98f00b204e9800998ecf8427e <== ""  
   0x0cc175b9c0f1b6a831c399e269772661 <== "a"
   0x900150983cd24fb0d6963f7d28e17f72 <== "abc"
   0xf96b697d7cb7938d525a2f31aaf161d0 <== "message digest"
   0xc3fcd3d76192e4007dfb496cca67e13b <== "abcdefghijklmnopqrstuvwxyz"
   0xd174ab98d277d9f5a5611c2c9f419d9f <== "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
   0x57edf4a22be3c955ac49da2e2107b67a <== "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
In addition, intermediate outputs to aid in developing an implementation can be found here.

The MD5 Message-Digest Algorithm was developed by RSA Data Security, Inc. in 1991.



 Warning
Rosetta Code is not a place you should rely on for examples of code in critical roles, including security.
Also, note that MD5 has been broken and should not be used in applications requiring security. For these consider SHA2 or the upcoming SHA3.

section .text
org 0x100
	mov	di, md5_for_display
	mov	si, test_input_1
	mov	cx, test_input_1_len
	call	compute_md5
	call	display_md5
	mov	si, test_input_2
	mov	cx, test_input_2_len
	call	compute_md5
	call	display_md5
	mov	si, test_input_3
	mov	cx, test_input_3_len
	call	compute_md5
	call	display_md5
	mov	si, test_input_4
	mov	cx, test_input_4_len
	call	compute_md5
	call	display_md5
	mov	si, test_input_5
	mov	cx, test_input_5_len
	call	compute_md5
	call	display_md5
	mov	si, test_input_6
	mov	cx, test_input_6_len
	call	compute_md5
	call	display_md5
	mov	si, test_input_7
	mov	cx, test_input_7_len
	call	compute_md5
	call	display_md5
	mov	ax, 0x4c00
	int	21h
 
md5_for_display times 16 db 0
HEX_CHARS db '0123456789ABCDEF'
 
display_md5:
	mov	ah, 9
	mov	dx, display_str_1
	int	0x21
	push	cx
	push	si
	mov	cx, 16
	mov	si, di
	xor	bx, bx
.loop:
	lodsb
	mov	bl, al
	and	bl, 0x0F
	push	bx
	mov	bl, al
	shr	bx, 4
	mov	ah, 2
	mov	dl, [HEX_CHARS + bx]
	int	0x21
	pop	bx
	mov	dl, [HEX_CHARS + bx]
	int	0x21
	dec	cx
	jnz	.loop
	mov	ah, 9
	mov	dx, display_str_2
	int	0x21
	pop	si
	pop	cx
	test	cx, cx
	jz	do_newline
	mov	ah, 2
display_string:
	lodsb
	mov	dl, al
	int	0x21
	dec	cx
	jnz	display_string
do_newline:
	mov	ah, 9
	mov	dx, display_str_3
	int	0x21
	ret;
 
compute_md5:
	; si --> input bytes, cx = input len, di --> 16-byte output buffer
	; assumes all in the same segment
	cld
	pusha
	push	di
	push	si
	mov	[message_len], cx
 
	mov	bx, cx
	shr	bx, 6
	mov	[ending_bytes_block_num], bx
	mov	[num_blocks], bx
	inc	word [num_blocks]
	shl	bx, 6
	add	si, bx
	and	cx, 0x3f
	push	cx
	mov	di, ending_bytes
	rep	movsb
	mov	al, 0x80
	stosb
	pop	cx
	sub	cx, 55
	neg	cx
	jge	add_padding
	add	cx, 64
	inc	word [num_blocks]
add_padding:
	mov	al, 0
	rep	stosb
	xor	eax, eax
	mov	ax, [message_len]
	shl	eax, 3
	mov	cx, 8
store_message_len:
	stosb
	shr	eax, 8
	dec	cx
	jnz	store_message_len
	pop	si
	mov	[md5_a], dword INIT_A
	mov	[md5_b], dword INIT_B
	mov	[md5_c], dword INIT_C
	mov	[md5_d], dword INIT_D
block_loop:
	push	cx
	cmp	cx, [ending_bytes_block_num]
	jne	backup_abcd
	; switch buffers if towards the end where padding needed
	mov	si, ending_bytes
backup_abcd:
	push	dword [md5_d]
	push	dword [md5_c]
	push	dword [md5_b]
	push	dword [md5_a]
	xor	cx, cx
	xor	eax, eax
main_loop:
	push	cx
	mov	ax, cx
	shr	ax, 4
	test	al, al
	jz	pass0
	cmp	al, 1
	je	pass1
	cmp	al, 2
	je	pass2
	; pass3
	mov	eax, [md5_c]
	mov	ebx, [md5_d]
	not	ebx
	or	ebx, [md5_b]
	xor	eax, ebx
	jmp	do_rotate
 
pass0:
	mov	eax, [md5_b]
	mov	ebx, eax
	and	eax, [md5_c]
	not	ebx
	and	ebx, [md5_d]
	or	eax, ebx
	jmp	do_rotate
 
pass1:
	mov	eax, [md5_d]
	mov	edx, eax
	and	eax, [md5_b]
	not	edx
	and	edx, [md5_c]
	or	eax, edx
	jmp	do_rotate
 
pass2:
	mov	eax, [md5_b]
	xor	eax, [md5_c]
	xor	eax, [md5_d]
do_rotate:
	add	eax, [md5_a]
	mov	bx, cx
	shl	bx, 1
	mov	bx, [BUFFER_INDEX_TABLE + bx]
	add	eax, [si + bx]
	mov	bx, cx
	shl	bx, 2
	add	eax, dword [TABLE_T + bx]
	mov	bx, cx
	ror	bx, 2
	shr	bl, 2
	rol	bx, 2
	mov	cl, [SHIFT_AMTS + bx]
	rol	eax, cl
	add	eax, [md5_b]
	push	eax
	push	dword [md5_b]
	push	dword [md5_c]
	push	dword [md5_d]
	pop	dword [md5_a]
	pop	dword [md5_d]
	pop	dword [md5_c]
	pop	dword [md5_b]
	pop	cx
	inc	cx
	cmp	cx, 64
	jb	main_loop
	; add to original values
	pop	eax
	add	[md5_a], eax
	pop	eax
	add	[md5_b], eax
	pop	eax
	add	[md5_c], eax
	pop	eax
	add	[md5_d], eax
	; advance pointers
	add	si, 64
	pop	cx
	inc	cx
	cmp	cx, [num_blocks]
	jne	block_loop
	mov	cx, 4
	mov	si, md5_a
	pop	di
	rep	movsd
	popa
	ret
 
section .data
 
INIT_A equ 0x67452301
INIT_B equ 0xEFCDAB89
INIT_C equ 0x98BADCFE
INIT_D equ 0x10325476
 
SHIFT_AMTS db 7, 12, 17, 22, 5,  9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21
 
TABLE_T dd 0xD76AA478, 0xE8C7B756, 0x242070DB, 0xC1BDCEEE, 0xF57C0FAF, 0x4787C62A, 0xA8304613, 0xFD469501, 0x698098D8, 0x8B44F7AF, 0xFFFF5BB1, 0x895CD7BE, 0x6B901122, 0xFD987193, 0xA679438E, 0x49B40821, 0xF61E2562, 0xC040B340, 0x265E5A51, 0xE9B6C7AA, 0xD62F105D, 0x02441453, 0xD8A1E681, 0xE7D3FBC8, 0x21E1CDE6, 0xC33707D6, 0xF4D50D87, 0x455A14ED, 0xA9E3E905, 0xFCEFA3F8, 0x676F02D9, 0x8D2A4C8A, 0xFFFA3942, 0x8771F681, 0x6D9D6122, 0xFDE5380C, 0xA4BEEA44, 0x4BDECFA9, 0xF6BB4B60, 0xBEBFBC70, 0x289B7EC6, 0xEAA127FA, 0xD4EF3085, 0x04881D05, 0xD9D4D039, 0xE6DB99E5, 0x1FA27CF8, 0xC4AC5665, 0xF4292244, 0x432AFF97, 0xAB9423A7, 0xFC93A039, 0x655B59C3, 0x8F0CCC92, 0xFFEFF47D, 0x85845DD1, 0x6FA87E4F, 0xFE2CE6E0, 0xA3014314, 0x4E0811A1, 0xF7537E82, 0xBD3AF235, 0x2AD7D2BB, 0xEB86D391
BUFFER_INDEX_TABLE dw 0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 44, 48, 52, 56, 60, 4, 24, 44, 0, 20, 40, 60, 16, 36, 56, 12, 32, 52, 8, 28, 48, 20, 32, 44, 56, 4, 16, 28, 40, 52, 0, 12, 24, 36, 48, 60, 8, 0, 28, 56, 20, 48, 12, 40, 4, 32, 60, 24, 52, 16, 44, 8, 36
ending_bytes_block_num dw 0
ending_bytes times 128 db 0
message_len dw 0
num_blocks dw 0
md5_a dd 0
md5_b dd 0
md5_c dd 0
md5_d dd 0
 
display_str_1 db '0x$'
display_str_2 db ' <== "$'
display_str_3 db '"', 13, 10, '$'
 
test_input_1:
test_input_1_len equ $ - test_input_1
test_input_2 db 'a'
test_input_2_len equ $ - test_input_2
test_input_3 db 'abc'
test_input_3_len equ $ - test_input_3
test_input_4 db 'message digest'
test_input_4_len equ $ - test_input_4
test_input_5 db 'abcdefghijklmnopqrstuvwxyz'
test_input_5_len equ $ - test_input_5
test_input_6 db 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
test_input_6_len equ $ - test_input_6
test_input_7 db '12345678901234567890123456789012345678901234567890123456789012345678901234567890'
test_input_7_len equ $ - test_input_7