MD4 Cipher

Share your advanced PureBasic knowledge/code with the community.
infratec
Always Here
Always Here
Posts: 7576
Joined: Sun Sep 07, 2008 12:45 pm
Location: Germany

MD4 Cipher

Post by infratec »

Code: Select all

;
; https://oryx-embedded.com/doc/dir_a9aab978e0be629e504b25df915d67e8.html
;
; https://www.purebasic.fr/english/viewtopic.php?t=81310

CompilerIf #PB_Compiler_IsMainFile
  EnableExplicit
CompilerEndIf

#PB_Cipher_MD4 = 14

; MD4 block size
#MD4_BLOCK_SIZE = 64
; MD4 digest size
#MD4_DIGEST_SIZE = 16
; Minimum length of the padding string
#MD4_MIN_PAD_SIZE = 9


Structure Md4Context
  StructureUnion
    h.l[4]
    digest.a[16]
  EndStructureUnion
  StructureUnion
    x.l[16]
    buffer.a[64]
  EndStructureUnion
  size.i
  totalSize.q
EndStructure


; MD4 auxiliary functions
Macro ROL32(x, n)
  (((x) << (n)) | ((x) >> (32-(n))))
EndMacro

Macro F(x, y, z)
  (((x) & (y)) | (~(x) & (z)))
EndMacro

Macro G(x, y, z)
  (((x) & (y)) | ((x) & (z)) | ((y) & (z)))
EndMacro

Macro H(x, y, z)
  ((x) ! (y) ! (z))
EndMacro

Macro FF(a, b, c, d, x, s)
  a + (F(b, c, d) + (x))
  a & $FFFFFFFF
  a = ROL32(a, s)
EndMacro

Macro GG(a, b, c, d, x, s)
  a + (G(b, c, d) + (x) + $5A827999)
  a & $FFFFFFFF
  a = ROL32(a, s)
EndMacro

Macro HH(a, b, c, d, x, s)
  a + (H(b, c, d) + (x) + $6ED9EBA1)
  a & $FFFFFFFF
  a = ROL32(a, s)
EndMacro

; MD4 padding
Global Dim MD4_Padding.a(63)
MD4_Padding(0) = $80
; Static const uint8_t padding[64] =
; {
; 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
; 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
; };


Procedure md4Init(*context.Md4Context)
  
  ; Set initial hash value
  *context\h[0] = $67452301
  *context\h[1] = $EFCDAB89
  *context\h[2] = $98BADCFE
  *context\h[3] = $10325476
  
  ; Number of bytes in the buffer
  *context\size = 0
  ; Total length of the message
  *context\totalSize = 0
  
EndProcedure


Procedure.l htole32(LongValue.l)
  ProcedureReturn LongValue
EndProcedure


Procedure.l letoh32(LongValue.l)
  ProcedureReturn LongValue
EndProcedure


Structure md4LongArray
  v.l[0]
EndStructure

Procedure md4ProcessBlock(*context.Md4Context)
  
  Protected.i i
  Protected.q a, b, c, d
  Protected *x.md4LongArray
  
  
  ; Initialize the 4 working registers
  a = *context\h[0] & $FFFFFFFF
  b = *context\h[1] & $FFFFFFFF
  c = *context\h[2] & $FFFFFFFF
  d = *context\h[3] & $FFFFFFFF
  
  ; Process message in 16-word blocks
  *x = @*context\x[0]
  
  ; Convert from little-endian byte order To host byte order
  For i = 0 To 15
    *x\v[i] = letoh32(*x\v[i])
  Next i
  
  ; Round 1
  FF(a, b, c, d, *x\v[0],   3)
  FF(d, a, b, c, *x\v[1],   7)
  FF(c, d, a, b, *x\v[2],  11)
  FF(b, c, d, a, *x\v[3],  19)
  FF(a, b, c, d, *x\v[4],   3)
  FF(d, a, b, c, *x\v[5],   7)
  FF(c, d, a, b, *x\v[6],  11)
  FF(b, c, d, a, *x\v[7],  19)
  FF(a, b, c, d, *x\v[8],   3)
  FF(d, a, b, c, *x\v[9],   7)
  FF(c, d, a, b, *x\v[10], 11)
  FF(b, c, d, a, *x\v[11], 19)
  FF(a, b, c, d, *x\v[12],  3)
  FF(d, a, b, c, *x\v[13],  7)
  FF(c, d, a, b, *x\v[14], 11)
  FF(b, c, d, a, *x\v[15], 19)
  
  ; Round 2
  GG(a, b, c, d, *x\v[0],   3)
  GG(d, a, b, c, *x\v[4],   5)
  GG(c, d, a, b, *x\v[8],   9)
  GG(b, c, d, a, *x\v[12], 13)
  GG(a, b, c, d, *x\v[1],   3)
  GG(d, a, b, c, *x\v[5],   5)
  GG(c, d, a, b, *x\v[9],   9)
  GG(b, c, d, a, *x\v[13], 13)
  GG(a, b, c, d, *x\v[2],   3)
  GG(d, a, b, c, *x\v[6],   5)
  GG(c, d, a, b, *x\v[10],  9)
  GG(b, c, d, a, *x\v[14], 13)
  GG(a, b, c, d, *x\v[3],   3)
  GG(d, a, b, c, *x\v[7],   5)
  GG(c, d, a, b, *x\v[11],  9)
  GG(b, c, d, a, *x\v[15], 13)
  
  ; Round 3
  HH(a, b, c, d, *x\v[0],   3)
  HH(d, a, b, c, *x\v[8],   9)
  HH(c, d, a, b, *x\v[4],  11)
  HH(b, c, d, a, *x\v[12], 15)
  HH(a, b, c, d, *x\v[2],   3)
  HH(d, a, b, c, *x\v[10],  9)
  HH(c, d, a, b, *x\v[6],  11)
  HH(b, c, d, a, *x\v[14], 15)
  HH(a, b, c, d, *x\v[1],   3)
  HH(d, a, b, c, *x\v[9],   9)
  HH(c, d, a, b, *x\v[5],  11)
  HH(b, c, d, a, *x\v[13], 15)
  HH(a, b, c, d, *x\v[3],   3)
  HH(d, a, b, c, *x\v[11],  9)
  HH(c, d, a, b, *x\v[7],  11)
  HH(b, c, d, a, *x\v[15], 15)
  
  ; Update the hash value
  *context\h[0] = (*context\h[0] + a) & $FFFFFFFF
  *context\h[1] = (*context\h[1] + b) & $FFFFFFFF
  *context\h[2] = (*context\h[2] + c) & $FFFFFFFF
  *context\h[3] = (*context\h[3] + d) & $FFFFFFFF
  
