MD5

Mac OSX specific forum
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

MD5

Post by wilbert »

I don't know why but the PB MD5 functions on OS X are much slower compared to those on Windows (at least on my iMac).
Here's an alternative that's a bit faster. The structure of the code is based on the SHA code from Netmaestro.

Code: Select all

;================================================================
;
; Library Commands:         MD5_FingerPrint()
;                           MD5_FileFingerPrint()
;
;================================================================
;
; 
; Usage: 
;
; result$ = MD5_Fingerprint(*address, length)
; result$ = MD5_FileFingerprint(file$, [,*progress])
;
; Progress callback function:
;
; ProcedureC MyCallBack(value.i)
;   ; value is 0 to 100 representing percentage completed
; Endprocedure 

;================================================================
;                      STRUCTURE                      
;================================================================

Structure md5_context
  state.l [4]
  buffer.a [64]
  padding.a [64]
  processed.q
  jobsize.q
  callback.l
EndStructure

;================================================================
;                      HELPER MACROS                     
;================================================================

;  0 - 15  f = d xor (b and (c xor d))
Macro P0(a,b,c,d, r, w, k)
  !mov ebp, c
  !xor ebp, d
  !and ebp, b
  !xor ebp, d
  !add a, ebp
  !add a, k
  !add a, [edi + (w * 4)]
  !rol a, r
  !add a, b
EndMacro

; 16 - 31  f = c xor (d and (b xor c))
Macro P1(a,b,c,d, r, w, k)
  !mov ebp, b
  !xor ebp, c
  !and ebp, d
  !xor ebp, c
  !add a, ebp
  !add a, k
  !add a, [edi + (w * 4)]
  !rol a, r
  !add a, b
EndMacro

; 32 - 47  f = b xor c xor d
Macro P2(a,b,c,d, r, w, k)
  !mov ebp, d
  !xor ebp, b
  !xor ebp, c
  !add a, ebp
  !add a, k
  !add a, [edi + (w * 4)]
  !rol a, r
  !add a, b
EndMacro

; 48 - 63  f = c xor (b or (not d))
Macro P3(a,b,c,d, r, w, k)
  !mov ebp, d
  !not ebp
  !or ebp, b
  !xor ebp, c
  !add a, ebp
  !add a, k
  !add a, [edi + (w * 4)]
  !rol a, r
  !add a, b
EndMacro

;================================================================
;                  LOCAL FUNCTIONS
;================================================================

Procedure md5_starts(*ctx.md5_context)
  
  *ctx\state[0] = $67452301
  *ctx\state[1] = $efcdab89
  *ctx\state[2] = $98badcfe
  *ctx\state[3] = $10325476
  
EndProcedure

