/*
* Derived from:
*
* MDDRIVER.C - test driver for MD2, MD4 and MD5
*/
/*
* Copyright (C) 1990-2, RSA Data Security, Inc. Created 1990. All
* rights reserved.
*
* RSA Data Security, Inc. makes no representations concerning either
* the merchantability of this software or the suitability of this
* software for any particular purpose. It is provided "as is"
* without express or implied warranty of any kind.
*
* These notices must be retained in any copies of any part of this
* documentation and/or software.
*/
#include <sys/cdefs.h>
#include <sys/param.h>
#include <sys/resource.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
#include <alloca.h>
#include "hash-common.h"
#include "md5-my.h"
#include "sphash-opt.h"
/*
* Length of test block, number of test blocks.
*/
#define TEST_BLOCK_LEN 10000
#define TEST_BLOCK_COUNT 100000
static char *progname;
static bool cflag;
static bool pflag;
static bool qflag;
static bool sflag;
static bool wflag;
static bool strict;
static bool skip;
static bool ignoreMissing;
static char* checkAgainst;
static int checksFailed;
static bool failed;
static int endl = '\n';
typedef void (DIGEST_Init)(void *);
typedef void (DIGEST_Update)(void *, const unsigned char *, size_t);
typedef char *(DIGEST_End)(void *, char *);
/*
* Digests a reference suite of strings and prints the results.
*/
static const char * MD5TestInput[] =
{
"",
"a",
"abc",
"message digest",
"abcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
"12345678901234567890123456789012345678901234567890123456789012345678901234567890",
"MD5 has not yet (2001-09-03) been broken, but sufficient attacks have been made that its security is in some doubt",
NULL
};
static const char *MD5TestOutput[] = {
"d41d8cd98f00b204e9800998ecf8427e",
"0cc175b9c0f1b6a831c399e269772661",
"900150983cd24fb0d6963f7d28e17f72",
"f96b697d7cb7938d525a2f31aaf161d0",
"c3fcd3d76192e4007dfb496cca67e13b",
"d174ab98d277d9f5a5611c2c9f419d9f",
"57edf4a22be3c955ac49da2e2107b67a",
"b50663f41d44d92171cb9976bc118538",
NULL
};
// test generated by gen_tests.py for sphash64
static const char * SPEHTestInput[] =
{
"",
"A",
"AB",
"ABC",
"ABCD",
"ABCDE",
"ABCDEF",
"ABCDEFG",
"ABCDEFGH",
"ABCDEFGHI",
"ABCDEFGHIJ",
"ABCDEFGHIJK",
"ABCDEFGHIJKL",
"ABCDEFGHIJKLM",
"ABCDEFGHIJKLMN",
"ABCDEFGHIJKLMNO",
"ABCDEFGHIJKLMNOP",
"ABCDEFGHIJKLMNOPQ",
"ABCDEFGHIJKLMNOPQR",
"ABCDEFGHIJKLMNOPQRS",
"ABCDEFGHIJKLMNOPQRST",
"ABCDEFGHIJKLMNOPQRSTU",
"ABCDEFGHIJKLMNOPQRSTUV",
"ABCDEFGHIJKLMNOPQRSTUVW",
"ABCDEFGHIJKLMNOPQRSTUVWX",
"ABCDEFGHIJKLMNOPQRSTUVWXY",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"ABCDEFGHIJKLMNOPQRSTUVWXYZa",
"ABCDEFGHIJKLMNOPQRSTUVWXYZab",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabc",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcd",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcde",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdef",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefg",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefgh",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghij",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijk",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkl",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklm",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmn",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmno",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnop",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopq",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrs",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrst",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstu",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuv",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvw",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwx",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxy",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz01234567",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz012345678",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+",
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
NULL
};
static const char * SPEHTestOutput[] =
{
"d41a687aa4cc0943",
"2a5e7dce9268795d",
"2aa32aff120c8bbd",
"4cc834eba8232eff",
"b5f630638324d3d0",
"00d62e62cae9f9aa",
"1f4834059da5176d",
"cdb55e07b6adef31",
"2e75f39fc807fc23",
"796d9cbe9113139d",
"2ce8adc2c851ecb0",
"16328a6d67b91749",
"8f27ee28db11432e",
"ff8ebd80fc4901af",
"eda4273c5a25c6d1",
"6b67eabf1b45e35d",
"30fbf5f7554cf5f6",
"441277d3fa663352",
"075f3895c302f7e6",
"f059391fade7d419",
"5401b2d5de6fbb8f",
"ad875ef54d37659a",
"f6a62b2c5acfbcc7",
"7e738c42b144a6ec",
"931f490123fa354e",
"e82cccbd86e7dd4f",
"572e7b43b67ffebe",
"d6f63e1bec142428",
"9ff11a98d3c79da1",
"41cfff1465fadc46",
"6f0bd77654c97b23",
"014679bed5d79c5e",
"bc0eb42ef70f0530",
"7168d2bb8489a157",
"07f9fa4feb64ee94",
"a1060236ec6e090e",
"6451656f2d230af5",
"102c41241065be1d",
"bb53b43e2ab6ad8f",
"af000b355ac99cdd",
"cb74fb56e86f169f",
"c4f71bbd90e1c866",
"4645b03e95fc5ed1",
"002b72b7c78c2cc1",
"fe024cae6d090c47",
"14579a258878555a",
"fff3397c52e4b7db",
"7bbc08da4d8e8903",
"77ea369c4b08a0b1",
"4400ccb28853a79d",
"f7c15c3e1e620555",
"6e6f45b3738fb494",
"7a04997feed34fe2",
"99693a218e8ce8fd",
"73073e23cf74c1d8",
"9cf1bc4696913db8",
"04c3ed98a67396fe",
"467d233497ad8129",
"3521b52e730ae695",
"d2ed4d776c5911a8",
"eacc8b2cbf189b0a",
"61ce1c0d71c89483",
"c1d826c0eb85c37e",
"39f4b728293a6dba",
"fb51a133ac079c54",
NULL
};
typedef struct Algorithm_t {
const char *progname;
const char *perlname;
const char *name;
const char ** TestInput;
const char ** TestOutput;
struct hash_iface * (*make_hash)(void);
struct hash_iface * hash;
} Algorithm_t;
//static void MD5_Update(DIGEST_CTX *, const unsigned char *, size_t);
static char *MDInput(const Algorithm_t *, FILE *, char *, bool);
static void MDOutput(const Algorithm_t *, char *, const char *);
static void TimeTrial(const Algorithm_t *);
static void TestSuite(const Algorithm_t *);
static void usage(const Algorithm_t *);
static void version(void);
typedef struct hash_iface * DIGEST_CTX;
/* max(MD5_DIGEST_LENGTH, SHA_DIGEST_LENGTH,
SHA256_DIGEST_LENGTH, SHA512_DIGEST_LENGTH,
RIPEMD160_DIGEST_LENGTH, SKEIN1024_DIGEST_LENGTH)*2+1 */
#define HEX_DIGEST_LENGTH 257
/* algorithm function table */
static struct Algorithm_t Algorithm[] =
{
{
"md5", NULL, "MD5",
MD5TestInput,
MD5TestOutput,
&make_md5_my,
NULL
},
{
"speh", NULL, "SPEH",
SPEHTestInput,
SPEHTestOutput,
&make_sphash_opt,
NULL
},
{ }
};
static char * dataToHex(const Algorithm_t * alg, const uint8_t * msg, size_t len, char * strbuf); // same as MD5Data but uses Algorithm_t with hash_iface * in it to generate hash
static char * convToHex(const Algorithm_t * alg, char * strbus);
static int digest = -1;
static unsigned int malformed;
static enum mode {
mode_bsd,
mode_gnu,
mode_perl,
} mode = mode_bsd;
static enum input_mode {
input_binary = '*',
input_text = ' ',
input_universal = 'U',
input_bits = '^',
} input_mode = input_binary;
static enum output_mode {
output_bare,
output_tagged,
output_reverse,
output_gnu,
} output_mode = output_tagged;
enum optval {
opt_end = -1,
/* ensure we don't collide with shortopts */
opt_dummy = CHAR_MAX,
/* BSD options */
opt_check,
opt_passthrough,
opt_quiet,
opt_reverse,
opt_string,
opt_time_trial,
opt_self_test,
/* GNU options */
opt_binary,
opt_help,
opt_ignore_missing,
opt_status,
opt_strict,
opt_tag,
opt_text,
opt_warn,
opt_version,
opt_zero,
/* Perl options */
opt_algorithm,
opt_bits,
opt_universal,
};
static const struct option bsd_longopts[] = {
{ "check", required_argument, 0, opt_check },
{ "passthrough", no_argument, 0, opt_passthrough },
{ "quiet", no_argument, 0, opt_quiet },
{ "reverse", no_argument, 0, opt_reverse },
{ "string", required_argument, 0, opt_string },
{ "time-trial", no_argument, 0, opt_time_trial },
{ "self-test", no_argument, 0, opt_self_test },
{ }
};
static const char *bsd_shortopts = "bc:pqrs:tx";
static const struct option gnu_longopts[] = {
{ "binary", no_argument, 0, opt_binary },
{ "check", no_argument, 0, opt_check },
{ "help", no_argument, 0, opt_help },
{ "ignore-missing", no_argument, 0, opt_ignore_missing },
{ "quiet", no_argument, 0, opt_quiet },
{ "status", no_argument, 0, opt_status },
{ "strict", no_argument, 0, opt_strict },
{ "tag", no_argument, 0, opt_tag },
{ "text", no_argument, 0, opt_text },
{ "version", no_argument, 0, opt_version },
{ "warn", no_argument, 0, opt_warn },
{ "zero", no_argument, 0, opt_zero },
{ }
};
static const char *gnu_shortopts = "bctwz";
static const struct option perl_longopts[] = {
{ "algorithm", required_argument, 0, opt_algorithm },
{ "check", required_argument, 0, opt_check },
{ "help", no_argument, 0, opt_help },
{ "ignore-missing", no_argument, 0, opt_ignore_missing },
{ "quiet", no_argument, 0, opt_quiet },
{ "status", no_argument, 0, opt_status },
{ "strict", no_argument, 0, opt_strict },
{ "tag", no_argument, 0, opt_tag },
{ "text", no_argument, 0, opt_text },
{ "UNIVERSAL", no_argument, 0, opt_universal },
{ "version", no_argument, 0, opt_version },
{ "warn", no_argument, 0, opt_warn },
{ "01", no_argument, 0, opt_bits },
{ }
};
static const char *perl_shortopts = "0a:bchqstUvw";
//static void
//MD5_Update(DIGEST_CTX *c, const unsigned char *data, size_t len)
//{
// MD5Update(c, data, len);
//}
struct chksumrec {
char *filename;
char *chksum;
struct chksumrec *next;
};
static struct chksumrec *head = NULL;
static struct chksumrec **next = &head;
static unsigned int numrecs;
#define PADDING 7 /* extra padding for "SHA512t256 (...) = ...\n" style */
#define CHKFILELINELEN (HEX_DIGEST_LENGTH + MAXPATHLEN + PADDING)
static void
gnu_check(const char *checksumsfile)
{
FILE *inp;
char *linebuf = NULL;
size_t linecap;
ssize_t linelen;
int lineno;
char *filename;
char *hashstr;
struct chksumrec *rec;
const char *digestname;
size_t digestnamelen;
size_t hashstrlen;
if (strcmp(checksumsfile
, "-") == 0)
inp = stdin;
else if ((inp
= fopen(checksumsfile
, "r")) == NULL
)
err(1, "%s", checksumsfile);
digestname = Algorithm[digest].name;
digestnamelen
= strlen(digestname
);
hashstrlen
= strlen((Algorithm
[digest
].
TestOutput)[0]);
lineno = 0;
linecap = CHKFILELINELEN;
while ((linelen = getline(&linebuf, &linecap, inp)) > 0) {
lineno++;
while (linelen > 0 && linebuf[linelen - 1] == '\n')
linelen--;
linebuf[linelen] = '\0';
filename = linebuf + digestnamelen + 2;
hashstr = linebuf + linelen - hashstrlen;
/*
* supported formats:
* BSD: <DigestName> (<Filename>): <Digest>
* GNU: <Digest> [ *U^]<Filename>
*/
if ((size_t)linelen >= digestnamelen + hashstrlen + 6 &&
strncmp(linebuf
, digestname
, digestnamelen
) == 0 &&
strncmp(filename
- 2, " (", 2) == 0 &&
strncmp(hashstr
- 4, ") = ", 4) == 0 &&
strspn(hashstr
, "0123456789ABCDEFabcdef") == hashstrlen
) {
*(hashstr - 4) = '\0';
} else if ((size_t)linelen >= hashstrlen + 3 &&
strspn(linebuf
, "0123456789ABCDEFabcdef") == hashstrlen
&&
linebuf[hashstrlen] == ' ') {
linebuf[hashstrlen] = '\0';
hashstr = linebuf;
filename = linebuf + hashstrlen + 1;
} else {
if (wflag) {
warnx("%s: %d: improperly formatted "
"%s checksum line",
checksumsfile, lineno,
mode == mode_perl ? "SHA" : digestname);
}
malformed++;
continue;
}
if (rec == NULL)
errx(1, "malloc failed");
rec->chksum = strdup(hashstr);
rec->filename = strdup(filename);
if (rec->chksum == NULL || rec->filename == NULL)
errx(1, "malloc failed");
rec->next = NULL;
*next = rec;
next = &rec->next;
numrecs++;
}
if (inp != stdin)
}
/* Main driver.
Arguments (may be any combination):
-sstring - digests string
-t - runs time trial
-x - runs test script
filename - digests file
(none) - digests standard input
*/
int
main(int argc, char *argv[])
{
const struct option *longopts;
const char *shortopts;
FILE *f;
int i, opt;
char *p, *string = NULL;
char buf[HEX_DIGEST_LENGTH];
size_t len;
struct chksumrec *rec;
if ((progname
= strrchr(argv
[0], '/')) == NULL
)
progname = argv[0];
else
progname++;
/*
* GNU coreutils has a number of programs named *sum. These produce
* similar results to the BSD version, but in a different format,
* similar to BSD's -r flag. We install links to this program with
* ending 'sum' to provide this compatibility. Check here to see if the
* name of the program ends in 'sum', set the flag and drop the 'sum' so
* the digest lookup works. Also, make -t a nop when running in this mode
* since that means 'text file' there (though it's a nop in coreutils
* on unix-like systems). The -c flag conflicts, so it's just disabled
* in this mode (though in the future it might be implemented).
*
* We also strive to be compatible with the shasum script which is
* included in Perl. It is roughly equivalent to the GNU offering
* but uses a command-line argument to select the algorithm, and
* supports only SHA-1 and SHA-2.
*/
if (strcmp(progname
, "shasum") == 0) {
mode = mode_perl;
input_mode = input_text;
output_mode = output_gnu;
digest = 1;
longopts = perl_longopts;
shortopts = perl_shortopts;
} else if (len
> 3 && strcmp(progname
+ len
- 3, "sum") == 0) {
len -= 3;
mode = mode_gnu;
input_mode = input_text;
/*
* The historical behavior in GNU emulation mode is
* output_reverse, however this not true to the original
* and the flag that was used to force the correct output
* was -b, which means something else (input_binary) in
* GNU land. Switch to the correct behavior.
*/
output_mode = output_gnu;
longopts = gnu_longopts;
shortopts = gnu_shortopts;
} else {
mode = mode_bsd;
input_mode = input_binary;
output_mode = output_tagged;
longopts = bsd_longopts;
shortopts = bsd_shortopts;
}
// initialize algorithms
for( Algorithm_t * a = Algorithm; a->make_hash; a++ )
{
a->hash = (*a->make_hash)();
if( !a->hash )
{
fprintf(stderr
, "couldn't run (a->make_hash)()\n");
}
if( !(*a->hash->hash_init)(a->hash) )
{
fprintf(stderr
, "(a->make_hash->hash_init)() failed\n");
}
}
if (digest < 0) {
for (digest = 0; Algorithm[digest].progname != NULL; digest++)
if (strncasecmp(Algorithm[digest].progname, progname, len) == 0)
break;
if (Algorithm[digest].progname == NULL)
digest = 0;
}
failed = false;
checkAgainst = NULL;
checksFailed = 0;
skip = false;
while ((opt = getopt_long(argc, argv, shortopts, longopts, NULL)) != opt_end)
switch (opt) {
case opt_bits:
case '0':
input_mode = input_bits;
break;
case opt_algorithm:
case 'a':
for (i = 0; Algorithm[i].progname != NULL; i++) {
if (Algorithm[i].perlname != NULL &&
strcasecmp(Algorithm[i].perlname, optarg) == 0) {
digest = i;
break;
}
}
if (Algorithm[i].progname == NULL)
usage(&Algorithm[digest]);
break;
case opt_binary:
case 'b':
/* in BSD mode, -b is now a no-op */
if (mode != mode_bsd)
input_mode = input_binary;
break;
case opt_check:
case 'c':
cflag = true;
if (mode == mode_bsd)
checkAgainst = optarg;
break;
case opt_passthrough:
case 'p':
pflag = true;
break;
case opt_quiet:
case 'q':
output_mode = output_bare;
qflag = true;
break;
case opt_reverse:
case 'r':
if (!qflag)
output_mode = output_reverse;
break;
case opt_status:
sflag = true;
break;
case opt_strict:
strict = 1;
break;
case 's':
if (mode == mode_perl) {
sflag = true;
break;
}
/* fall through */
case opt_string:
output_mode = output_bare;
string = optarg;
break;
case opt_tag:
output_mode = output_tagged;
break;
case opt_time_trial:
case opt_text:
case 't':
if (mode == mode_bsd) {
TimeTrial(&Algorithm[digest]);
skip = true;
} else {
input_mode = input_text;
}
break;
case opt_universal:
case 'U':
input_mode = input_universal;
break;
case opt_version:
version();
break;
case opt_warn:
case 'w':
wflag = true;
break;
case opt_self_test:
case 'x':
TestSuite(&Algorithm[digest]);
skip = true;
break;
case opt_zero:
case 'z':
endl = '\0';
break;
case opt_ignore_missing:
ignoreMissing = true;
break;
default:
usage(&Algorithm[digest]);
}
argc -= optind;
argv += optind;
if (cflag && mode != mode_bsd) {
/*
* Read digest files into a linked list, then replace argv
* with an array of the filenames from that list.
*/
if (argc < 1)
usage(&Algorithm[digest]);
while (argc--)
gnu_check(*argv++);
argc = 0;
argv
= calloc(sizeof(char *), numrecs
+ 1);
for (rec = head; rec != NULL; rec = rec->next) {
argv[argc] = rec->filename;
argc++;
}
argv[argc] = NULL;
rec = head;
}
if (*argv) {
do {
struct stat st;
const char *filename = *argv;
const char *filemode = "rb";
if (*filename == '*' ||
*filename == ' ' ||
*filename == 'U' ||
*filename == '^') {
if (lstat(filename, &st) != 0) {
input_mode = (int)*filename;
filename++;
}
}
if (input_mode == input_text)
filemode = "r";
if ((f
= fopen(filename
, filemode
)) == NULL
) {
if (errno != ENOENT || !(cflag && ignoreMissing)) {
warn("%s", filename);
failed = true;
}
if (cflag && mode != mode_bsd)
rec = rec->next;
continue;
}
/*
* XXX Enter capability mode on the last argv file.
* When a casper file service or other approach is
* available, switch to that and enter capability mode
* earlier.
*/
if (*(argv + 1) == NULL) {
}
if (cflag && mode != mode_bsd) {
checkAgainst = rec->chksum;
rec = rec->next;
}
p = MDInput(&Algorithm[digest], f, buf, false);
MDOutput(&Algorithm[digest], p, filename);
} while (*++argv);
} else if (!cflag && string == NULL && !skip) {
if (mode == mode_bsd)
output_mode = output_bare;
p = MDInput(&Algorithm[digest], stdin, buf, pflag);
MDOutput(&Algorithm[digest], p, "-");
} else if (string != NULL) {
// p = Algorithm[digest].Data(string, len, buf);
p = dataToHex(&Algorithm[digest], string, len, buf);
MDOutput(&Algorithm[digest], p, string);
}
if (cflag && mode != mode_bsd) {
if (!sflag && malformed > 1)
warnx("WARNING: %d lines are improperly formatted", malformed);
else if (!sflag && malformed > 0)
warnx("WARNING: %d line is improperly formatted", malformed);
if (!sflag && checksFailed > 1)
warnx("WARNING: %d computed checksums did NOT match", checksFailed);
else if (!sflag && checksFailed > 0)
warnx("WARNING: %d computed checksum did NOT match", checksFailed);
if (checksFailed != 0 || (strict && malformed > 0))
return (1);
}
if (failed)
return (1);
if (checksFailed > 0)
return (2);
return (0);
}
/*
* Common input handling
*/
static char *
MDInput(const Algorithm_t *alg, FILE *f, char *buf, bool tee)
{
char block[4096];
DIGEST_CTX context;
char *end, *p, *q;
size_t len;
int bits;
uint8_t byte;
bool cr = false;
// alg->Init(&context);
alg->hash->hash_start(alg->hash);
while ((len
= fread(block
, 1, sizeof(block
), f
)) > 0) {
switch (input_mode) {
case input_binary:
case input_text:
if (tee
&& fwrite(block
, 1, len
, stdout
) != len
)
err(1, "stdout");
// alg->Update(&context, block, len);
alg->hash->hash_addbytes(alg->hash, block, len);
break;
case input_universal:
end = block + len;
for (p = q = block; p < end; p = q) {
if (cr) {
if (*p == '\n')
p++;
err(1, "stdout");
// alg->Update(&context, "\n", 1);
alg->hash->hash_addbytes(alg->hash, "\n", 1);
cr = false;
}
for (q = p; q < end && *q != '\r'; q++)
/* nothing */;
if (q > p) {
if (tee &&
fwrite(p
, 1, q
- p
, stdout
) !=
(size_t)(q - p))
err(1, "stdout");
// alg->Update(&context, p, q - p);
alg->hash->hash_addbytes(alg->hash, p, q-p);
}
if (q < end && *q == '\r') {
cr = true;
q++;
}
}
break;
case input_bits:
end = block + len;
bits = byte = 0;
for (p = block; p < end; p++) {
if (*p == '0' || *p == '1') {
byte <<= 1;
byte |= *p - '0';
if (++bits == 8) {
err(1, "stdout");
// alg->Update(&context, &byte, 1);
alg->hash->hash_addbytes(alg->hash, &byte, 1);
bits = byte = 0;
}
}
}
break;
}
}
// alg->End(&context, buf);
convToHex(alg, buf);
return (NULL);
}
if (cr) {
err(1, "stdout");
// alg->Update(&context, "\n", 1);
alg->hash->hash_addbytes(alg->hash, "\n", 1);
}
if (input_mode == input_bits && bits != 0)
errx(1, "input length was not a multiple of 8");
// return (alg->End(&context, buf));
return convToHex(alg, buf);
}
/*
* Common output handling
*/
static void
MDOutput(const Algorithm_t *alg, char *p, const char *name)
{
bool checkfailed = false;
if (p == NULL) {
warn("%s", name);
failed = true;
} else if (cflag && mode != mode_bsd) {
checkfailed = strcasecmp(checkAgainst, p) != 0;
if (!sflag && (!qflag || checkfailed))
printf("%s: %s%c", name
, checkfailed
? "FAILED" : "OK",
endl);
} else {
switch (output_mode) {
case output_bare:
break;
case output_gnu:
printf("%s %c%s", p
, input_mode
, name
);
break;
case output_reverse:
break;
case output_tagged:
if (mode == mode_perl &&
strncmp(alg
->name
, "SHA512t", 7) == 0) {
printf("%.6s/%s", alg
->name
, alg
->name
+ 7);
} else {
}
printf(" (%s) = %s", name
, p
);
break;
}
if (checkAgainst) {
checkfailed = strcasecmp(checkAgainst, p) != 0;
if (!qflag && checkfailed)
}
}
if (checkfailed)
checksFailed++;
}
/*
* Measures the time to digest TEST_BLOCK_COUNT TEST_BLOCK_LEN-byte blocks.
*/
static void
TimeTrial(const Algorithm_t *alg)
{
DIGEST_CTX context;
struct rusage before, after;
struct timeval total;
float seconds;
unsigned char block[TEST_BLOCK_LEN];
unsigned int i;
char *p, buf[HEX_DIGEST_LENGTH];
printf("%s time trial. Digesting %d %d-byte blocks ...",
alg->name, TEST_BLOCK_COUNT, TEST_BLOCK_LEN);
/* Initialize block */
for (i = 0; i < TEST_BLOCK_LEN; i++)
block[i] = (unsigned char) (i & 0xff);
/* Start timer */
getrusage(RUSAGE_SELF, &before);
/* Digest blocks */
// alg->Init(&context);
alg->hash->hash_start(alg->hash);
for (i = 0; i < TEST_BLOCK_COUNT; i++)
//alg->Update(&context, block, TEST_BLOCK_LEN);
alg->hash->hash_addbytes(alg->hash, block, TEST_BLOCK_LEN);
//p = alg->End(&context, buf);
p = convToHex(alg, buf);
/* Stop timer */
getrusage(RUSAGE_SELF, &after);
timersub(&after.ru_utime, &before.ru_utime, &total);
seconds = total.tv_sec + (float) total.tv_usec / 1000000;
printf("\nTime = %f seconds\n", seconds
);
printf("Speed = %f MiB/second\n", (float) TEST_BLOCK_LEN
*
(float) TEST_BLOCK_COUNT / seconds / (1 << 20));
}
static void
TestSuite(const Algorithm_t *alg)
{
int i;
char buffer[HEX_DIGEST_LENGTH];
printf("%s test suite:\n", alg
->name
);
const char ** in_arr;
const char ** out_arr;
in_arr = alg->TestInput;
out_arr = alg->TestOutput;
while( *in_arr && *out_arr )
{
//for (i = 0; i < MDTESTCOUNT; i++) {
//(*alg->Data)(MDTestInput[i], strlen(MDTestInput[i]), buffer);
dataToHex
(alg
, *in_arr
, strlen(*in_arr
), buffer
);
printf("%s (\"%s\") = %s", alg
->name
, *in_arr
, buffer
);
if (strcmp(buffer
, *out_arr
) == 0) {
printf(" - verified correct\n");
} else {
printf(" - INCORRECT RESULT!\n");
failed = true;
}
in_arr++;
out_arr++;
}
}
static void
usage(const Algorithm_t *alg)
{
switch (mode) {
case mode_gnu:
fprintf(stderr
, "usage: %ssum [-bctwz] [files ...]\n", alg
->progname
);
break;
case mode_perl:
fprintf(stderr
, "usage: shasum [-0bchqstUvw] [-a alg] [files ...]\n");
break;
default:
fprintf(stderr
, "usage: %s [-pqrtx] [-c string] [-s string] [files ...]\n",
alg->progname);
}
}
static void
version(void)
{
if (mode == mode_gnu)
printf("%s (FreeBSD) ", progname
);
}
static char * dataToHex(const Algorithm_t * alg, const uint8_t * msg, size_t len, char * strbuf)
{
struct hash_iface * h = alg->hash;
h->hash_start(h);
h->hash_addbytes(h,msg,len);
return convToHex(alg, strbuf);
}
static char * convToHex(const Algorithm_t * alg, char * strbuf)
{
struct hash_iface * h = alg->hash;
size_t hlen = h->hash_getsize(h);
size_t slen = hlen*2+1; // string len, incl. \0
uint8_t * result = alloca(hlen);
h->hash_result(h,result);
if( !strbuf )
strbuf
= (char *) malloc( slen
);
if( !strbuf )
return NULL;
const char hex[]="0123456789abcdef";
for(size_t i=0;i<hlen;i++)
{
strbuf[2*i] = hex[result[i]>>4];
strbuf[2*i+1] = hex[result[i]&15];
}
strbuf[slen-1]=0;
return strbuf;
}