1 |
//$Header$ |
2 |
//------------------------------------------------------------------------------------------------- |
3 |
//This file is part of "David T. Ashley's Shared Source Code", a set of shared components |
4 |
//integrated into many of David T. Ashley's projects. |
5 |
//------------------------------------------------------------------------------------------------- |
6 |
//This source code and any program in which it is compiled/used is provided under the MIT License, |
7 |
//reproduced below. |
8 |
//------------------------------------------------------------------------------------------------- |
9 |
//Permission is hereby granted, free of charge, to any person obtaining a copy of |
10 |
//this software and associated documentation files(the "Software"), to deal in the |
11 |
//Software without restriction, including without limitation the rights to use, |
12 |
//copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the |
13 |
//Software, and to permit persons to whom the Software is furnished to do so, |
14 |
//subject to the following conditions : |
15 |
// |
16 |
//The above copyright notice and this permission notice shall be included in all |
17 |
//copies or substantial portions of the Software. |
18 |
// |
19 |
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
20 |
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
21 |
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE |
22 |
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
23 |
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
24 |
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
25 |
//SOFTWARE. |
26 |
//------------------------------------------------------------------------------------------------- |
27 |
/* A description of the functionality of this module and the public interface |
28 |
** definition is contained in the associated .H file. |
29 |
*/ |
30 |
|
31 |
#define MODULE_ESRG_MD5 |
32 |
|
33 |
#include <assert.h> |
34 |
#include <stddef.h> |
35 |
#include <string.h> |
36 |
|
37 |
#include "charfunc.h" |
38 |
#include "esrg_md5.h" |
39 |
|
40 |
//These are macros which are defined for efficiency. These |
41 |
//functions come from RFC 1321. |
42 |
#define ESRG_MD5_FUNC_F(x,y,z) (((x) & (y)) | ((~x) & (z))) |
43 |
#define ESRG_MD5_FUNC_G(x,y,z) (((x) & (z)) | ( (y) & (~z))) |
44 |
#define ESRG_MD5_FUNC_H(x,y,z) ((x) ^ (y) ^ (z)) |
45 |
#define ESRG_MD5_FUNC_I(x,y,z) ((y) ^ ((x) | (~z))) |
46 |
|
47 |
//This is a left rotation macro, again for efficiency. This |
48 |
//macro rotates a 32-bit quantity x left (cyclically) by |
49 |
//n bits. |
50 |
#define ESRG_MD5_FUNC_ROT_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) |
51 |
|
52 |
//These macros do one operation as described in the RFC. These allow |
53 |
//the inlining of code for far more speed. |
54 |
#define ESRG_MD5_FUNC_FF(a,b,c,d,x,s,ac) { \ |
55 |
(a) += ESRG_MD5_FUNC_F((b),(c),(d)) + (x) + (unsigned)(ac); \ |
56 |
(a) = ESRG_MD5_FUNC_ROT_LEFT((a),(s)); \ |
57 |
(a) += (b); \ |
58 |
} |
59 |
#define ESRG_MD5_FUNC_GG(a,b,c,d,x,s,ac) { \ |
60 |
(a) += ESRG_MD5_FUNC_G((b),(c),(d)) + (x) + (unsigned)(ac); \ |
61 |
(a) = ESRG_MD5_FUNC_ROT_LEFT((a),(s)); \ |
62 |
(a) += (b); \ |
63 |
} |
64 |
#define ESRG_MD5_FUNC_HH(a,b,c,d,x,s,ac) { \ |
65 |
(a) += ESRG_MD5_FUNC_H((b),(c),(d)) + (x) + (unsigned)(ac); \ |
66 |
(a) = ESRG_MD5_FUNC_ROT_LEFT((a),(s)); \ |
67 |
(a) += (b); \ |
68 |
} |
69 |
#define ESRG_MD5_FUNC_II(a,b,c,d,x,s,ac) { \ |
70 |
(a) += ESRG_MD5_FUNC_I((b),(c),(d)) + (x) + (unsigned)(ac); \ |
71 |
(a) = ESRG_MD5_FUNC_ROT_LEFT((a),(s)); \ |
72 |
(a) += (b); \ |
73 |
} |
74 |
|
75 |
|
76 |
//This is the padding table to append. It is done with |
77 |
//an array for quickness. |
78 |
static unsigned char ESRG_MD5_pad_table[] = |
79 |
{ |
80 |
0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
81 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
82 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
83 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
84 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
85 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
86 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
87 |
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
88 |
}; |
89 |
|
90 |
|
91 |
void ESRG_MD5_Md5StateStructOpen(struct ESRG_MD5_Md5StateStruct *arg) |
92 |
{ |
93 |
assert(arg != NULL); |
94 |
|
95 |
memset(arg, 0, sizeof(struct ESRG_MD5_Md5StateStruct)); |
96 |
//Everything to zero, processed bitcount automatically set to zero. |
97 |
|
98 |
arg->A = 0x67452301; //These assignments directly from RFC. |
99 |
arg->B = 0xEFCDAB89; |
100 |
arg->C = 0x98BADCFE; |
101 |
arg->D = 0x10325476; |
102 |
} |
103 |
|
104 |
|
105 |
//Copies the byte buffer to the word buffer within the state block. |
106 |
//This is done in a way which hides big-endian/little-endian concerns. |
107 |
|
108 |
static void ESRG_MD5_CopyBytesToWords(struct ESRG_MD5_Md5StateStruct *arg) |
109 |
{ |
110 |
int i; |
111 |
|
112 |
assert(arg != NULL); |
113 |
|
114 |
//Copy the buffer contents into the words. We need to be careful |
115 |
//to do this right, because of big-endian/little-endian concerns. |
116 |
for (i=0; i<16; i++) |
117 |
{ |
118 |
assert((i * 4 + 3) < 64); |
119 |
arg->X[i] = (((unsigned int)(arg->buf[i*4+3])) << 24) |
120 |
+ |
121 |
(((unsigned int)(arg->buf[i*4+2])) << 16) |
122 |
+ |
123 |
(((unsigned int)(arg->buf[i*4+1])) << 8) |
124 |
+ |
125 |
(((unsigned int)(arg->buf[i*4])) ); |
126 |
} |
127 |
} |
128 |
|
129 |
|
130 |
//Does the MD-5 rounds as specified by RFC 1321. |
131 |
|
132 |
static void ESRG_MD5_DoMd5Rounds(struct ESRG_MD5_Md5StateStruct *arg) |
133 |
{ |
134 |
unsigned AA, BB, CC, DD; |
135 |
//Directly from RFC 1321. |
136 |
unsigned A, B, C, D; |
137 |
//We also want to buffer out the state variables, to eliminate |
138 |
//the risk of repeated pointer dereferences. |
139 |
unsigned X[16]; |
140 |
//Buffer to avoid repeated dereferences. |
141 |
|
142 |
assert(arg != NULL); |
143 |
|
144 |
//Copy bytes into words. |
145 |
ESRG_MD5_CopyBytesToWords(arg); |
146 |
|
147 |
//Copy out the buffer for speed. |
148 |
X[ 0] = arg->X[ 0]; |
149 |
X[ 1] = arg->X[ 1]; |
150 |
X[ 2] = arg->X[ 2]; |
151 |
X[ 3] = arg->X[ 3]; |
152 |
X[ 4] = arg->X[ 4]; |
153 |
X[ 5] = arg->X[ 5]; |
154 |
X[ 6] = arg->X[ 6]; |
155 |
X[ 7] = arg->X[ 7]; |
156 |
X[ 8] = arg->X[ 8]; |
157 |
X[ 9] = arg->X[ 9]; |
158 |
X[10] = arg->X[10]; |
159 |
X[11] = arg->X[11]; |
160 |
X[12] = arg->X[12]; |
161 |
X[13] = arg->X[13]; |
162 |
X[14] = arg->X[14]; |
163 |
X[15] = arg->X[15]; |
164 |
|
165 |
//Buffer out the state for speed. |
166 |
A = arg->A; |
167 |
B = arg->B; |
168 |
C = arg->C; |
169 |
D = arg->D; |
170 |
|
171 |
//Make the assignments to temporary variables as described by the RFC. |
172 |
AA = A; |
173 |
BB = B; |
174 |
CC = C; |
175 |
DD = D; |
176 |
|
177 |
//We can now do the MD-5 rounds directly as described in the RFC. The |
178 |
//most effective way to do this is with macros. I tried using a tabulated |
179 |
//approach, but the speed hit was unbelievably bad. This approach is |
180 |
//about the best known. |
181 |
// |
182 |
//Round 1 |
183 |
// |
184 |
ESRG_MD5_FUNC_FF(A,B,C,D,X[ 0], 7,0xd76aa478); /* 1 */ |
185 |
ESRG_MD5_FUNC_FF(D,A,B,C,X[ 1],12,0xe8c7b756); /* 2 */ |
186 |
ESRG_MD5_FUNC_FF(C,D,A,B,X[ 2],17,0x242070db); /* 3 */ |
187 |
ESRG_MD5_FUNC_FF(B,C,D,A,X[ 3],22,0xc1bdceee); /* 4 */ |
188 |
ESRG_MD5_FUNC_FF(A,B,C,D,X[ 4], 7,0xf57c0faf); /* 5 */ |
189 |
ESRG_MD5_FUNC_FF(D,A,B,C,X[ 5],12,0x4787c62a); /* 6 */ |
190 |
ESRG_MD5_FUNC_FF(C,D,A,B,X[ 6],17,0xa8304613); /* 7 */ |
191 |
ESRG_MD5_FUNC_FF(B,C,D,A,X[ 7],22,0xfd469501); /* 8 */ |
192 |
ESRG_MD5_FUNC_FF(A,B,C,D,X[ 8], 7,0x698098d8); /* 9 */ |
193 |
ESRG_MD5_FUNC_FF(D,A,B,C,X[ 9],12,0x8b44f7af); /* 10 */ |
194 |
ESRG_MD5_FUNC_FF(C,D,A,B,X[10],17,0xffff5bb1); /* 11 */ |
195 |
ESRG_MD5_FUNC_FF(B,C,D,A,X[11],22,0x895cd7be); /* 12 */ |
196 |
ESRG_MD5_FUNC_FF(A,B,C,D,X[12], 7,0x6b901122); /* 13 */ |
197 |
ESRG_MD5_FUNC_FF(D,A,B,C,X[13],12,0xfd987193); /* 14 */ |
198 |
ESRG_MD5_FUNC_FF(C,D,A,B,X[14],17,0xa679438e); /* 15 */ |
199 |
ESRG_MD5_FUNC_FF(B,C,D,A,X[15],22,0x49b40821); /* 16 */ |
200 |
// |
201 |
//Round 2 |
202 |
// |
203 |
ESRG_MD5_FUNC_GG(A,B,C,D,X[ 1], 5,0xf61e2562); /* 17 */ |
204 |
ESRG_MD5_FUNC_GG(D,A,B,C,X[ 6], 9,0xc040b340); /* 18 */ |
205 |
ESRG_MD5_FUNC_GG(C,D,A,B,X[11],14,0x265e5a51); /* 19 */ |
206 |
ESRG_MD5_FUNC_GG(B,C,D,A,X[ 0],20,0xe9b6c7aa); /* 20 */ |
207 |
ESRG_MD5_FUNC_GG(A,B,C,D,X[ 5], 5,0xd62f105d); /* 21 */ |
208 |
ESRG_MD5_FUNC_GG(D,A,B,C,X[10], 9,0x02441453); /* 22 */ |
209 |
ESRG_MD5_FUNC_GG(C,D,A,B,X[15],14,0xd8a1e681); /* 23 */ |
210 |
ESRG_MD5_FUNC_GG(B,C,D,A,X[ 4],20,0xe7d3fbc8); /* 24 */ |
211 |
ESRG_MD5_FUNC_GG(A,B,C,D,X[ 9], 5,0x21e1cde6); /* 25 */ |
212 |
ESRG_MD5_FUNC_GG(D,A,B,C,X[14], 9,0xc33707d6); /* 26 */ |
213 |
ESRG_MD5_FUNC_GG(C,D,A,B,X[ 3],14,0xf4d50d87); /* 27 */ |
214 |
ESRG_MD5_FUNC_GG(B,C,D,A,X[ 8],20,0x455a14ed); /* 28 */ |
215 |
ESRG_MD5_FUNC_GG(A,B,C,D,X[13], 5,0xa9e3e905); /* 29 */ |
216 |
ESRG_MD5_FUNC_GG(D,A,B,C,X[ 2], 9,0xfcefa3f8); /* 30 */ |
217 |
ESRG_MD5_FUNC_GG(C,D,A,B,X[ 7],14,0x676f02d9); /* 31 */ |
218 |
ESRG_MD5_FUNC_GG(B,C,D,A,X[12],20,0x8d2a4c8a); /* 32 */ |
219 |
// |
220 |
//Round 3 |
221 |
// |
222 |
ESRG_MD5_FUNC_HH(A,B,C,D,X[ 5], 4,0xfffa3942); /* 33 */ |
223 |
ESRG_MD5_FUNC_HH(D,A,B,C,X[ 8],11,0x8771f681); /* 34 */ |
224 |
ESRG_MD5_FUNC_HH(C,D,A,B,X[11],16,0x6d9d6122); /* 35 */ |
225 |
ESRG_MD5_FUNC_HH(B,C,D,A,X[14],23,0xfde5380c); /* 36 */ |
226 |
ESRG_MD5_FUNC_HH(A,B,C,D,X[ 1], 4,0xa4beea44); /* 37 */ |
227 |
ESRG_MD5_FUNC_HH(D,A,B,C,X[ 4],11,0x4bdecfa9); /* 38 */ |
228 |
ESRG_MD5_FUNC_HH(C,D,A,B,X[ 7],16,0xf6bb4b60); /* 39 */ |
229 |
ESRG_MD5_FUNC_HH(B,C,D,A,X[10],23,0xbebfbc70); /* 40 */ |
230 |
ESRG_MD5_FUNC_HH(A,B,C,D,X[13], 4,0x289b7ec6); /* 41 */ |
231 |
ESRG_MD5_FUNC_HH(D,A,B,C,X[ 0],11,0xeaa127fa); /* 42 */ |
232 |
ESRG_MD5_FUNC_HH(C,D,A,B,X[ 3],16,0xd4ef3085); /* 43 */ |
233 |
ESRG_MD5_FUNC_HH(B,C,D,A,X[ 6],23,0x04881d05); /* 44 */ |
234 |
ESRG_MD5_FUNC_HH(A,B,C,D,X[ 9], 4,0xd9d4d039); /* 45 */ |
235 |
ESRG_MD5_FUNC_HH(D,A,B,C,X[12],11,0xe6db99e5); /* 46 */ |
236 |
ESRG_MD5_FUNC_HH(C,D,A,B,X[15],16,0x1fa27cf8); /* 47 */ |
237 |
ESRG_MD5_FUNC_HH(B,C,D,A,X[ 2],23,0xc4ac5665); /* 48 */ |
238 |
// |
239 |
//Round 4 |
240 |
// |
241 |
ESRG_MD5_FUNC_II(A,B,C,D,X[ 0], 6,0xf4292244); /* 49 */ |
242 |
ESRG_MD5_FUNC_II(D,A,B,C,X[ 7],10,0x432aff97); /* 50 */ |
243 |
ESRG_MD5_FUNC_II(C,D,A,B,X[14],15,0xab9423a7); /* 51 */ |
244 |
ESRG_MD5_FUNC_II(B,C,D,A,X[ 5],21,0xfc93a039); /* 52 */ |
245 |
ESRG_MD5_FUNC_II(A,B,C,D,X[12], 6,0x655b59c3); /* 53 */ |
246 |
ESRG_MD5_FUNC_II(D,A,B,C,X[ 3],10,0x8f0ccc92); /* 54 */ |
247 |
ESRG_MD5_FUNC_II(C,D,A,B,X[10],15,0xffeff47d); /* 55 */ |
248 |
ESRG_MD5_FUNC_II(B,C,D,A,X[ 1],21,0x85845dd1); /* 56 */ |
249 |
ESRG_MD5_FUNC_II(A,B,C,D,X[ 8], 6,0x6fa87e4f); /* 57 */ |
250 |
ESRG_MD5_FUNC_II(D,A,B,C,X[15],10,0xfe2ce6e0); /* 58 */ |
251 |
ESRG_MD5_FUNC_II(C,D,A,B,X[ 6],15,0xa3014314); /* 59 */ |
252 |
ESRG_MD5_FUNC_II(B,C,D,A,X[13],21,0x4e0811a1); /* 60 */ |
253 |
ESRG_MD5_FUNC_II(A,B,C,D,X[ 4], 6,0xf7537e82); /* 61 */ |
254 |
ESRG_MD5_FUNC_II(D,A,B,C,X[11],10,0xbd3af235); /* 62 */ |
255 |
ESRG_MD5_FUNC_II(C,D,A,B,X[ 2],15,0x2ad7d2bb); /* 63 */ |
256 |
ESRG_MD5_FUNC_II(B,C,D,A,X[ 9],21,0xeb86d391); /* 64 */ |
257 |
//End of Round 4. |
258 |
|
259 |
//Perform the four additions as mandated by the RFC. |
260 |
A += AA; |
261 |
B += BB; |
262 |
C += CC; |
263 |
D += DD; |
264 |
|
265 |
//Buffer the state vector back. |
266 |
arg->A = A; |
267 |
arg->B = B; |
268 |
arg->C = C; |
269 |
arg->D = D; |
270 |
} |
271 |
|
272 |
|
273 |
void ESRG_MD5_Md5StateStructAddData(struct ESRG_MD5_Md5StateStruct *arg, |
274 |
void *pointer_in, |
275 |
unsigned len) |
276 |
{ |
277 |
unsigned int low_32; |
278 |
unsigned int byte_offset; |
279 |
unsigned char *data; |
280 |
|
281 |
assert(arg != NULL); |
282 |
assert(pointer_in != NULL); |
283 |
|
284 |
data = (unsigned char *)pointer_in; |
285 |
//It is easier to do it this way, rather than cast all the time. |
286 |
|
287 |
low_32 = (unsigned int)arg->bit_count; |
288 |
//Copy off the least significant bits. Easier to do once. |
289 |
|
290 |
byte_offset = low_32 >> 3; |
291 |
//This gives our byte offset, up to 500+Mb or so. |
292 |
|
293 |
while(len--) |
294 |
{ |
295 |
//We process rounds AFTER a byte is added to the buffer. So |
296 |
//it is always safe to add a byte first. |
297 |
arg->buf[byte_offset & 0x3F] = *data; |
298 |
|
299 |
//Nothing to do unless this was the final byte of the buffer. |
300 |
if ((byte_offset & 0x3F) == 63) |
301 |
{ |
302 |
ESRG_MD5_DoMd5Rounds(arg); |
303 |
} |
304 |
|
305 |
//Increment. |
306 |
data++; |
307 |
byte_offset++; |
308 |
arg->bit_count += 8; |
309 |
} |
310 |
} |
311 |
|
312 |
|
313 |
void ESRG_MD5_Md5StateStructClose(struct ESRG_MD5_Md5StateStruct *state, |
314 |
struct ESRG_MD5_Md5ResultStruct *result) |
315 |
{ |
316 |
unsigned int low_32, high_32, high_32_copy, low_32_copy; |
317 |
unsigned int byte_offset; |
318 |
unsigned int buffer_offset; |
319 |
unsigned char length_buf[8]; |
320 |
//int i; |
321 |
|
322 |
assert(state != NULL); |
323 |
assert(result != NULL); |
324 |
|
325 |
//Obtain easier-to-use indices. These provide a snapshot of the |
326 |
//length before padding is done. |
327 |
low_32 = (unsigned int)state->bit_count; |
328 |
high_32 = (unsigned int)(state->bit_count >> 32); |
329 |
byte_offset = low_32 >> 3; |
330 |
buffer_offset = byte_offset & 0x3F; |
331 |
|
332 |
//We need to pad the buffer out to 8 bytes short of a multiple, |
333 |
//per RFC 1321. |
334 |
ESRG_MD5_Md5StateStructAddData(state, |
335 |
ESRG_MD5_pad_table, |
336 |
(buffer_offset==56) ? (64) : ((56 - buffer_offset) & 0x3F)); |
337 |
|
338 |
//At this point we are fully prepped to stuff in the length in bits. |
339 |
//Prepare the length in a buffer. |
340 |
high_32_copy = high_32; |
341 |
low_32_copy = low_32; |
342 |
length_buf[0] = (unsigned char)(low_32_copy); |
343 |
length_buf[1] = (unsigned char)(low_32_copy >> 8); |
344 |
length_buf[2] = (unsigned char)(low_32_copy >> 16); |
345 |
length_buf[3] = (unsigned char)(low_32_copy >> 24); |
346 |
length_buf[4] = (unsigned char)(high_32_copy); |
347 |
length_buf[5] = (unsigned char)(high_32_copy >> 8); |
348 |
length_buf[6] = (unsigned char)(high_32_copy >> 16); |
349 |
length_buf[7] = (unsigned char)(high_32_copy >> 24); |
350 |
|
351 |
//Tack on the length. This is guaranteed to generate end up with |
352 |
//the last thing being done the compute plus the index being zero. |
353 |
// |
354 |
ESRG_MD5_Md5StateStructAddData(state, |
355 |
length_buf, |
356 |
8); |
357 |
|
358 |
//Be absolutely sure we are rolled over to zero. |
359 |
assert((((int)state->bit_count) & 0x1FF) == 0); |
360 |
|
361 |
//Zero out the return state, just to be sure. |
362 |
memset(result, 0, sizeof(struct ESRG_MD5_Md5ResultStruct)); |
363 |
|
364 |
//Give caller the binary version. |
365 |
result->md5_words[0] = state->A; |
366 |
result->md5_words[1] = state->B; |
367 |
result->md5_words[2] = state->C; |
368 |
result->md5_words[3] = state->D; |
369 |
|
370 |
//Convert to string for caller. |
371 |
CHARFUNC_int_to_lc_hex_rev(state->A, result->md5_chars + 0); |
372 |
CHARFUNC_int_to_lc_hex_rev(state->B, result->md5_chars + 8); |
373 |
CHARFUNC_int_to_lc_hex_rev(state->C, result->md5_chars + 16); |
374 |
CHARFUNC_int_to_lc_hex_rev(state->D, result->md5_chars + 24); |
375 |
|
376 |
//Because of the way the CHARFUNC_int_to_lc_hex_rev() function |
377 |
//works, it produces the mirror image of the sequence of nibbles. |
378 |
//This is not quite what we want. What we want (least significant |
379 |
//byte first, but within each byte most significant nibble first) |
380 |
//from each integer is this: |
381 |
// |
382 |
// n1 n0 n3 n2 n5 n4 n7 n6 |
383 |
// |
384 |
//but what we get from that function is this: |
385 |
// |
386 |
// n0 n1 n2 n3 n4 n5 n6 n6, |
387 |
// |
388 |
//so we have to swap nibbles in each byte. |
389 |
// |
390 |
{ |
391 |
int i; |
392 |
char temp; |
393 |
|
394 |
for (i=0; i<16; i++) |
395 |
{ |
396 |
temp = result->md5_chars[i*2]; |
397 |
result->md5_chars[i*2] = result->md5_chars[i*2+1]; |
398 |
result->md5_chars[i*2+1] = temp; |
399 |
} |
400 |
} |
401 |
|
402 |
result->md5_chars[32] = 0; //Terminator. |
403 |
|
404 |
//Destroy the state, which may contain sensitive information. |
405 |
//This idea came from Rivest's sample code. |
406 |
memset(state, 0, sizeof(struct ESRG_MD5_Md5StateStruct)); |
407 |
} |
408 |
|
409 |
|
410 |
//Returns version control string for file. |
411 |
// |
412 |
const char *ESRG_MD5_cvcinfo(void) |
413 |
{ |
414 |
return ("$Header$"); |
415 |
} |
416 |
|
417 |
|
418 |
//Returns version control string for associated .H file. |
419 |
// |
420 |
const char *ESRG_MD5_hvcinfo(void) |
421 |
{ |
422 |
return (ESRG_MD5_H_VERSION); |
423 |
} |
424 |
|
425 |
//End of esrg_md5.c. |