/* 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;
}