# include "sha512.h"
#endif
#if HASH_ALGO_CKSUM
+# include "sha3.h"
+#endif
+#if HASH_ALGO_CKSUM
# include "sm3.h"
#endif
#include "fadvise.h"
/* If true, print binary digests, not hex. */
static bool raw_digest = false;
+/* blake2b and sha3 allow the -l option. Luckily they both have the same
+ maximum digest size. */
#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
-# define BLAKE2B_MAX_LEN BLAKE2B_OUTBYTES
+# if HASH_ALGO_CKSUM
+static_assert (BLAKE2B_OUTBYTES == SHA3_512_DIGEST_SIZE);
+# endif
+# define DIGEST_MAX_LEN BLAKE2B_OUTBYTES
static uintmax_t digest_length;
-#endif /* HASH_ALGO_BLAKE2 */
+#endif /* HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM */
typedef void (*digest_output_fn)(char const *, int, void const *, bool,
bool, unsigned char, bool, uintmax_t);
return sha512_stream (stream, resstream);
}
static int
+sha3_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
+{
+ switch (*length)
+ {
+ case SHA3_224_DIGEST_SIZE:
+ return sha3_224_stream (stream, resstream);
+ case SHA3_256_DIGEST_SIZE:
+ return sha3_256_stream (stream, resstream);
+ case SHA3_384_DIGEST_SIZE:
+ return sha3_384_stream (stream, resstream);
+ case SHA3_512_DIGEST_SIZE:
+ return sha3_512_stream (stream, resstream);
+ default:
+ unreachable ();
+ }
+}
+static int
blake2b_sum_stream (FILE *stream, void *resstream, uintmax_t *length)
{
return blake2b_stream (stream, resstream, *length);
sha256,
sha384,
sha512,
+ sha3,
blake2b,
sm3,
};
static char const *const algorithm_args[] =
{
"bsd", "sysv", "crc", "crc32b", "md5", "sha1", "sha224",
- "sha256", "sha384", "sha512", "blake2b", "sm3", nullptr
+ "sha256", "sha384", "sha512", "sha3", "blake2b", "sm3", nullptr
};
static enum Algorithm const algorithm_types[] =
{
bsd, sysv, crc, crc32b, md5, sha1, sha224,
- sha256, sha384, sha512, blake2b, sm3,
+ sha256, sha384, sha512, sha3, blake2b, sm3,
};
ARGMATCH_VERIFY (algorithm_args, algorithm_types);
static char const *const algorithm_tags[] =
{
"BSD", "SYSV", "CRC", "CRC32B", "MD5", "SHA1", "SHA224",
- "SHA256", "SHA384", "SHA512", "BLAKE2b", "SM3", nullptr
+ "SHA256", "SHA384", "SHA512", "SHA3", "BLAKE2b", "SM3", nullptr
};
static int const algorithm_bits[] =
{
16, 16, 32, 32, 128, 160, 224,
- 256, 384, 512, 512, 256, 0
+ 256, 384, 512, 512, 512, 256, 0
};
static_assert (ARRAY_CARDINALITY (algorithm_bits)
sha256_sum_stream,
sha384_sum_stream,
sha512_sum_stream,
+ sha3_sum_stream,
blake2b_sum_stream,
sm3_sum_stream,
};
output_file,
output_file,
output_file,
+ output_file,
};
bool cksum_debug;
#endif
"), stdout);
# if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
fputs (_("\
- -l, --length=BITS digest length in bits; must not exceed the max for\n\
- the blake2 algorithm and must be a multiple of 8\n\
+ -l, --length=BITS digest length in bits; must not exceed the max size\n\
+ and must be a multiple of 8 for blake2b;\n\
+ must be 224, 256, 384, or 512 for sha3\n\
"), stdout);
# endif
# if HASH_ALGO_CKSUM
sha256 (equivalent to sha256sum)\n\
sha384 (equivalent to sha384sum)\n\
sha512 (equivalent to sha512sum)\n\
+ sha3 (only available through cksum)\n\
blake2b (equivalent to b2sum)\n\
sm3 (only available through cksum)\n\
\n"), stdout);
s[--i] = '(';
# if HASH_ALGO_BLAKE2
- digest_length = BLAKE2B_MAX_LEN * 8;
+ digest_length = DIGEST_MAX_LEN * 8;
# else
digest_length = algorithm_bits[cksum_algorithm];
# endif
{
uintmax_t length;
char *siend;
- if (! (xstrtoumax (s + i, &siend, 0, &length, nullptr) == LONGINT_OK
- && 0 < length && length <= digest_length
- && length % 8 == 0))
+ if (xstrtoumax (s + i, &siend, 0, &length, nullptr) != LONGINT_OK)
+ return false;
+# if HASH_ALGO_CKSUM
+ else if (cksum_algorithm == sha3)
+ {
+ if (length != SHA3_224_DIGEST_SIZE * 8
+ && length != SHA3_256_DIGEST_SIZE * 8
+ && length != SHA3_384_DIGEST_SIZE * 8
+ && length != SHA3_512_DIGEST_SIZE * 8)
+ return false;
+ }
+# endif
+ else if (!(0 < length && length <= digest_length && length % 8 == 0))
return false;
i = siend - s;
#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
/* Auto determine length. */
# if HASH_ALGO_CKSUM
- if (cksum_algorithm == blake2b) {
+ if (cksum_algorithm == blake2b || cksum_algorithm == sha3) {
# endif
unsigned char const *hp = *digest;
digest_hex_bytes = 0;
while (c_isxdigit (*hp++))
digest_hex_bytes++;
+# if HASH_ALGO_CKSUM
+ if (cksum_algorithm == sha3 && digest_hex_bytes / 2 != SHA3_224_DIGEST_SIZE
+ && digest_hex_bytes / 2 != SHA3_256_DIGEST_SIZE
+ && digest_hex_bytes / 2 != SHA3_384_DIGEST_SIZE
+ && digest_hex_bytes / 2 != SHA3_512_DIGEST_SIZE)
+ return false;
+# endif
if (digest_hex_bytes < 2 || digest_hex_bytes % 2
- || BLAKE2B_MAX_LEN * 2 < digest_hex_bytes)
+ || DIGEST_MAX_LEN * 2 < digest_hex_bytes)
return false;
digest_length = digest_hex_bytes * 4;
# if HASH_ALGO_CKSUM
fadvise (fp, FADVISE_SEQUENTIAL);
#if HASH_ALGO_CKSUM
- if (cksum_algorithm == blake2b)
+ if (cksum_algorithm == blake2b || cksum_algorithm == sha3)
*length = digest_length / 8;
err = DIGEST_STREAM (fp, bin_result, length);
#elif HASH_ALGO_SUM
{
fputs (DIGEST_TYPE_STRING, stdout);
# if HASH_ALGO_BLAKE2
- if (digest_length < BLAKE2B_MAX_LEN * 8)
+ if (digest_length < DIGEST_MAX_LEN * 8)
printf ("-%ju", digest_length);
# elif HASH_ALGO_CKSUM
+ if (cksum_algorithm == sha3)
+ printf ("-%ju", digest_length);
if (cksum_algorithm == blake2b)
{
- if (digest_length < BLAKE2B_MAX_LEN * 8)
+ if (digest_length < DIGEST_MAX_LEN * 8)
printf ("-%ju", digest_length);
}
# endif
min_digest_line_length = MIN_DIGEST_LINE_LENGTH;
#if HASH_ALGO_BLAKE2 || HASH_ALGO_CKSUM
# if HASH_ALGO_CKSUM
- if (digest_length && cksum_algorithm != blake2b)
+ if (digest_length && (cksum_algorithm != blake2b && cksum_algorithm != sha3))
error (EXIT_FAILURE, 0,
- _("--length is only supported with --algorithm=blake2b"));
-# endif
- if (digest_length > BLAKE2B_MAX_LEN * 8)
+ _("--length is only supported with --algorithm=blake2b or "
+ "--algorithm=sha3"));
+ if (cksum_algorithm == sha3)
{
- error (0, 0, _("invalid length: %s"), quote (digest_length_str));
- error (EXIT_FAILURE, 0,
- _("maximum digest length for %s is %d bits"),
- quote (DIGEST_TYPE_STRING),
- BLAKE2B_MAX_LEN * 8);
+ /* Do not require --length with --check. */
+ if (digest_length == 0 && *digest_length_str == '\0' && ! do_check)
+ error (EXIT_FAILURE, 0, _("--algorithm=sha3 requires specifying "
+ "--length 224, 256, 384, or 512"));
+ /* If --check and --length are used we verify the digest length. */
+ if ((! do_check || *digest_length_str != '\0')
+ && digest_length != SHA3_224_DIGEST_SIZE * 8
+ && digest_length != SHA3_256_DIGEST_SIZE * 8
+ && digest_length != SHA3_384_DIGEST_SIZE * 8
+ && digest_length != SHA3_512_DIGEST_SIZE * 8)
+ {
+ error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+ error (EXIT_FAILURE, 0, _("digest length for %s must be "
+ "224, 256, 384, or 512"),
+ quote (DIGEST_TYPE_STRING));
+ }
}
- if (digest_length % 8 != 0)
+ else
{
- error (0, 0, _("invalid length: %s"), quote (digest_length_str));
- error (EXIT_FAILURE, 0, _("length is not a multiple of 8"));
+ /* If the digest length checks for SHA-3 are satisfied, the less strict
+ checks for BLAKE2 will also be. */
+# else
+ {
+# endif
+ if (digest_length > DIGEST_MAX_LEN * 8)
+ {
+ error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+ error (EXIT_FAILURE, 0,
+ _("maximum digest length for %s is %d bits"),
+ quote (DIGEST_TYPE_STRING),
+ DIGEST_MAX_LEN * 8);
+ }
+ if (digest_length % 8 != 0)
+ {
+ error (0, 0, _("invalid length: %s"), quote (digest_length_str));
+ error (EXIT_FAILURE, 0, _("length is not a multiple of 8"));
+ }
}
if (digest_length == 0)
{
# if HASH_ALGO_BLAKE2
- digest_length = BLAKE2B_MAX_LEN * 8;
+ digest_length = DIGEST_MAX_LEN * 8;
# else
digest_length = algorithm_bits[cksum_algorithm];
# endif
--- /dev/null
+#!/bin/sh
+# 'cksum -a sha3' tests.
+
+# Copyright (C) 2025 Free Software Foundation, Inc.
+
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
+print_ver_ cksum
+getlimits_
+
+# Ensure we can --check the --tag format we produce
+for i in 'a' ' b' '*c' '44' ' '; do
+ echo "$i" > "$i"
+ for l in 224 256 384 512; do
+ cksum -a sha3 -l $l "$i" >> check.sha3
+ done
+done
+# Note -l is inferred from the tags in the mixed format file
+cksum -a sha3 --strict -c check.sha3 || fail=1
+
+# Also ensure the openssl tagged variant works
+sed 's/ //; s/ =/=/' < check.sha3 > openssl.sha3 || framework_failure_
+cksum -a sha3 --strict -c openssl.sha3 || fail=1
+
+# Ensure we can check non tagged format
+for l in 224 256 384 512; do
+ cksum -a sha3 --untagged --text -l $l /dev/null \
+ | tee -a check.vals > check.sha3
+ cksum -a sha3 -l $l --strict -c check.sha3 || fail=1
+ cksum -a sha3 --strict -c check.sha3 || fail=1
+done
+
+# Ensure the checksum values are correct. The reference
+# check.vals was created using OpenSSL.
+cksum -a sha3 --length=256 check.vals > out.tmp || fail=1
+tr '*' ' ' < out.tmp > out || framework_failure_ # Remove binary tag on cygwin
+printf '%s' 'SHA3-256 (check.vals) = ' > exp
+echo 'b4753bf1696fda712821b665494c89090ffb0e87b8645559ad9f5db25b42d4f3' >> exp
+compare exp out || fail=1
+
+# Make sure --check does not handle unsupported digest sizes, e.g. truncated
+# digests.
+printf '%s' 'SHA3-248 (check.vals) = ' > inp
+echo 'b4753bf1696fda712821b665494c89090ffb0e87b8645559ad9f5db25b42d4' >> inp
+returns_ 1 cksum -a sha3 -c --warn inp 2>err || fail=1
+cat <<EOF > exp || framework_failure_
+cksum: inp: 1: improperly formatted SHA3 checksum line
+cksum: inp: no properly formatted checksum lines found
+EOF
+compare exp err || fail=1
+
+# Only validate the last specified, used length
+cksum -a sha3 -l 253 -l 256 /dev/null || fail=1
+
+# SHA-3 only allows values for --length to be 224, 256, 384, and 512.
+# Check that multiples of 8 that are allowed by BLAKE2 are disallowed.
+for len in 216 248 376 504 513 1024 $UINTMAX_OFLOW; do
+ returns_ 1 cksum -a sha3 -l $len /dev/null 2>err || fail=1
+ cat <<EOF > exp || framework_failure_
+cksum: invalid length: '$len'
+cksum: digest length for 'SHA3' must be 224, 256, 384, or 512
+EOF
+ compare exp err || fail=1
+ # We still check --length when --check is used.
+ returns_ 1 cksum -a sha3 -l $len --check /dev/null 2>err || fail=1
+ compare exp err
+done
+
+Exit $fail