EndProcedure


Procedure md4Update(*context.Md4Context, *Data, length.i)
  
  Protected.i n
  
  
  ; Process the incoming Data
  While length > 0
    ; The buffer can hold at most 64 bytes
    If length < 64 - *context\size
      n = length
    Else
      n = 64 - *context\size
    EndIf
    
    ; Copy the Data To the buffer
    CopyMemory(*Data, @*context\buffer[0] + *context\size, n)
    
    ; Update the MD4 context
    *context\size + n
    *context\totalSize + n
    ; Advance the Data pointer
    *Data = *Data + n
    ; Remaining bytes To process
    length - n
    
    ; Process message in 16-word blocks
    If *context\size = 64
      ; Transform the 16-word block
      md4ProcessBlock(*context)
      ; Empty the buffer
      *context\size = 0
    EndIf
  Wend
EndProcedure




Procedure md4Final(*context.Md4Context, *digest.Ascii)
  
  Protected.i i, paddingSize
  Protected.q totalSize
  
  
  ; Length of the original message (before padding)
  totalSize = *context\totalSize * 8
  
  ; Pad the message so that its length is congruent To 56 modulo 64
  If *context\size < 56
    paddingSize = 56 - *context\size
  Else
    paddingSize = 64 + 56 - *context\size
  EndIf
  
  ; Append padding
  md4Update(*context, @MD4_padding(0), paddingSize)
  
  ; Append the length of the original message
  *context\x[14] = htole32(totalSize & $FFFFFFFF)
  *context\x[15] = htole32(totalSize >> 32)
  
  ; Calculate the message digest
  md4ProcessBlock(*context)
  
  ; Convert from host byte order To little-endian byte order
;   For i = 0 To 3
;     *context\h[i] = htole32(*context\h[i])
;   Next i
  
  ; Copy the resulting digest
  If *digest
    CopyMemory(@*context\digest[0], *digest, #MD4_DIGEST_SIZE)
  EndIf
EndProcedure




Procedure.s FingerPrintMD4(*buffer, size.i)
  
  Protected.i i
  Protected Context.Md4Context
  Protected Hash$
  Protected Dim Digest.a(15)
  
  
  If *buffer And size > 0
    md4Init(@Context)
    md4Update(@Context, *buffer, size)
    md4Final(@Context, @Digest(0))
    For i = 0 To 15
      Hash$ + LCase(RSet(Hex(Digest(i)), 2, "0"))
    Next i
  EndIf
  
  ProcedureReturn Hash$
  
EndProcedure


Procedure.s StringFingerprintMD4(String$)
  
  Protected *UTF8
  Protected Hash$
  
  
  *UTF8 = UTF8(String$)
  If *UTF8
    Hash$ = FingerPrintMD4(*UTF8, MemorySize(*UTF8) - 1)
    FreeMemory(*UTF8)
  EndIf
  
  ProcedureReturn Hash$
  
EndProcedure


Procedure.s FileFingerprintMD4(Filename$, Offset.i=0, Length.i=0)
  
  Protected.i i, File, Size
  Protected Hash$
  Protected *Buffer
  
  
  File = ReadFile(#PB_Any, Filename$)
  If File
    
    If Offset > 0
      FileSeek(File, Offset)
    EndIf
    
    If Length
      Size = Length
    Else
      Size = Lof(File)
      If Offset
        Size - Offset
      EndIf
    EndIf
    
    *Buffer = AllocateMemory(Size, #PB_Memory_NoClear)
    If *Buffer
      If ReadData(File, *Buffer, Size) = Size
        Hash$ = FingerPrintMD4(*Buffer, Size)
      EndIf
      FreeMemory(*Buffer)
    EndIf
    
    CloseFile(File)
  EndIf
  
  ProcedureReturn Hash$
  
EndProcedure


CompilerIf #PB_Compiler_IsMainFile
  ;-Demo
  
  Debug "MD4:       " + StringFingerprintMD4("Hello World 1234567890")
  Debug "Should be: bb51e309cdbbd13434bd1f7dde51f44d"
  
CompilerEndIf
Last edited by infratec on Sun Aug 04, 2024 8:52 pm, edited 1 time in total.
User avatar
Kwai chang caine
Always Here
Always Here
Posts: 5494
Joined: Sun Nov 05, 2006 11:42 pm
Location: Lyon - France

Re: MD4 Cipher

Post by Kwai chang caine »

Apparently works here :wink:
I don't know that exist an old MD4 before MD5 :oops:
So thanks for sharing 8)
ImageThe happiness is a road...
Not a destination
Post Reply