diff options
Diffstat (limited to 'bsum.c')
-rw-r--r-- | bsum.c | 533 |
1 files changed, 533 insertions, 0 deletions
@@ -0,0 +1,533 @@ +/* See LICENSE file for copyright and license details. */ +#include "common.h" + +const char *argv0 = "bsum"; + +static int lenght_by_command_name = 0; + +static void +usage(void) +{ + fprintf(stderr, "usage: %s%s [-c | -B | -L | -U] [-xz] [file] ...", + argv0, lenght_by_command_name ? "" : " [-l bits]"); + exit(2); +} + +static void * +erealloc(void *ptr, size_t n) +{ + ptr = realloc(ptr, n); + if (!ptr) { + fprintf(stderr, "%s: %s\n", argv0, strerror(errno)); + exit(2); + } + return ptr; +} + +static void +get_lenght_by_command_name(const char *command) +{ + const char *p; + p = strrchr(command, '/'); + p = p ? &p[1] : command; + if (strstr(p, "b224sum")) { + lenght_by_command_name = 224; + } else if (strstr(p, "b256sum")) { + lenght_by_command_name = 256; + } else if (strstr(p, "b384sum")) { + lenght_by_command_name = 384; + } else if (strstr(p, "b512sum")) { + lenght_by_command_name = 512; + } +} + +static int +hash_file_blake224(int fd, const char *fname, int decode_hex, unsigned char hash[], size_t *hash_lenp) +{ + struct libblake_blake224_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_blake224_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_blake224_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_blake224_digest_get_required_input_size(len, 0, NULL); + if (req > 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; +} |