Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.

MD5 Hash in Fortran revisit

JohnDrohan
Novice
1,176 Views

In case anyone is trying to use the code from this forum post:

https://community.intel.com/t5/Intel-Fortran-Compiler/MD5-Hash/td-p/801183

I wanted to let folks know that there was a bug I fixed that might be helpful to someone in the future.

I changed this line of code:

character*((int(len(string)/64)+1)*64) newString

to:

character*((int((len(string)+8)/64)+1)*64) newString

The inclusion of the "+8" allows for the 8 bit length portion of the MD5 padding spec to be accounted for and prevent string overflows for strings that need fewer than 8 bits of padding.

Not that this code is being used many places....I just recently used it and had to debug/fix the issue so figured I'd post about it just in case.

1 Reply
wtstephens
New Contributor I
151 Views

Thanks for that fix!  Also had to fix the "blank" rather than "0" bug in the original code -- using a "z8.8" format.

 

Then turned it into a module. Here it is:

module md5_hash

contains

! **********************************************************************
character*32 function md5(string)
! ---------------------------------------------------------------------*
!     Programmierer    : VEZ2/Pieper                                   *
!     Version          : 1.0                                           *
!     letzte nderung  : 07.05.2010                                     *
!     Aufgabe          : Erzeugt aus einem String einen MD5 Hashwert   *
! **********************************************************************
implicit none
character*(*), intent(in) :: string

character*((int((len(string)+8)/64)+1)*64) newString
character*8 wtmp

integer(kind=4) j,n1,n2,n3,n4,pos
integer(kind=4) r(64),k(64),h0,h1,h2,h3,a,b,c,d,f,g,temp,w(16),i,intLen
integer(kind=8) hoch32
real(kind=8) sinus,absolut,real8i

r = [ 7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22, &
      5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20, &
      4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23, &
      6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21]

do i=1,64
    real8i = floatk(int8(i))
    sinus = dsin(real8i)
    absolut = dabs(sinus)
    hoch32 = 2.**32.
    k(i) = int8(absolut * floatk(hoch32))
enddo

h0 = #67452301
h1 = #EFCDAB89
h2 = #98BADCFE
h3 = #10325476

j = len(string)+1
newString(:j) = string // char(128)
i = mod(j, 64)
do while(i /= 56)
    j = j + 1
    newString(j:j) = char(0)
    i = mod(j, 64)
enddo

intLen = len(string)*8
do i = 0,3
    temp = intLen .and. #FF
    j = j + 1
    newString(j:j) = char(temp)
    intLen = shiftr(intLen, 8)
enddo

do i = 1,4
    j = j + 1
    newString(j:j) = char(0)
enddo

do i = 1,int(len(newString)/64)

    do j = 1,16
        pos = (j-1)*4+(i-1)*64
        n1 = ichar(newString(4+pos:4+pos))
        n2 = ichar(newString(3+pos:3+pos))
        n3 = ichar(newString(2+pos:2+pos))
        n4 = ichar(newString(1+pos:1+pos))
        
        write(wtmp,'(4(z2.2))') n1,n2,n3,n4
        read(wtmp,'(z8)') w(j)
    enddo

    a = h0
    b = h1
    c = h2
    d = h3

    do j = 1,64
        if (j >= 1 .and. j <= 16) then
            f = (b .and. c) .or. ((.not. b) .and. d)
            g = j
        else if (j >= 17 .and. j <= 32) then
            f = (d .and. b) .or. ((.not. d) .and. c)
            g = mod(5*(j-1) + 1, 16) + 1
        else if (j >= 33 .and. j <= 48) then
            f = ieor(b, ieor(c, d))
            g = mod(3*(j-1) + 5, 16) + 1
        else if (j >= 49 .and. j <= 64) then
            f = ieor(c, (b .or. (.not. d)))
            g = mod(7*(j-1), 16) + 1
        endif
        
        temp = d
        d = c
        c = b
        b = b + leftrotate((a + f + k(j) + w(g)) , r(j))
        a = temp
    enddo

    h0 = h0 + a
    h1 = h1 + b
    h2 = h2 + c
    h3 = h3 + d
enddo
h0 = umdrehen(h0)
h1 = umdrehen(h1)
h2 = umdrehen(h2)
h3 = umdrehen(h3)
write(md5,'(4(z8.8))') h0,h1,h2,h3
return
endfunction

! **********************************************************************
pure function leftrotate (x, c) result(i4value)
! ---------------------------------------------------------------------*
!     Programmierer    : VEZ2/Pieper                                   *
!     Version          : 1.0                                           *
!     letzte nderung  : 07.05.2010                                     *
!     Aufgabe          : Fhrt ein Leftrotate der Bits durch            *
! **********************************************************************
implicit none
integer*4, intent(in) :: x, c
integer*4 :: i4value

integer*4 result1,result2

result1 = shiftl(x,c)
result2 = shiftr(x, (32-c))
i4value = result1 .or. result2
return
endfunction

! **********************************************************************
pure function umdrehen(zahl_input) result(i4value)
! ---------------------------------------------------------------------*
!     Programmierer    : VEZ2/Pieper                                   *
!     Version          : 1.0                                           *
!     letzte nderung  : 07.05.2010                                    *
!     Aufgabe          : Macht aus Big Endian -> Little Endian Bits    *
! **********************************************************************
implicit none
integer*4, intent(in) :: zahl_input
integer*4 :: i4value

integer*4 i,tmp,zahl

zahl = zahl_input
i4value = 0
do i = 1,4
    i4value = shiftl(i4value, 8)
    tmp = zahl .and. #FF
    i4value = i4value + tmp;
    zahl = shiftr(zahl, 8)
enddo
return
endfunction

endmodule

Here is a test program:

program md5_test
    use md5_hash

    character*32 md5_test_1 / "9EAB500708CAD82AAD8E156BDA5ED03A" /
    character*32 md5_test_2 / "F2F8F10BE9359F85503A242DE2E0665B" /
    character*32 md5_test_3 / "7215EE9C7D9DC229D2921A40E899EC5F" /
    character*32 c32
    character*100 ctest

    ctest = "TestString1"
    c32 = md5(trim(ctest))
    if( c32 /= md5_test_1 )then
        write(*,'(4A)') 'Mismatch! md5=',c32,' vs ',md5_test_1
    else
        write(*,'(4A)') 'Test #1 of md5 is Ok! md5("',trim(ctest),'") is: ',c32
    endif
    write(*,*)
    
    ctest = "SecondTestString"
    c32 = md5(trim(ctest))
    if( c32 /= md5_test_2 )then
        write(*,'(4A)') 'Mismatch! md5=',c32,' vs ',md5_test_2
    else
        write(*,'(4A)') 'Test #2 of md5 is Ok! md5("',trim(ctest),'") is: ',c32
    endif
    write(*,*)

    c32 = md5(" ")
    if( c32 /= md5_test_3 )then
        write(*,'(4A)') 'Mismatch! md5=',c32,' vs ',md5_test_3
    else
        write(*,'(4A)') 'Test #3 of md5 is Ok! md5(" ") is: ',c32
    endif

endprogram

Which I compared to output from the Windows certutil utility program:

certutil -hashfile testfile.txt md5

 

0 Kudos
Reply