ooRexx logo
   1: #!/usr/bin/rexx
   2: 
   3: /* ---------------------------------------------------------------- */
   4: /* Implements MD5 digest calculation                                */
   5: /* ---------------------------------------------------------------- */
   6: /*                                                                  */
   7: /* Originally by Ruurd J. Idenburg                                  */                                                             
   8: /*                                                                  */
   9: /* No copyright, no licence, no guarantees or warrantees, be it     */
  10: /* explicit, implicit or whatever. Usage is totally and completely  */
  11: /* at the users own risk, the author shall not be liable for any    */ 
  12: /* damages whatsoever, for any reason whatsoever.                   */
  13: /*                                                                  */
  14: /* Please keep this comment block intact when modifying this code   */
  15: /* and add a note with date and a description.                      */
  16: /*                                                                  */
  17: /* Implementation mainly based on pseudocode in:                    */
  18: /* https://en.wikipedia.org/wiki/MD5                                */
  19: /*                                                                  */
  20: /* Requires OORexx 4.2.0 or later                                   */
  21: /* ---------------------------------------------------------------- */
  22: /* 2016/01/13 - Initial version                                     */
  23: /* 2016/04/27 - Added initial comment block.                        */
  24: /* ---------------------------------------------------------------- */
  25: 
  26: -- standard numeric digits of 9 is not enough in this case
  27: ::options digits 20
  28: 
  29: ::class md5 public
  30: 
  31: ::method init
  32:   expose a0 b0 c0 d0 count buffer index K. s  -- instance variables
  33:   use strict arg chunk=""
  34:   -- Initialize message digest
  35:   a0 = .int32~new('67452301'x,"C")   -- A
  36:   b0 = .int32~new('efcdab89'x,"C")   -- B
  37:   c0 = .int32~new('98badcfe'x,"C")   -- C
  38:   d0 = .int32~new('10325476'x,"C")   -- D
  39:   -- The 512 bit chunk buffer
  40:   buffer = .mutablebuffer~new('00'x~copies(64),64)
  41:   -- The position in the buffer to insert new input
  42:   index = 1
  43:   -- message bytecount 
  44:   count = 0 
  45:   -- initialize leftrotate amounts
  46:   nrs = .array~of(7,12,17,22)
  47:   s = nrs~union(nrs)~union(nrs)~union(nrs)
  48:   nrs = .array~of(5,9,14,20)
  49:   s = s~union(nrs)~union(nrs)~union(nrs)~union(nrs)
  50:   nrs = .array~of(4,11,16,23)
  51:   s = s~union(nrs)~union(nrs)~union(nrs)~union(nrs)
  52:   nrs = .array~of(6,10,15,21)
  53:   s = s~union(nrs)~union(nrs)~union(nrs)~union(nrs)
  54:   -- initialize sinus derived constants.
  55:   -- sin function from RXMath Library shipped with OORexx
  56:   -- see ::routine directive at the end of the code 
  57:   do i=0 to 63
  58:     K.i = .int32~new(((2**32)*(sin(i+1,16,R)~abs))~floor) 
  59:   end
  60:   -- process initial string if any
  61:   self~update(chunk)
  62: exit
  63: 
  64: ::method update
  65:   expose a0 b0 c0 d0 count buffer index K. s  -- instance variables
  66:   use strict arg chunk
  67:   count += chunk~length
  68:   if chunk~length<65-index then do
  69:     buffer~overlay(chunk,index)
  70:     index += chunk~length
  71:   end
  72:   else do
  73:     split = 65-index+1
  74:     parse var chunk part =(split) chunk
  75:     buffer~overlay(part,index)
  76:     index = 65
  77:   end
  78:   -- Only proces completely filled buffer
  79:   do while index=65
  80:     A = a0
  81:     B = b0
  82:     C = c0
  83:     D = d0
  84:     do i=0 to 63
  85:       select 
  86:         when i<16 then do
  87:           F = D~xor(B~and(C~xor(D)))
  88:           g = i
  89:         end
  90:         when i<32 then do
  91:           F = C~xor(D~and(B~xor(C)))
  92:           g = (5*i+1)//16
  93:         end
  94:         when i<48 then do
  95:           F = B~xor(C)~xor(D)
  96:           g = (3*i+5)//16
  97:         end
  98:         otherwise do
  99:           F = C~xor(B~or(D~not))
 100:           g = (7*i)//16
 101:         end
 102:       end
 103:       M = .int32~new(buffer~substr(g*4+1,4)~reverse,"C")  -- 32bit word in little-endian
 104:       dTemp = D
 105:       D = C
 106:       C = B
 107:       B = (B + (A+F+K.i+M)~bitrotate(s[i+1]))
 108:       A = dTemp
 109:     end
 110:     a0 = a0+A
 111:     b0 = b0+B
 112:     c0 = c0+C
 113:     d0 = d0+D
 114:     parse var chunk part 65 chunk
 115:     index = part~length+1
 116:     buffer~overlay(part,1,part~length)
 117:   end
 118: exit
 119: 
 120: ::method digest
 121:   expose a0 b0 c0 d0 count buffer index K s -- instance variables
 122:   padlen = 64
 123:   if index<57 then padlen = 57-index
 124:   if index>57 then padlen = 121-index
 125:   padding = '00'x~copies(padlen)~bitor('80'x)
 126:   bitcount = count*8//2**64
 127:   lowword = bitcount//2**32
 128:   hiword = bitcount%2**32
 129:   lowcount = lowword~d2c(4)~reverse -- make it little-endian
 130:   hicount = hiword~d2c(4)~reverse   -- make it little-endian
 131:   self~update(padding || lowcount || hicount)
 132: return a0~string || b0~string || c0~string || d0~string
 133: 
 134: -- A convenience class to encapsulate operations on non OORexx-like
 135: -- things such as little-endian 32-bit words  
 136: ::class int32 private
 137: 
 138: ::attribute arch class
 139: 
 140: ::method init class
 141:   self~arch = "little-endian"   -- can be adapted for multiple architectures 
 142: 
 143: -- Method to create an int32 like object
 144: -- Input can be a OORexx whole number (type="I") or
 145: -- a character string of 4 bytes (type="C")
 146: -- input truncated or padded to 32-bit word/string
 147: ::method init
 148:   expose char4 int32
 149:   use strict arg input, type="Integer"
 150:   -- type must be one of "I"nteger or "C"haracter
 151:   t = type~subchar(1)~upper
 152:   select
 153:     when t=='I' then do
 154:       char4 = input~d2c(4)
 155:       int32 = char4~c2d
 156:     end
 157:     when t=='C' then do
 158:       char4 = input~right(4,'00'x)
 159:       int32 = char4~c2d
 160:     end
 161:     otherwise do
 162:       raise syntax 93.915 array("IC",type)
 163:     end
 164:   end
 165: exit
 166: 
 167: ::method xor  -- wrapper for OORexx bitxor method
 168:   expose char4
 169:   use strict arg other
 170: return .int32~new(char4~bitxor(other~char),"C")
 171:  
 172: ::method and  -- wrapper for OORexx bitand method
 173:   expose char4
 174:   use strict arg other
 175: return .int32~new(char4~bitand(other~char),"C")
 176:  
 177: ::method or   -- wrapper for OORexx bitor method
 178:   expose char4
 179:   use strict arg other
 180: return .int32~new(char4~bitor(other~char),"C")
 181:  
 182: ::method not   -- OORexx not implementation
 183:   expose char4
 184: return .int32~new(char4~bitxor('ffffffff'x),"C")
 185: 
 186: ::method bitleft -- OORexx shift (<<) implementation
 187:   expose char4
 188:   use strict arg bits
 189:   bstring = char4~c2x~x2b
 190:   bstring = bstring~substr(bits+1)~left(bstring~length,'0')
 191: return .int32~new(bstring~b2x~x2d)
 192: 
 193: ::method bitright -- OORexx shift (>>) implementation
 194:   expose char4
 195:   use strict arg bits, signed=.false
 196:   bstring = char4~c2x~x2b
 197:   fill = '0'
 198:   if signed then fill = bstring~subchar(1)
 199:   bstring = bstring~left(bstring~length-bits)~right(bstring~length,fill)
 200: return .int32~new(bstring~b2x~x2d)
 201: 
 202: ::method bitrotate  -- OORexx (left) rotate method
 203:   expose char4
 204:   use strict arg bits, direction='left'
 205:   d = direction~subchar(1)~upper
 206:   if d=='L' then do
 207:     leftpart = self~bitleft(bits)
 208:     rightpart = self~bitright(32-bits)
 209:   end
 210:   else do
 211:     leftpart = self~bitleft(32-bits)
 212:     rightpart = self~bitright(bits)
 213:   end
 214: return rightpart~or(leftpart)
 215: 
 216: ::method int  -- retrieve integer as number
 217:   expose int32
 218: return int32
 219: 
 220: ::method char -- retrieve integer as characters
 221:   expose char4
 222: return char4
 223: 
 224: ::method string -- retrieve integer as hexadecimal string
 225:   expose char4
 226: return char4~reverse~c2x~lower
 227:   
 228: ::method '+'  -- OORexx method to add 2 .int32 instances
 229:   expose int32
 230:   use strict arg other
 231: return .int32~new(int32+other~int)
 232: 
 233: -- Simplify function names for the necessary 'RxMath' functions	
 234: ::routine sin EXTERNAL "LIBRARY rxmath RxCalcSin"
All content © Ruurd Idenburg, 2007–2025, except where marked otherwise. All rights reserved. This page is primarily for non-commercial use only. The Idenburg website records no personal information and sets no ‘cookies’. This site is hosted on my on server at my home, falling under Dutch (privacy) laws.

This page updated on Wed, 28 May 2025 10:38:18 +0200.