3 RIPEMD-160 generate and check utility
4 by Po Shan Cheah, Copyright (c) 1997
5 You may distribute this program freely provided this notice
30 void setrc(int newrc
) {
32 /* raises but never lowers the program return code to newrc */
39 /* convert a hex digit to an integer */
43 if (c
>= 'A' && c
<= 'F')
49 int hexdigit(char c
) {
50 /* returns true if character is a hexadecimal digit */
53 (c
>= '0' && c
<= '9') ||
54 (toupper(c
) >= 'A' && toupper(c
) <= 'F');
59 char *encodestr(char *instr
) {
60 /* replace unprintable characters and % itself with %HH */
62 static char buf
[BUFLEN
];
65 for ( ; *instr
; ++instr
) {
66 if (isprint(*instr
) && !isspace(*instr
) && *instr
!= ESCAPE
)
69 sprintf(outstr
, "%%%02x", *instr
);
77 char *decodestr(char *instr
) {
78 /* undo what encodestr did */
80 static char buf
[BUFLEN
];
84 if (*instr
== ESCAPE
&&
87 *outstr
++ = cvthex(instr
[1]) << 4 | cvthex(instr
[2]);
91 *outstr
++ = *instr
++;
97 void pack_chunk(word
*chunk
, byte
*buf
) {
98 /* pack 64 bytes into 16 little-endian 32-bit words */
102 for (j
= 0; j
< 16; ++j
, buf
+= 4)
103 *chunk
++ = (word
) buf
[0] | (word
) buf
[1] << 8 |
104 (word
) buf
[2] << 16 | (word
) buf
[3] << 24 ;
107 byte
*rmdfp(char *fname
, FILE *fp
) {
108 /* calculate the message digest of a file
109 and return it in string form */
117 word mdbuf
[RMDBITS
/ 32];
118 static byte code
[RMDBITS
/ 8];
120 length
[0] = length
[1] = 0;
123 /* do all full BUFLEN portions */
125 while ((bytesread
= fread(buf
, 1, BUFLEN
, fp
)) == BUFLEN
) {
127 for (i
= 0; i
< BUFLEN
; i
+= 64) {
128 pack_chunk(chunk
, buf
+ i
);
129 MDcompress(mdbuf
, chunk
);
132 if ((tmp
= length
[0] + bytesread
) < length
[0])
133 ++ length
[1]; /* overflow */
138 fprintf(stderr
, "%s: %s\n", fname
, strerror(errno
));
143 /* do all the remaining 64-byte blocks */
145 for (i
= 0; i
< bytesread
- 63; i
+= 64) {
146 pack_chunk(chunk
, buf
+ i
);
147 MDcompress(mdbuf
, chunk
);
150 if ((tmp
= length
[0] + bytesread
) < length
[0])
151 ++ length
[1]; /* overflow */
154 /* do the last partial or zero length block */
156 MDfinish(mdbuf
, buf
+ i
, length
[0] << 3,
157 length
[0] >> 29 | length
[1] << 3);
159 /* convert to 64-byte string using little-endian conversion */
161 for (i
= 0; i
< RMDBITS
/ 8; i
+= 4) {
163 word wd
= mdbuf
[i
>> 2];
165 code
[i
] = (byte
) (wd
);
166 code
[i
+ 1] = (byte
) (wd
>> 8);
167 code
[i
+ 2] = (byte
) (wd
>> 16);
168 code
[i
+ 3] = (byte
) (wd
>> 24);
174 void printcode(byte
*code
) {
175 /* print a RIPEMD-160 code */
179 for (i
= 0; i
< RMDBITS
/ 8; ++i
)
180 printf("%02x", code
[i
]);
183 byte
*rmdfile(char *fname
) {
184 /* calculate the message digest of a single file
185 and output the digest followed by the file name */
189 fp
= fopen(fname
, binary_mode
? "rb" : "r");
191 fprintf(stderr
, "%s: %s\n", fname
, strerror(errno
));
197 byte
*code
= rmdfp(fname
, fp
);
203 int checkdigest(char *digest
) {
204 /* returns true if string is a valid message digest string */
206 if (strlen(digest
) != RMDBITS
/ 8 * 2)
209 for ( ; *digest
; ++digest
) {
210 if ( ! hexdigit(*digest
))
217 void stringtocode(byte
*code
, char *str
) {
218 /* convert message digest string into 20 byte code */
222 for (i
= 0; i
< RMDBITS
/ 8; ++i
, str
+= 2)
223 *code
++ = cvthex(str
[0]) << 4 | cvthex(str
[1]);
226 void checkfp(char *fname
, FILE *fp
) {
227 /* check message digests. message digest data comes from
228 the given open file handle. */
235 byte inputcode
[RMDBITS
/ 8];
238 char infilename
[BUFLEN
];
240 while (fgets(line
, BUFLEN
, fp
)) {
246 if (sscanf(line
, "%s%s", digest
, infilename
) < 2) {
247 fprintf(stderr
, "%s: invalid input on line %d\n",
253 if (!checkdigest(digest
)) {
254 fprintf(stderr
, "%s: invalid message digest on line %d\n",
260 stringtocode(inputcode
, digest
);
262 /* calculate message digest for file */
264 filename
= decodestr(infilename
);
266 code
= rmdfile(filename
);
271 /* compare digests */
273 if (memcmp(inputcode
, code
, RMDBITS
/ 8) != 0) {
278 printf("FAILED %s\n", filename
);
280 printf("%s: RIPEMD-160 check failed for `%s'\n",
286 printf("GOOD %s\n", filename
);
291 if (verbose_mode
&& nfail
)
292 printf("%s: %d of %d file(s) failed RIPEMD-160 check\n",
293 progname
, nfail
, nfile
);
296 fprintf(stderr
, "%s: %s\n", fname
, strerror(errno
));
301 void checkfile(char *fname
) {
302 /* check message digests. message digest data comes from
307 fp
= fopen(fname
, "r");
309 fprintf(stderr
, "%s: %s\n", fname
, strerror(errno
));
319 int main(int argc
, char *argv
[]) {
326 /* parse command line arguments */
328 while ((c
= getopt(argc
, argv
, "bcvh")) >= 0) {
347 printf("Usage: %s [-b] [-c] [-v] [<file>...]\n"
348 "Generates or checks RIPEMD-160 message digests\n"
349 " -b read files in binary mode\n"
350 " -c check message digests\n"
351 " -v verbose, print file names while checking\n\n"
352 "If -c is not specified, then one message digest is "
353 "generated per file\n"
354 "and sent to standard output. If no files are specified "
356 "taken from standard input and only one message digest "
359 "If -c is specified then message digests for a list of "
361 "checked. The input should be a table in the same format "
363 "of this program when message digests are generated. If a "
365 "specified then the message digest table is read from that "
367 "file is specified, then the message digest table is read "
369 "input.\n", progname
);
378 /* check using data from file named on command line */
380 checkfile(argv
[optind
]);
386 /* check using data from standard input */
388 checkfp("(stdin)", stdin
);
397 /* process file arguments */
399 for ( ; optind
< argc
; ++optind
) {
401 byte
*code
= rmdfile(argv
[optind
]);
405 printf(" %s\n", encodestr(argv
[optind
]));
412 /* no file arguments so take input from stdin */
414 byte
*code
= rmdfp("(stdin)", stdin
);