From 222f009731176e3d54d2e45e1c7fd9ccf0f97dd2 Mon Sep 17 00:00:00 2001 From: Mattias Andrée Date: Fri, 7 Jan 2022 11:42:40 +0100 Subject: First commit MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Mattias Andrée --- .gitignore | 8 + LICENSE | 15 ++ Makefile | 51 ++++++ arg.h | 38 +++++ b224sum.1 | 68 ++++++++ b256sum.1 | 68 ++++++++ b384sum.1 | 68 ++++++++ b512sum.1 | 68 ++++++++ bsum.1 | 72 +++++++++ bsum.c | 533 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ common.h | 14 ++ config.mk | 8 + 12 files changed, 1011 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 arg.h create mode 100644 b224sum.1 create mode 100644 b256sum.1 create mode 100644 b384sum.1 create mode 100644 b512sum.1 create mode 100644 bsum.1 create mode 100644 bsum.c create mode 100644 common.h create mode 100644 config.mk diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d17d682 --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +*\#* +*~ +*.o +*.su +*.gcov +*.gcno +*.gcda +/bsum diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d0aa103 --- /dev/null +++ b/LICENSE @@ -0,0 +1,15 @@ +ISC License + +© 2022 Mattias Andrée + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..3f743a8 --- /dev/null +++ b/Makefile @@ -0,0 +1,51 @@ +.POSIX: + +CONFIGFILE = config.mk +include $(CONFIGFILE) + +OBJ =\ + bsum.o + +HDR =\ + arg.h\ + common.h + +all: bsum +$(OBJ): $(HDR) + +.c.o: + $(CC) -c -o $@ $< $(CFLAGS) $(CPPFLAGS) + +bsum: $(OBJ) + $(CC) -o $@ $(OBJ) $(LDFLAGS) + +install: bsum + mkdir -p -- "$(DESTDIR)$(PREFIX)/bin" + mkdir -p -- "$(DESTDIR)$(MANPREFIX)/man1/" + cp -- bsum "$(DESTDIR)$(PREFIX)/bin/" + ln -sf -- bsum "$(DESTDIR)$(PREFIX)/bin/b224sum" + ln -sf -- bsum "$(DESTDIR)$(PREFIX)/bin/b256sum" + ln -sf -- bsum "$(DESTDIR)$(PREFIX)/bin/b384sum" + ln -sf -- bsum "$(DESTDIR)$(PREFIX)/bin/b512sum" + cp -- bsum.1 "$(DESTDIR)$(MANPREFIX)/man1/" + +uninstall: + -rm -f -- "$(DESTDIR)$(PREFIX)/bin/bsum" + -rm -f -- "$(DESTDIR)$(PREFIX)/bin/b224sum" + -rm -f -- "$(DESTDIR)$(PREFIX)/bin/b256sum" + -rm -f -- "$(DESTDIR)$(PREFIX)/bin/b384sum" + -rm -f -- "$(DESTDIR)$(PREFIX)/bin/b512sum" + -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/bsum.1" + -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/b224sum.1" + -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/b256sum.1" + -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/b384sum.1" + -rm -f -- "$(DESTDIR)$(MANPREFIX)/man1/b512sum.1" + +clean: + -rm -f -- *.o *.su *.gcov *.gcno *.gcda + -rm -f -- bsum + +.SUFFIXES: +.SUFFIXES: .o .c + +.PHONY: all install uninstall clean diff --git a/arg.h b/arg.h new file mode 100644 index 0000000..1231320 --- /dev/null +++ b/arg.h @@ -0,0 +1,38 @@ +/* Trivial code, not subject to copyright, use as you see fit. + * Reimplementation of 20h's arg.h */ + +#ifndef ARG_H +#define ARG_H + +#include + + +extern const char *argv0; + + +#define ARGBEGIN do {\ + char arg_h_flag_, arg_h_break_;\ + if (!argc)\ + break;\ + argv0 = argv[0];\ + while (--argc, *++argv && argv[0][0] == '-' && argv[0][1]) {\ + if (argv[0][1] == '-' && !argv[0][2]) {\ + argv++;\ + argc--;\ + break;\ + }\ + for (arg_h_break_ = 0; !arg_h_break_ && *++*argv;) {\ + switch ((arg_h_flag_ = **argv)) + +#define ARGEND }\ + }\ + } while (0) + + +#define FLAG() (arg_h_flag_) + +#define ARG() (arg_h_break_ = 1, argv[0][1] ? &argv[0][1] :\ + argv[1] ? (argc--, *++argv) :\ + (usage(), NULL)) + +#endif diff --git a/b224sum.1 b/b224sum.1 new file mode 100644 index 0000000..82c6a48 --- /dev/null +++ b/b224sum.1 @@ -0,0 +1,68 @@ +.TH B224SUM 1 blakesum +.SH NAME +b224sum - Compute and check BLAKE-224 message digests +.SH SYNOPSIS +.B bsum +[-c | -B | -L | -U] [-xz] +.RI [ file "] ..." +.SH DESCRIPTION +Print or check BLAKE-224 checksums. +.SH OPTIONS +The +.B b224sum +utility conforms to the Base Definitions volume of POSIX.1-2017, +.IR "Section 12.2" , +.IR "Utility Syntax Guidelines" . +.PP +The following options are supported: +.TP +.B -B +Output checksums in binary representation. This suppresses +the filenames and checksum delimiters. Only the checksums +are printed. +.TP +.B -c +Read BLAKE-224 sums from the file and check them against +the files on your systems. The input files files should be +formatted as the output of this program, or similarly. +This is not going to work if any of the filenames in the +input files starts with or , or if they +contain a or , or if they +contain a or , or if they +contain a or , or if they +contain a or , or if they +contain a size) + buf = erealloc(buf, size); + libblake_blake224_digest(&state, buf, len, 0, NULL, hash); + *hash_lenp = LIBBLAKE_BLAKE224_OUTPUT_SIZE; + free(buf); + return 0; +} + +static int +hash_file_blake256(int fd, const char *fname, int decode_hex, unsigned char hash[], size_t *hash_lenp) +{ + struct libblake_blake256_state state; + char *buf = NULL; + size_t size = 0; + size_t len = 0; + size_t off = 0; + size_t req; + ssize_t r; + int ok; + libblake_blake256_init(&state); + for (;;) { + if (len == size) + buf = erealloc(buf, size += 8 << 10); + r = read(fd, &buf[len], size - len); + if (r <= 0) { + if (!r) + break; + if (errno == EINTR) + continue; + fprintf(stderr, "%s: %s: %s\n", argv0, fname, strerror(errno)); + return -1; + } + len += (size_t)r; + if (!decode_hex) { + off += libblake_blake256_update(&state, &buf[off], len - off); + if (off == len) + off = 0; + } + } + if (off) + memmove(&buf[0], &buf[off], len -= off); + if (decode_hex) { + len = libblake_decode_hex(buf, len, buf, &ok); + if (!ok) { + fprintf(stderr, "%s: %s: %s\n", argv0, fname, "invalid hexadecimal input"); + return -1; + } + } + req = libblake_blake256_digest_get_required_input_size(len, 0, NULL); + if (req > size) + buf = erealloc(buf, size); + libblake_blake256_digest(&state, buf, len, 0, NULL, hash); + *hash_lenp = LIBBLAKE_BLAKE256_OUTPUT_SIZE; + free(buf); + return 0; +} + +static int +hash_file_blake384(int fd, const char *fname, int decode_hex, unsigned char hash[], size_t *hash_lenp) +{ + struct libblake_blake384_state state; + char *buf = NULL; + size_t size = 0; + size_t len = 0; + size_t off = 0; + size_t req; + ssize_t r; + int ok; + libblake_blake384_init(&state); + for (;;) { + if (len == size) + buf = erealloc(buf, size += 8 << 10); + r = read(fd, &buf[len], size - len); + if (r <= 0) { + if (!r) + break; + if (errno == EINTR) + continue; + fprintf(stderr, "%s: %s: %s\n", argv0, fname, strerror(errno)); + return -1; + } + len += (size_t)r; + if (!decode_hex) { + off += libblake_blake384_update(&state, &buf[off], len - off); + if (off == len) + off = 0; + } + } + if (off) + memmove(&buf[0], &buf[off], len -= off); + if (decode_hex) { + len = libblake_decode_hex(buf, len, buf, &ok); + if (!ok) { + fprintf(stderr, "%s: %s: %s\n", argv0, fname, "invalid hexadecimal input"); + return -1; + } + } + req = libblake_blake384_digest_get_required_input_size(len, 0, NULL); + if (req > size) + buf = erealloc(buf, size); + libblake_blake384_digest(&state, buf, len, 0, NULL, hash); + *hash_lenp = LIBBLAKE_BLAKE384_OUTPUT_SIZE; + free(buf); + return 0; +} + +static int +hash_file_blake512(int fd, const char *fname, int decode_hex, unsigned char hash[], size_t *hash_lenp) +{ + struct libblake_blake512_state state; + char *buf = NULL; + size_t size = 0; + size_t len = 0; + size_t off = 0; + size_t req; + ssize_t r; + int ok; + libblake_blake512_init(&state); + for (;;) { + if (len == size) + buf = erealloc(buf, size += 8 << 10); + r = read(fd, &buf[len], size - len); + if (r <= 0) { + if (!r) + break; + if (errno == EINTR) + continue; + fprintf(stderr, "%s: %s: %s\n", argv0, fname, strerror(errno)); + return -1; + } + len += (size_t)r; + if (!decode_hex) { + off += libblake_blake512_update(&state, &buf[off], len - off); + if (off == len) + off = 0; + } + } + if (off) + memmove(&buf[0], &buf[off], len -= off); + if (decode_hex) { + len = libblake_decode_hex(buf, len, buf, &ok); + if (!ok) { + fprintf(stderr, "%s: %s: %s\n", argv0, fname, "invalid hexadecimal input"); + return -1; + } + } + req = libblake_blake512_digest_get_required_input_size(len, 0, NULL); + if (req > size) + buf = erealloc(buf, size); + libblake_blake512_digest(&state, buf, len, 0, NULL, hash); + *hash_lenp = LIBBLAKE_BLAKE512_OUTPUT_SIZE; + free(buf); + return 0; +} + +static int +parse_fd(const char *name) +{ + long int num; + char *end; + if (!isdigit(*name)) + return -1; + errno = 0; + num = strtol(name, &end, 10); + if (num > INT_MAX || *end || errno) + return -1; + return (int)num; +} + +static int +open_file(const char *path, int *closep) +{ + int fd = -1; + + *closep = 0; + + if (!strcmp(path, "-")) + fd = STDIN_FILENO; + else if (!strcmp(path, "/dev/stdin")) + fd = STDIN_FILENO; + else if (!strcmp(path, "/dev/stdout")) + fd = STDOUT_FILENO; + else if (!strcmp(path, "/dev/stderr")) + fd = STDERR_FILENO; + else if (!strncmp(path, "/dev/fd/", sizeof("/dev/fd/") - 1)) + fd = parse_fd(&path[sizeof("/dev/fd/") - 1]); + else if (!strncmp(path, "/proc/self/fd/", sizeof("/proc/self/fd/") - 1)) + fd = parse_fd(&path[sizeof("/proc/self/fd/") - 1]); + + if (fd < 0) { + fd = open(path, O_RDONLY); + if (fd < 0) + return -1; + *closep = 1; + } + + return fd; +} + +static int +hash_file(const char *path, int length, int decode_hex, unsigned char hash[], size_t *hash_lenp) +{ + int ret, fd, close_fd; + + fd = open_file(path, &close_fd); + if (fd < 0) { + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + return -1; + } + + if (length == 224) + ret = hash_file_blake224(fd, path, decode_hex, hash, hash_lenp); + else if (length == 256) + ret = hash_file_blake256(fd, path, decode_hex, hash, hash_lenp); + else if (length == 384) + ret = hash_file_blake384(fd, path, decode_hex, hash, hash_lenp); + else if (length == 512) + ret = hash_file_blake512(fd, path, decode_hex, hash, hash_lenp); + else + abort(); + + if (close_fd) + close(fd); + return ret; +} + +static int +hash_and_print(const char *path, int length, int decode_hex, char newline, int output_case) +{ + unsigned char hash[LIBBLAKE_BLAKE512_OUTPUT_SIZE]; + char hex[LIBBLAKE_BLAKE512_OUTPUT_SIZE * 2 + 1]; + size_t hash_len; + + if (hash_file(path, length, decode_hex, hash, &hash_len)) + return -1; + + if (output_case < 0) { + fwrite(hash, 1, hash_len, stdout); + } else { + libblake_encode_hex(hash, hash_len, hex, output_case); + printf("%s %s%c", hex, path, newline); + } + + return 0; +} + +static int +check_and_print_file(const char *path, int length, int decode_hex, char *expected) +{ + unsigned char hash[LIBBLAKE_BLAKE512_OUTPUT_SIZE]; + int r, fd, close_fd; + + fd = open_file(path, &close_fd); + if (fd < 0) { + if (errno != ENOENT) + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + missing: + printf("%s: Missing\n", path); + return -1; + } + + if (length == 224) + r = hash_file_blake224(fd, path, decode_hex, hash, &(size_t){0}); + else if (length == 256) + r = hash_file_blake256(fd, path, decode_hex, hash, &(size_t){0}); + else if (length == 384) + r = hash_file_blake384(fd, path, decode_hex, hash, &(size_t){0}); + else if (length == 512) + r = hash_file_blake512(fd, path, decode_hex, hash, &(size_t){0}); + else + abort(); + + if (close_fd) + close(fd); + + if (r < 0) + goto missing; + + libblake_decode_hex(expected, (size_t)length / 4, expected, &(int){0}); + if (!memcmp(hash, expected, (size_t)length / 8)) { + printf("%s: OK\n", path); + return 0; + } else { + printf("%s: Fail\n", path); + return -1; + } +} + +static int +check_and_print(const char *path, int length, int decode_hex, char newline) +{ + int fd, close_fd, status = 0; + char *buf = NULL; + size_t size = 0; + size_t len = 0; + ssize_t r; + size_t i, j, k; + + fd = open_file(path, &close_fd); + if (fd < 0) { + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + exit(2); + } + + for (;;) { + if (len == size) + buf = erealloc(buf, size += 8 << 10); + r = read(fd, &buf[len], size - len); + if (r > 0) { + len += (size_t)r; + } else if (!r) { + break; + } else if (errno == EINTR) { + continue; + } else { + fprintf(stderr, "%s: %s: %s\n", argv0, path, strerror(errno)); + exit(2); + } + } + buf = erealloc(buf, len + 1); + buf[len] = '\0'; + + if (newline) { + for (i = 0; i < len; i = k + 1) { + while (isspace(buf[i])) + i++; + for (j = i; j - i < (size_t)length / 4; j++) + if (!isxdigit(buf[j])) + goto corrupt; + if (j == len || !isblank(buf[j])) + goto corrupt; + buf[j] = '\0'; + j++; + while (isblank(buf[j])) + j++; + if (!buf[j]) + goto corrupt; + for (k = j; buf[k] && buf[k] != newline;) + k++; + buf[k] = '\0'; + status |= check_and_print_file(&buf[j], length, decode_hex, &buf[i]); + } + } else { + for (i = 0; i < len; i = k + 1) { + for (j = i; j - i < (size_t)length / 4; j++) + if (!isxdigit(buf[j])) + goto corrupt; + if (buf[j + 0] != ' ' || buf[j + 1] != ' ') + goto corrupt; + buf[j] = '\0'; + j += 2; + k = j + strlen(&buf[j]); + status |= check_and_print_file(&buf[j], length, decode_hex, &buf[i]); + } + } + + if (close_fd) + close(fd); + return status; + +corrupt: + fprintf(stderr, "%s: %s: invalid file content\n", argv0, path); + exit(2); +} + +int +main(int argc, char *argv[]) +{ + int flag_check = 0; + int flag_binary = 0; + int flag_lower = 0; + int flag_upper = 0; + int flag_hex = 0; + int flag_zero = 0; + int length; + + int status = 0; + int output_case; + char newline; + + if (argv[0]) + get_lenght_by_command_name(argv[0]); + + length = lenght_by_command_name; + + ARGBEGIN { + case 'c': + flag_check = 1; + break; + case 'B': + flag_binary = 1; + break; + case 'L': + flag_lower = 1; + flag_upper = 0; + break; + case 'U': + flag_upper = 1; + flag_lower = 0; + break; + case 'x': + flag_hex = 1; + break; + case 'z': + flag_zero = 1; + break; + case 'l': + if (length) + usage(); + length = atoi(ARG()); + if (length != 224 && length != 256 && length != 384 && length != 512) { + fprintf(stderr, "%s: valid arguments for -l are 224 (default), 256, 384, and 512\n", argv0); + return 2; + } + break; + default: + usage(); + } ARGEND; + + if (flag_check + flag_binary + flag_lower + flag_upper > 1) + usage(); + + if (!length) + length = 224; + + newline = flag_zero ? '\0' : '\n'; + if (flag_check) { + if (!argc) { + status |= -check_and_print("-", length, flag_hex, newline); + } else { + for (; *argv; argv++) + status |= -check_and_print(*argv, length, flag_hex, newline); + } + } else { + output_case = flag_binary ? -1 : flag_upper; + if (!argc) { + status |= -hash_and_print("-", length, flag_hex, newline, output_case); + } else { + for (; *argv; argv++) + status |= -hash_and_print(*argv, length, flag_hex, newline, output_case); + } + } + + if (fflush(stdout) || ferror(stdout) || fclose(stdout)) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + return 2; + } + return status; +} diff --git a/common.h b/common.h new file mode 100644 index 0000000..b0cbd63 --- /dev/null +++ b/common.h @@ -0,0 +1,14 @@ +/* See LICENSE file for copyright and license details. */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "arg.h" diff --git a/config.mk b/config.mk new file mode 100644 index 0000000..5656df1 --- /dev/null +++ b/config.mk @@ -0,0 +1,8 @@ +PREFIX = /usr +MANPREFIX = $(PREFIX)/share/man + +CC = c99 + +CPPFLAGS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_XOPEN_SOURCE=700 -D_GNU_SOURCE +CFLAGS = -Wall -O3 +LDFLAGS = -s -lblake -- cgit v1.2.3-70-g09d2