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"