/*- * Copyright (c) 2015 Taylor R. Campbell * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #include #include "blake2b.h" #include "blake2s.h" static const char *progname; void setprogname(const char *argv0) { const char *p; p = strrchr(argv0, '/'); if (p != NULL) progname = p + 1; else progname = argv0; } const char * getprogname(void) { return progname; } static void warnx(const char *fmt, ...) { va_list va; (void)fprintf(stderr, "%s: ", getprogname()); va_start(va, fmt); (void)vfprintf(stderr, fmt, va); va_end(va); (void)fprintf(stderr, "\n"); } static void vwarn(const char *fmt, va_list va) { int error = errno; (void)fprintf(stderr, "%s: ", getprogname()); (void)vfprintf(stderr, fmt, va); (void)fprintf(stderr, ": %s\n", strerror(error)); } static void warn(const char *fmt, ...) { va_list va; va_start(va, fmt); vwarn(fmt, va); va_end(va); } static void err(int code, const char *fmt, ...) { va_list va; va_start(va, fmt); vwarn(fmt, va); va_end(va); exit(code); } static void usage(void) { (void)fprintf(stderr, "Usage: %s -a [-s ] ...\n", getprogname()); exit(EXIT_FAILURE); } static char buf[0x10000]; static ssize_t read_block(int fd, void *buf, size_t len) { ssize_t nread; while ((nread = read(fd, buf, len)) == -1) { switch (errno) { case EINTR: continue; case EAGAIN: { struct pollfd pfd = { .fd = fd, .events = POLLIN }; (void)poll(&pfd, 1, -1); continue; } default: return -1; } } return nread; } static void printhash(const void *hash, size_t len, const char *algo, size_t algolen, const char *name) { const uint8_t *p = hash; size_t i; if (name != NULL) { if (printf("%s", algo) < 0) err(EXIT_FAILURE, "printf"); if (len < algolen && printf("-%zu", len*8) < 0) err(EXIT_FAILURE, "printf"); if (printf(" (%s) = ", name) < 0) err(EXIT_FAILURE, "printf"); } for (i = 0; i < len; i++) { if (printf("%02hhx", *p++) < 0) err(EXIT_FAILURE, "printf"); } if (printf("\n") < 0) err(EXIT_FAILURE, "printf"); } static void doit_blake2b(size_t n, int fd, const char *name) { struct blake2b ctx; uint8_t h[BLAKE2B_MAX_DIGEST]; ssize_t nread; size_t nbuf; if (n > BLAKE2B_MAX_DIGEST) { warnx("invalid hash size: %zu", n); usage(); } if (n == 0) n = BLAKE2B_MAX_DIGEST; blake2b_init(&ctx, n, NULL, 0); while ((nread = read_block(fd, buf, sizeof buf)) != 0) { if (nread == -1) err(EXIT_FAILURE, "read"); nbuf = (size_t)nread; blake2b_update(&ctx, buf, nbuf); } blake2b_final(&ctx, h); printhash(h, n, "BLAKE2B", BLAKE2B_MAX_DIGEST, name); } static void doit_blake2s(size_t n, int fd, const char *name) { struct blake2s ctx; uint8_t h[BLAKE2S_MAX_DIGEST]; ssize_t nread; size_t nbuf; if (n > BLAKE2S_MAX_DIGEST) { warnx("invalid hash size: %zu", n); usage(); } if (n == 0) n = BLAKE2S_MAX_DIGEST; blake2s_init(&ctx, n, NULL, 0); while ((nread = read_block(fd, buf, sizeof buf)) != 0) { if (nread == -1) err(EXIT_FAILURE, "read"); nbuf = (size_t)nread; blake2s_update(&ctx, buf, nbuf); } blake2s_final(&ctx, h); printhash(h, n, "BLAKE2S", BLAKE2S_MAX_DIGEST, name); } static void ignore_signal(int signo) { (void)signo; } int main(int argc, char **argv) { extern char *optarg; extern int optind; void (*doit)(size_t, int, const char *) = NULL; size_t size = 0; int ch; setprogname(argv[0]); if (signal(SIGPIPE, &ignore_signal) == SIG_ERR) err(EXIT_FAILURE, "ignore SIGPIPE"); if (blake2b_selftest() != 0) err(EXIT_FAILURE, "blake2b self-test"); if (blake2s_selftest() != 0) err(EXIT_FAILURE, "blake2s self-test"); while ((ch = getopt(argc, argv, "+a:s:")) != -1) { switch (ch) { case 'a': if (doit != NULL) { warnx("specify only one `-a '"); usage(); } if (strcmp(optarg, "blake2s") == 0) { doit = &doit_blake2s; } else if (strcmp(optarg, "blake2b") == 0) { doit = &doit_blake2b; } else { warnx("unknown algorithm: %s", optarg); usage(); } break; case 's': { char *end; unsigned long s; if (size != 0) { warnx("specify only one `-s '"); usage(); } errno = 0; s = strtoul(optarg, &end, 0); if ((errno == ERANGE && s == ULONG_MAX) || (s > SIZE_MAX) || (s == 0)) { warnx("invalid hash size: %s", optarg); usage(); } size = s; break; } case '?': case '+': /* WTF GNU */ usage(); } } argc -= optind; argv += optind; if (doit == NULL) { warnx("missing `-a '"); usage(); } if (argc == 0) { (*doit)(size, STDIN_FILENO, NULL); return EXIT_SUCCESS; } while (argc--) { const char *fname = *argv++; int fd; if (strcmp(fname, "-") == 0) { fd = STDIN_FILENO; } else { fd = open(fname, O_RDONLY); if (fd == -1) { warn("%s", fname); continue; } } (*doit)(size, fd, fname); (void)close(fd); } return EXIT_SUCCESS; }