Procedure md5_process_addr__()
  
  !mov eax, md5_process_start
  ProcedureReturn
  !md5_process_start:
  
  !sub esp, 16
  !mov [esp     ], ebx
  !mov [esp +  4], esi
  !mov [esp +  8], edi
  !mov [esp + 12], ebp
  
  !mov esi, [esp + 20] ; *ctx
  !mov edi, [esp + 24] ; *bytes
  
  !mov eax, [esi     ]
  !mov ebx, [esi +  4]
  !mov ecx, [esi +  8]
  !mov edx, [esi + 12]
  
  !md5_process_loop:
  P0(eax, ebx, ecx, edx,  7,  0, 0xd76aa478)
  P0(edx, eax, ebx, ecx, 12,  1, 0xe8c7b756)
  P0(ecx, edx, eax, ebx, 17,  2, 0x242070db)
  P0(ebx, ecx, edx, eax, 22,  3, 0xc1bdceee)
  P0(eax, ebx, ecx, edx,  7,  4, 0xf57c0faf)
  P0(edx, eax, ebx, ecx, 12,  5, 0x4787c62a)
  P0(ecx, edx, eax, ebx, 17,  6, 0xa8304613)
  P0(ebx, ecx, edx, eax, 22,  7, 0xfd469501)
  P0(eax, ebx, ecx, edx,  7,  8, 0x698098d8)
  P0(edx, eax, ebx, ecx, 12,  9, 0x8b44f7af)
  P0(ecx, edx, eax, ebx, 17, 10, 0xffff5bb1)
  P0(ebx, ecx, edx, eax, 22, 11, 0x895cd7be)
  P0(eax, ebx, ecx, edx,  7, 12, 0x6b901122)
  P0(edx, eax, ebx, ecx, 12, 13, 0xfd987193)
  P0(ecx, edx, eax, ebx, 17, 14, 0xa679438e)
  P0(ebx, ecx, edx, eax, 22, 15, 0x49b40821)
  P1(eax, ebx, ecx, edx,  5,  1, 0xf61e2562)
  P1(edx, eax, ebx, ecx,  9,  6, 0xc040b340)
  P1(ecx, edx, eax, ebx, 14, 11, 0x265e5a51)
  P1(ebx, ecx, edx, eax, 20,  0, 0xe9b6c7aa)
  P1(eax, ebx, ecx, edx,  5,  5, 0xd62f105d)
  P1(edx, eax, ebx, ecx,  9, 10, 0x02441453)
  P1(ecx, edx, eax, ebx, 14, 15, 0xd8a1e681)
  P1(ebx, ecx, edx, eax, 20,  4, 0xe7d3fbc8)
  P1(eax, ebx, ecx, edx,  5,  9, 0x21e1cde6)
  P1(edx, eax, ebx, ecx,  9, 14, 0xc33707d6)
  P1(ecx, edx, eax, ebx, 14,  3, 0xf4d50d87)
  P1(ebx, ecx, edx, eax, 20,  8, 0x455a14ed)
  P1(eax, ebx, ecx, edx,  5, 13, 0xa9e3e905)
  P1(edx, eax, ebx, ecx,  9,  2, 0xfcefa3f8)
  P1(ecx, edx, eax, ebx, 14,  7, 0x676f02d9)
  P1(ebx, ecx, edx, eax, 20, 12, 0x8d2a4c8a)
  P2(eax, ebx, ecx, edx,  4,  5, 0xfffa3942)
  P2(edx, eax, ebx, ecx, 11,  8, 0x8771f681)
  P2(ecx, edx, eax, ebx, 16, 11, 0x6d9d6122)
  P2(ebx, ecx, edx, eax, 23, 14, 0xfde5380c)
  P2(eax, ebx, ecx, edx,  4,  1, 0xa4beea44)
  P2(edx, eax, ebx, ecx, 11,  4, 0x4bdecfa9)
  P2(ecx, edx, eax, ebx, 16,  7, 0xf6bb4b60)
  P2(ebx, ecx, edx, eax, 23, 10, 0xbebfbc70)
  P2(eax, ebx, ecx, edx,  4, 13, 0x289b7ec6)
  P2(edx, eax, ebx, ecx, 11,  0, 0xeaa127fa)
  P2(ecx, edx, eax, ebx, 16,  3, 0xd4ef3085)
  P2(ebx, ecx, edx, eax, 23,  6, 0x04881d05)
  P2(eax, ebx, ecx, edx,  4,  9, 0xd9d4d039)
  P2(edx, eax, ebx, ecx, 11, 12, 0xe6db99e5)
  P2(ecx, edx, eax, ebx, 16, 15, 0x1fa27cf8)
  P2(ebx, ecx, edx, eax, 23,  2, 0xc4ac5665)
  P3(eax, ebx, ecx, edx,  6,  0, 0xf4292244)
  P3(edx, eax, ebx, ecx, 10,  7, 0x432aff97)
  P3(ecx, edx, eax, ebx, 15, 14, 0xab9423a7)
  P3(ebx, ecx, edx, eax, 21,  5, 0xfc93a039)
  P3(eax, ebx, ecx, edx,  6, 12, 0x655b59c3)
  P3(edx, eax, ebx, ecx, 10,  3, 0x8f0ccc92)
  P3(ecx, edx, eax, ebx, 15, 10, 0xffeff47d)
  P3(ebx, ecx, edx, eax, 21,  1, 0x85845dd1)
  P3(eax, ebx, ecx, edx,  6,  8, 0x6fa87e4f)
  P3(edx, eax, ebx, ecx, 10, 15, 0xfe2ce6e0)
  P3(ecx, edx, eax, ebx, 15,  6, 0xa3014314)
  P3(ebx, ecx, edx, eax, 21, 13, 0x4e0811a1)
  P3(eax, ebx, ecx, edx,  6,  4, 0xf7537e82)
  P3(edx, eax, ebx, ecx, 10, 11, 0xbd3af235)
  P3(ecx, edx, eax, ebx, 15,  2, 0x2ad7d2bb)
  P3(ebx, ecx, edx, eax, 21,  9, 0xeb86d391)
  !add eax, [esi     ]
  !mov [esi     ], eax
  !add ebx, [esi +  4]
  !mov [esi +  4], ebx
  !add ecx, [esi +  8]
  !mov [esi +  8], ecx
  !add edx, [esi + 12]
  !mov [esi + 12], edx
  
  !add edi, 64
  !dec dword [esp + 28]
  !jnz md5_process_loop
  
  !mov ebx, [esp     ]
  !mov esi, [esp +  4]
  !mov edi, [esp +  8]
  !mov ebp, [esp + 12]
  !add esp, 16
  !ret
