summaryrefslogtreecommitdiff
path: root/src/md5sum.c
diff options
context:
space:
mode:
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);