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"
If you feel inclined to make corrections, suggestions etc., please mail me any.
All content © Ruurd Idenburg, 2007–, 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 a VPS(Virtual Private System) rented from Transip.nl, a Dutch company, falling under Dutch (privacy) laws (I think).

This page updated on by Ruurd Idenburg.