EndProcedure

Procedure md5_set_digest_addr__()
  !mov eax, md5_set_digest_start
  ProcedureReturn
  !md5_set_digest_start:
  !push ebx
  !mov ebx, [esp + 8]
  !mov edx, [esp + 12]
  !mov ecx, 15
  !md5_set_digest_loop:
  !mov al, [ebx + ecx]
  !mov ah, al
  !shr ah, 4
  !and ax, 0x0f0f
  !or ax, 0x9090
  !daa
  !adc al, 0x40
  !daa
  !ror ax, 8
  !daa
  !adc al, 0x40
  !daa
  !or ax, 0x2020
  !mov [edx + ecx * 2], ax
  !sub ecx, 1
  !jnc md5_set_digest_loop
  !pop ebx
  !ret
EndProcedure

PrototypeC md5_process_proto(*ctx, *bytes, blocks)
PrototypeC md5_set_digest_proto(*ctx, *output)
Global md5_process.md5_process_proto = md5_process_addr__()
Global md5_set_digest.md5_set_digest_proto = md5_set_digest_addr__()

Procedure md5_update(*ctx.md5_context, *input, length)  
  
  Protected left, fill, blocks
  
  If length = 0 : ProcedureReturn : EndIf
  
  left = *ctx\processed & $3f
  fill = 64 - left
  
  *ctx\processed + length
  
  If left And length >= fill
    CopyMemory(*input, @*ctx\buffer + left, fill)
    md5_process(*ctx, @*ctx\buffer, 1)
    length - fill
    *input + fill
    left = 0
  EndIf
  
  blocks = length >> 6
  length & $3f
  If blocks
    md5_process(*ctx, *input, blocks)
    *input + blocks << 6  
  EndIf
  
  If length 
    CopyMemory(*input, @*ctx\buffer + left, length)
  EndIf
  
EndProcedure

Procedure md5_finish(*ctx.md5_context, *output)
  
  Protected msglen.q, padn
  
  *ctx\padding[0] = $80
  padn = 56 - *ctx\processed & 63
  If padn <= 0 : padn + 64 : EndIf
  
  msglen = *ctx\jobsize << 3
  
  md5_update(*ctx, @*ctx\padding, padn)
  md5_update(*ctx, @msglen, 8)
  md5_set_digest(*ctx, *output)
  
  If *ctx\callback
    CallCFunctionFast(*ctx\callback, 100)
  EndIf
  
EndProcedure

;================================================================
;                  EXPORTED FUNCTIONS
;================================================================

