summaryrefslogtreecommitdiff
path: root/src/md5sum.c
diff options
context:
space:
mode:
authorPádraig Brady <P@draigBrady.com>2016-10-31 13:29:34 +0000
committerPádraig Brady <P@draigBrady.com>2016-11-22 20:04:24 +0000
commitea94589e9ef02624a3837f97f80efd7d3dcf56bf (patch)
tree5f3f96ca978e3e43c485d914bf95c8ffdeeb41dd /src/md5sum.c
parent373ba16f332d0632f2ce987893ed67bb4334c5d2 (diff)
downloadcoreutils-ea94589e9ef02624a3837f97f80efd7d3dcf56bf.tar.xz
b2sum: a new checksum utility with md5sum like interface
Note we don't support the --algorithm option of the b2sum command in the external BLAKE2 project, as that was deemed too confusing for users. "BLAKE2b" was chosen as the default algorithm to use, which is single threaded but performs well on 64 bit. * src/blake2: CC0 source copied from external project. * cfg.mk[VC_LIST_ALWAYS_EXCLUDE_REGEX]: Exclude blake2/ from syntax checks, make update-copyright, etc. * src/local.mk: Reference the sources for b2sum, and set the compilation flags. * doc/coreutils.texi (b2sum invocation): Reference the md5sum invocation node, and add descriptions of -l. * tests/misc/b2sum.sh: Add new test. * tests/local.mk: Reference new test. * AUTHORS: Add new binary. * README: Likewise. * build-aux/gen-lists-of-programs.sh: Likewise. * man/.gitignore: Likewise. * scripts/git-hooks/commit-msg: Likewise. * man/b2sum.x: New man page template. * man/local.mk: Reference new template. * src/.gitignore: Ignore new binaries. * src/blake2/.gitignore: Ignore new build atrifacts. * src/md5sum.c (usage): Describe the new -l option. * NEWS: Mention the new program.
Diffstat (limited to 'src/md5sum.c')
-rw-r--r--src/md5sum.c184
1 files changed, 170 insertions, 14 deletions
diff --git a/src/md5sum.c b/src/md5sum.c
index 6e85cb150..8e2160967 100644
--- a/src/md5sum.c
+++ b/src/md5sum.c
@@ -22,7 +22,14 @@
#include <sys/types.h>
#include "system.h"
+#include "argmatch.h"
+#include "quote.h"
+#include "xdectoint.h"
+#include "xstrtol.h"
+#if HASH_ALGO_BLAKE2
+# include "blake2/b2sum.h"
+#endif
#if HASH_ALGO_MD5
# include "md5.h"
#endif
@@ -49,6 +56,13 @@
# define DIGEST_BITS 128
# define DIGEST_REFERENCE "RFC 1321"
# define DIGEST_ALIGN 4
+#elif HASH_ALGO_BLAKE2
+# define PROGRAM_NAME "b2sum"
+# define DIGEST_TYPE_STRING "BLAKE2"
+# define DIGEST_STREAM blake2fns[b2_algorithm]
+# define DIGEST_BITS 512
+# define DIGEST_REFERENCE "RFC 7693"
+# define DIGEST_ALIGN 8
#elif HASH_ALGO_SHA1
# define PROGRAM_NAME "sha1sum"
# define DIGEST_TYPE_STRING "SHA1"
@@ -88,20 +102,30 @@
# error "Can't decide which hash algorithm to compile."
#endif
-#define DIGEST_HEX_BYTES (DIGEST_BITS / 4)
-#define DIGEST_BIN_BYTES (DIGEST_BITS / 8)
-
-#define AUTHORS \
+#if HASH_ALGO_BLAKE2
+# define AUTHORS \
+ proper_name ("Padraig Brady"), \
+ proper_name ("Samuel Neves")
+#else
+# define AUTHORS \
proper_name ("Ulrich Drepper"), \
proper_name ("Scott Miller"), \
proper_name ("David Madore")
+# define DIGEST_HEX_BYTES (DIGEST_BITS / 4)
+#endif
+#define DIGEST_BIN_BYTES (DIGEST_BITS / 8)
+
/* The minimum length of a valid digest line. This length does
not include any newline character at the end of a line. */
-#define MIN_DIGEST_LINE_LENGTH \
- (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \
- + 1 /* blank */ \
- + 1 /* minimum filename length */ )
+#if HASH_ALGO_BLAKE2
+# define MIN_DIGEST_LINE_LENGTH 3 /* With -l 8. */
+#else
+# define MIN_DIGEST_LINE_LENGTH \
+ (DIGEST_HEX_BYTES /* length of hexadecimal message digest */ \
+ + 1 /* blank */ \
+ + 1 /* minimum filename length */ )
+#endif
/* True if any of the files read were the standard input. */
static bool have_read_stdin;
@@ -133,6 +157,38 @@ static bool strict = false;
/* Whether a BSD reversed format checksum is detected. */
static int bsd_reversed = -1;
+#if HASH_ALGO_BLAKE2
+static char const *const algorithm_in_string[] =
+{
+ "blake2b", NULL
+};
+static char const *const algorithm_out_string[] =
+{
+ "BLAKE2b", NULL
+};
+enum Algorithm
+{
+ BLAKE2b
+};
+static enum Algorithm const algorithm[] =
+{
+ BLAKE2b
+};
+ARGMATCH_VERIFY (algorithm_in_string, algorithm);
+ARGMATCH_VERIFY (algorithm_out_string, algorithm);
+
+static enum Algorithm b2_algorithm;
+static uintmax_t b2_length;
+static blake2fn blake2fns[]=
+{
+ blake2b_stream
+};
+static uintmax_t blake2_max_len[]=
+{
+ BLAKE2B_OUTBYTES
+};
+#endif /* HASH_ALGO_BLAKE2 */
+
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum
@@ -146,6 +202,9 @@ enum
static struct option const long_options[] =
{
+#if HASH_ALGO_BLAKE2
+ { "length", required_argument, NULL, 'l'},
+#endif
{ "binary", no_argument, NULL, 'b' },
{ "check", no_argument, NULL, 'c' },
{ "ignore-missing", no_argument, NULL, IGNORE_MISSING_OPTION},
@@ -176,7 +235,6 @@ Print or check %s (%d-bit) checksums.\n\
DIGEST_BITS);
emit_stdin_note ();
-
if (O_BINARY)
fputs (_("\
\n\
@@ -187,9 +245,16 @@ Print or check %s (%d-bit) checksums.\n\
\n\
-b, --binary read in binary mode\n\
"), stdout);
+
printf (_("\
-c, --check read %s sums from the FILEs and check them\n"),
DIGEST_TYPE_STRING);
+#if HASH_ALGO_BLAKE2
+ fputs (_("\
+ -l, --length digest length in bits; must not exceed the maximum for\n\
+ the blake2 algorithm and must be a multiple of 8\n\
+"), stdout);
+#endif
fputs (_("\
--tag create a BSD-style checksum\n\
"), stdout);
@@ -350,15 +415,51 @@ split_3 (char *s, size_t s_len,
algo_name_len = strlen (DIGEST_TYPE_STRING);
if (STREQ_LEN (s + i, DIGEST_TYPE_STRING, algo_name_len))
{
- if (s[i + algo_name_len] == ' ')
+ i += algo_name_len;
+#if HASH_ALGO_BLAKE2
+ /* Terminate and match algorithm name. */
+ char const *algo_name = &s[i - algo_name_len];
+ while (! ISWHITE (s[i]) && s[i] != '-' && s[i] != '(')
++i;
- if (s[i + algo_name_len] == '(')
+ bool length_specified = s[i] == '-';
+ bool openssl_format = s[i] == '('; /* and no length_specified */
+ s[i++] = '\0';
+ ptrdiff_t algo = argmatch (algo_name, algorithm_out_string, NULL, 0);
+ if (algo < 0)
+ return false;
+ else
+ b2_algorithm = algo;
+ if (openssl_format)
+ s[--i] = '(';
+
+ if (length_specified)
{
+ unsigned long int tmp_ulong;
+ if (xstrtoul (s + i, NULL, 0, &tmp_ulong, NULL) == LONGINT_OK
+ && 0 < tmp_ulong && tmp_ulong <= blake2_max_len[b2_algorithm] * 8
+ && tmp_ulong % 8 == 0)
+ b2_length = tmp_ulong;
+ else
+ return false;
+
+ while (ISDIGIT (s[i]))
+ ++i;
+ }
+ else
+ b2_length = blake2_max_len[b2_algorithm] * 8;
+
+ digest_hex_bytes = b2_length / 4;
+#endif
+ if (s[i] == ' ')
+ ++i;
+ if (s[i] == '(')
+ {
+ ++i;
*binary = 0;
- return bsd_split_3 (s + i + algo_name_len + 1,
- s_len - (i + algo_name_len + 1),
+ return bsd_split_3 (s + i, s_len - i,
hex_digest, file_name, escaped_filename);
}
+ return false;
}
/* Ignore this line if it is too short.
@@ -370,6 +471,18 @@ split_3 (char *s, size_t s_len,
*hex_digest = (unsigned char *) &s[i];
+#if HASH_ALGO_BLAKE2
+ /* Auto determine length. */
+ unsigned char const *hp = *hex_digest;
+ digest_hex_bytes = 0;
+ while (isxdigit (*hp++))
+ digest_hex_bytes++;
+ if (digest_hex_bytes < 2 || digest_hex_bytes % 2
+ || blake2_max_len[b2_algorithm] * 2 < digest_hex_bytes)
+ return false;
+ b2_length = digest_hex_bytes * 4;
+#endif
+
/* The first field has to be the n-character hexadecimal
representation of the message digest. If it is not followed
immediately by a white space it's an error. */
@@ -505,7 +618,11 @@ digest_file (const char *filename, int *binary, unsigned char *bin_result,
fadvise (fp, FADVISE_SEQUENTIAL);
+#if HASH_ALGO_BLAKE2
+ err = DIGEST_STREAM (fp, bin_result, b2_length / 8);
+#else
err = DIGEST_STREAM (fp, bin_result);
+#endif
if (err)
{
error (0, errno, "%s", quotef (filename));
@@ -758,9 +875,28 @@ main (int argc, char **argv)
so that processes running in parallel do not intersperse their output. */
setvbuf (stdout, NULL, _IOLBF, 0);
- while ((opt = getopt_long (argc, argv, "bctw", long_options, NULL)) != -1)
+#if HASH_ALGO_BLAKE2
+ const char* short_opts = "l:bctw";
+ const char* b2_length_str = "";
+#else
+ const char* short_opts = "bctw";
+#endif
+
+ while ((opt = getopt_long (argc, argv, short_opts, long_options, NULL)) != -1)
switch (opt)
{
+#if HASH_ALGO_BLAKE2
+ case 'l':
+ b2_length = xdectoumax (optarg, 0, UINTMAX_MAX, "",
+ _("invalid length"), 0);
+ b2_length_str = optarg;
+ if (b2_length % 8 != 0)
+ {
+ error (0, 0, _("invalid length: %s"), quote (b2_length_str));
+ die (EXIT_FAILURE, 0, _("length is not a multiple of 8"));
+ }
+ break;
+#endif
case 'b':
binary = 1;
break;
@@ -802,7 +938,21 @@ main (int argc, char **argv)
}
min_digest_line_length = MIN_DIGEST_LINE_LENGTH;
+#if HASH_ALGO_BLAKE2
+ if (b2_length > blake2_max_len[b2_algorithm] * 8)
+ {
+ error (0, 0, _("invalid length: %s"), quote (b2_length_str));
+ die (EXIT_FAILURE, 0,
+ _("maximum digest length for %s is %"PRIuMAX" bits"),
+ quote (algorithm_in_string[b2_algorithm]),
+ blake2_max_len[b2_algorithm] * 8);
+ }
+ if (b2_length == 0 && ! do_check)
+ b2_length = blake2_max_len[b2_algorithm] * 8;
+ digest_hex_bytes = b2_length / 4;
+#else
digest_hex_bytes = DIGEST_HEX_BYTES;
+#endif
if (prefix_tag && !binary)
{
@@ -901,7 +1051,13 @@ main (int argc, char **argv)
if (needs_escape)
putchar ('\\');
+#if HASH_ALGO_BLAKE2
+ fputs (algorithm_out_string[b2_algorithm], stdout);
+ if (b2_length < blake2_max_len[b2_algorithm] * 8)
+ printf ("-%"PRIuMAX, b2_length);
+#else
fputs (DIGEST_TYPE_STRING, stdout);
+#endif
fputs (" (", stdout);
print_filename (file, needs_escape);
fputs (") = ", stdout);