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