ProcedureDLL.s MD5_Fingerprint(*datapointer, length) ; Data address, data size
  
  Protected digest.s
  Protected *output, *ctx.md5_context
  
  *ctx = AllocateMemory(SizeOf(md5_context))
  *ctx\jobsize = length
  
  *output = AllocateMemory(32)
  
  md5_starts(*ctx)
  md5_update(*ctx, *datapointer, length)
  md5_finish(*ctx, *output)
  digest = PeekS(*output, 32, #PB_Ascii)
  
  FreeMemory(*output)
  FreeMemory(*ctx)
  
  ProcedureReturn digest
  
EndProcedure

ProcedureDLL.s MD5_FileFingerprint(filename.s, *callback = 0) ; filename$, [ ,<procaddress> ]
  
  Protected digest.s
  Protected *datapointer, *output, *ctx.md5_context
  Protected bytesread, fresult
  
  fresult = OpenFile(#PB_Any, filename)
  If fresult
    
    *ctx = AllocateMemory(SizeOf(md5_context))
    *ctx\jobsize = Lof(fresult)
    *ctx\callback = *callback
    
    *datapointer = AllocateMemory($40000)
    *output = AllocateMemory(32)
    
    md5_starts(*ctx)
    
    While Not Eof(fresult)
      bytesread = ReadData(fresult, *datapointer, $40000)
      md5_update(*ctx, *datapointer, bytesread)
      If *ctx\callback
        CallCFunctionFast(*ctx\callback, 100 * *ctx\processed / *ctx\jobsize)
      EndIf
    Wend
    CloseFile(fresult)
    
    md5_finish(*ctx, *output)
    digest = PeekS(*output, 32, #PB_Ascii)
    
    FreeMemory(*output)
    FreeMemory(*datapointer)
    FreeMemory(*ctx)
    
  EndIf
  ProcedureReturn digest
  
EndProcedure
Shiba
New User
New User
Posts: 3
Joined: Tue May 14, 2024 10:16 am

Re: MD5

Post by Shiba »

Dear Wilbert thanks for posting these code but i have some problems with it :)

i use these code but i get. a wrong md5 of it and even some weird chars inside
if i use "test" as string i get this digest back (wrong hash and a ` inside)

7f9e7abcac32c484b`cd5d943738a5ec

did i miss something ?

Code: Select all


Procedure.s getMd5()
  
  Protected digest.s
  Protected *output, *ctx.md5_context
  
  *StrBuffer = AllocateMemory(4)
  PokeS(*StrBuffer, "test", 4, #PB_Ascii)
  ;ShowMemoryViewer(*NameBuffer, 16)
  
  length = MemorySize(*NameBuffer)
  
  *ctx = AllocateMemory(SizeOf(md5_context))
  *ctx\jobsize = length
  
  *output = AllocateMemory(32)
  
  md5_starts(*ctx)
  md5_update(*ctx, *NameBuffer, length)
  md5_finish(*ctx, *output)
  digest = PeekS(*output, 32, #PB_Ascii)
  
  FreeMemory(*output)
  FreeMemory(*ctx)
  
  ProcedureReturn digest
  
EndProcedure
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: MD5

Post by wilbert »

Is there any reason for using my code instead of the built in functions from the Cipher library ?
https://www.purebasic.com/documentation ... index.html

My code in this thread is 12+ years old and 32 bit x86 only.
It is not adapted for 64 bit x64 and will also not work on an Arm cpu or with the C backend.
I could probably make it work again for Intel/AMD cpu with Asm backend if there's a good reason for that.
Windows (x64)
Raspberry Pi OS (Arm64)
Shiba
New User
New User
Posts: 3
Joined: Tue May 14, 2024 10:16 am

Re: MD5

Post by Shiba »

Hey Wilbert thanks for your Response. Yeah i used your Code because i can modify here the Table values and init Numbers.
Which i cant so with the built in.

I use the following Version Pb 6.04 x86.
wilbert
PureBasic Expert
PureBasic Expert
Posts: 3942
Joined: Sun Aug 08, 2004 5:21 am
Location: Netherlands

Re: MD5

Post by wilbert »

In your code you are starting out with *StrBuffer and continue with *NameBuffer .
That's probably what is causing the problems because *NameBuffer doesn't point to anything.
Windows (x64)
Raspberry Pi OS (Arm64)
Shiba
New User
New User
Posts: 3
Joined: Tue May 14, 2024 10:16 am

Re: MD5

Post by Shiba »

Hey Wilbert sorry that was not the problem, but ya i mismatched that variable name also .
I have switched to an older version of PB to 5.71 and now your code is working properly.

But anyway thanks :)
Post Reply