/* SPDX-License-Identifier: GPL-2.0 * * Copyright (C) 2018 Jason A. Donenfeld . All Rights Reserved. */ #define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include /* There's a function in OpenSSL for this too, but it's horrible to use. */ static bool hex2bin(unsigned char *bin, const char *hex, size_t hexlen) { unsigned char c, c_acc = 0, c_alpha0, c_alpha, c_num0, c_num, c_val; volatile unsigned char ret = 0; if (hexlen % 2) return false; for (unsigned int i = 0; i < hexlen; i += 2) { c = (unsigned char)hex[i]; c_num = c ^ 48U; c_num0 = (c_num - 10U) >> 8; c_alpha = (c & ~32U) - 55U; c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; ret |= ((c_num0 | c_alpha0) - 1) >> 8; c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); c_acc = c_val * 16U; c = (unsigned char)hex[i + 1]; c_num = c ^ 48U; c_num0 = (c_num - 10U) >> 8; c_alpha = (c & ~32U) - 55U; c_alpha0 = ((c_alpha - 10U) ^ (c_alpha - 16U)) >> 8; ret |= ((c_num0 | c_alpha0) - 1) >> 8; c_val = (c_num0 & c_num) | (c_alpha0 & c_alpha); bin[i / 2] = c_acc | c_val; } return 1 & ((ret - 1) >> 8); } int main(int argc, char *argv[]) { int fd; off_t pos; ssize_t len; size_t hexlen, hashlen, filesize = 0; const char *hex, *algo, *home; unsigned char hash[1024], computed_hash[1024], buffer[1024 * 128]; const EVP_MD *md; EVP_MD_CTX *mdctx; if (argc != 3) { fprintf(stderr, "Usage: %s ALGORITHM HASH\n", argv[0]); return 1; } home = getenv("HOME"); fd = open(home ? home : "/", O_TMPFILE | O_EXCL | O_RDWR, S_IRUSR | S_IWUSR); if (fd < 0) { perror("Error: unable to create temporary inode"); return 1; } algo = argv[1]; hex = argv[2]; hexlen = strlen(hex); OpenSSL_add_all_digests(); md = EVP_get_digestbyname(algo); if (!md) { fprintf(stderr, "Error: unknown hashing algorithm\n"); return 1; } hashlen = EVP_MD_size(md); if (hashlen > sizeof(hash) || hexlen % 2 || hexlen / 2 != hashlen) { fprintf(stderr, "Error: invalid hash length\n"); return 1; } if (!hex2bin(hash, hex, hexlen)) { fprintf(stderr, "Error: invalid hash input\n"); return 1; } mdctx = EVP_MD_CTX_create(); if (!mdctx || !EVP_DigestInit_ex(mdctx, md, NULL)) { fprintf(stderr, "Error: unable to create OpenSSL context\n"); return 1; } while ((len = read(0, buffer, sizeof(buffer))) > 0) { if (write(fd, buffer, len) != len) { perror("Error: unable to write to temporary inode"); return 1; } if (!EVP_DigestUpdate(mdctx, buffer, len)) { fprintf(stderr, "Error: unable to update OpenSSL hash function\n"); return 1; } filesize += len; } if (!EVP_DigestFinal_ex(mdctx, computed_hash, NULL)) { fprintf(stderr, "Error: unable to finalize OpenSSL hash function\n"); return 1; } if (CRYPTO_memcmp(computed_hash, hash, hashlen)) { fprintf(stderr, "Error: input data does not hash to supplied hash\n"); return 2; } if (lseek(fd, 0, SEEK_SET) < 0) { perror("Error: unable to seek to beginning of temporary file\n"); return 1; } for (pos = 0; filesize && (len = sendfile(1, fd, &pos, filesize)) > 0; filesize -= len); if (len < 0) { if (errno != EINVAL || pos != 0) { perror("Error: unable sendfile data to stdout"); return 1; } while ((len = read(fd, buffer, sizeof(buffer))) > 0) { if (write(1, buffer, len) != len) { perror("Error: unable to write data to stdout"); return 1; } } } return 0; }