Login

Subversion Repositories NedoOS

Rev

Blame | Last modification | View Log | Download | RSS feed

/*
 * 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;
                }
                rec = malloc(sizeof(*rec));
                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)
                fclose(inp);
}

/* 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.
         */

        len = strlen(progname);
        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");
                        exit(1);
                }

                if( !(*a->hash->hash_init)(a->hash) )
                {
                        fprintf(stderr, "(a->make_hash->hash_init)() failed\n");
                        exit(1);
                }
        }


        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);
                        (void)fclose(f);
                        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) {
                len = strlen(string);

//              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++;
                                        if (tee && putchar('\n') == EOF)
                                                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) {
                                                if (tee && putchar(byte) == EOF)
                                                        err(1, "stdout");

//                                              alg->Update(&context, &byte, 1);
                                                alg->hash->hash_addbytes(alg->hash, &byte, 1);

                                                bits = byte = 0;
                                        }
                                }
                        }
                        break;
                }
        }
        if (ferror(f)) {

//              alg->End(&context, buf);
                convToHex(alg, buf);

                return (NULL);
        }
        if (cr) {
                if (tee && putchar('\n') == EOF)
                        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:
                        printf("%s", p);
                        break;
                case output_gnu:
                        printf("%s %c%s", p, input_mode, name);
                        break;
                case output_reverse:
                        printf("%s %s", p, name);
                        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", alg->name);
                        }
                        printf(" (%s) = %s", name, p);
                        break;
                }
                if (checkAgainst) {
                        checkfailed = strcasecmp(checkAgainst, p) != 0;
                        if (!qflag && checkfailed)
                                printf(" [ Failed ]");
                }
                printf("%c", endl);
        }
        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);
        fflush(stdout);

        /* 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(" done\n");
        printf("Digest = %s", p);
        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);
        }
        exit(1);
}

static void
version(void)
{
        if (mode == mode_gnu)
                printf("%s (FreeBSD) ", progname);
        exit(0);
}


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;
}