Code: Select all
;
; https://oryx-embedded.com/doc/dir_a9aab978e0be629e504b25df915d67e8.html
;
; https://www.purebasic.fr/english/viewtopic.php?t=81309
CompilerIf #PB_Compiler_IsMainFile
EnableExplicit
CompilerEndIf
#PB_Cipher_MD2 = 12
; MD2 block size
#MD2_BLOCK_SIZE = 16
; MD2 digest size
#MD2_DIGEST_SIZE = 16
; Minimum length of the padding string
#MD2_MIN_PAD_SIZE = 1
Structure Md2Context
StructureUnion
x.a[48]
digest.a[16]
EndStructureUnion
m.a[16]
c.a[16]
size.l
EndStructure
; MD2 constants
DataSection
MD2_s:
Data.a $29, $2E, $43, $C9, $A2, $D8, $7C, $01, $3D, $36, $54, $A1, $EC, $F0, $06, $13
Data.a $62, $A7, $05, $F3, $C0, $C7, $73, $8C, $98, $93, $2B, $D9, $BC, $4C, $82, $CA
Data.a $1E, $9B, $57, $3C, $FD, $D4, $E0, $16, $67, $42, $6F, $18, $8A, $17, $E5, $12
Data.a $BE, $4E, $C4, $D6, $DA, $9E, $DE, $49, $A0, $FB, $F5, $8E, $BB, $2F, $EE, $7A
Data.a $A9, $68, $79, $91, $15, $B2, $07, $3F, $94, $C2, $10, $89, $0B, $22, $5F, $21
Data.a $80, $7F, $5D, $9A, $5A, $90, $32, $27, $35, $3E, $CC, $E7, $BF, $F7, $97, $03
Data.a $FF, $19, $30, $B3, $48, $A5, $B5, $D1, $D7, $5E, $92, $2A, $AC, $56, $AA, $C6
Data.a $4F, $B8, $38, $D2, $96, $A4, $7D, $B6, $76, $FC, $6B, $E2, $9C, $74, $04, $F1
Data.a $45, $9D, $70, $59, $64, $71, $87, $20, $86, $5B, $CF, $65, $E6, $2D, $A8, $02
Data.a $1B, $60, $25, $AD, $AE, $B0, $B9, $F6, $1C, $46, $61, $69, $34, $40, $7E, $0F
Data.a $55, $47, $A3, $23, $DD, $51, $AF, $3A, $C3, $5C, $F9, $CE, $BA, $C5, $EA, $26
Data.a $2C, $53, $0D, $6E, $85, $28, $84, $09, $D3, $DF, $CD, $F4, $41, $81, $4D, $52
Data.a $6A, $DC, $37, $C8, $6C, $C1, $AB, $FA, $24, $E1, $7B, $08, $0C, $BD, $B1, $4A
Data.a $78, $88, $95, $8B, $E3, $63, $E8, $6D, $E9, $CB, $D5, $FE, $3B, $00, $1D, $39
Data.a $F2, $EF, $B7, $0E, $66, $58, $D0, $E4, $A6, $77, $72, $F8, $EB, $75, $4B, $0A
Data.a $31, $44, $50, $B4, $8F, $ED, $1F, $1A, $DB, $99, $8D, $33, $9F, $11, $83, $14
EndDataSection
Procedure md2Init(*context.Md2Context)
; Initialize the 48-byte buffer X
FillMemory(@*context\x[0], 48, 0)
; Clear checksum
FillMemory(@*context\c[0], 16, 0)
; Number of bytes in the buffer
*context\size = 0
EndProcedure
Structure md2AsciiArray
v.a[0]
EndStructure
Procedure md2ProcessBlock(*m.md2AsciiArray, *x.md2AsciiArray, *c.md2AsciiArray)
Protected.i j, k, m, t
; Update checksum
t = *c\v[15]
For j = 0 To 15
*c\v[j] ! PeekA(?MD2_s + (*m\v[j] ! t))
t = *c\v[j]
Next j
; Copy current block into X
For j = 0 To 15
*x\v[16 + j] = *m\v[j]
*x\v[32 + j] = *x\v[16 + j] ! *x\v[j]
Next j
; Encrypt block (18 rounds)
t = 0
For j = 0 To 17
; Round j
For k = 0 To 47
*x\v[k] ! PeekA(?MD2_s + t)
t = *x\v[k]
Next k
; Set t To (t + j) modulo 256
t = (t + j) & $FF
Next j
EndProcedure
Procedure md2Update(*context.Md2Context, *Data, length.i)
Protected n.i
; Process the incoming Data
While length > 0
; The buffer can hold at most 16 bytes
If length < 16 - *context\size
n = length
Else
n = 16 - *context\size
EndIf
; Copy the Data To the buffer
CopyMemory(*Data, @*context\m[0] + *context\size, n)
; Update the MD2 context
*context\size + n
; Advance the Data pointer
*Data + n
; Remaining bytes To process
length - n
; Process message in 16-word blocks
If *context\size = 16
; Transform the 16-word block
md2ProcessBlock(@*context\m[0], @*context\x[0], @*context\c[0])
; Empty the buffer
*context\size = 0
EndIf
Wend
EndProcedure
Procedure md2Final(*context.Md2Context, *digest.Ascii)
Protected n.i
; Pad the message so that its length is congruent To 0 modulo 16
n = 16 - *context\size
; Append padding bytes
FillMemory(@*context\m[0] + *context\size, n, n)
; Transform the 16-word block
md2ProcessBlock(@*context\m[0], @*context\x[0], @*context\c[0])
; Append the checksum
CopyMemory(@*context\c[0], @*context\m[0], 16)
; Transform the 16-word block
md2ProcessBlock(@*context\m[0], @*context\x[0], @*context\c[0])
; Copy the resulting digest
If *digest <> #Null
CopyMemory(@*context\digest[0], *digest, #MD2_DIGEST_SIZE)
EndIf
EndProcedure
Procedure.s FingerPrintMD2(*buffer, size.i)
Protected.i i
Protected Context.Md2Context
Protected Hash$
Protected Dim Digest.a(15)
If *buffer And Size > 0
md2Init(@Context)
md2Update(@Context, *buffer, size)
md2Final(@Context, @Digest(0))
For i = 0 To 15
Hash$ + LCase(RSet(Hex(Digest(i)), 2, "0"))
Next i
EndIf
ProcedureReturn Hash$
EndProcedure
Procedure.s StringFingerprintMD2(String$)
Protected *UTF8
Protected Hash$
*UTF8 = UTF8(String$)
If *UTF8
Hash$ = FingerPrintMD2(*UTF8, MemorySize(*UTF8) - 1)
FreeMemory(*UTF8)
EndIf
ProcedureReturn Hash$
EndProcedure
Procedure.s FileFingerprintMD2(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$ = FingerPrintMD2(*Buffer, Size)
EndIf
FreeMemory(*Buffer)
EndIf
CloseFile(File)
EndIf
ProcedureReturn Hash$
EndProcedure
CompilerIf #PB_Compiler_IsMainFile
;-Demo
Debug "MD2: " + StringFingerprintMD2("Hello World 1234567890")
Debug "Should be: 2f31fac7bc62bd91d603b1e5f6c51362"
CompilerEndIf