From 94d6b7954dc5b7c98171095b56b15ed695923bb5 Mon Sep 17 00:00:00 2001 From: "Alex Xu (Hello71)" Date: Wed, 15 Aug 2018 11:26:39 -0400 Subject: Move source files to src --- Makefile.in | 2 +- musl-libgen-c.h | 53 ----- random-seed.c | 575 ---------------------------------------------------- sha2.c | 204 ------------------- sha2.h | 69 ------- src/musl-libgen-c.h | 53 +++++ src/random-seed.c | 575 ++++++++++++++++++++++++++++++++++++++++++++++++++++ src/sha2.c | 204 +++++++++++++++++++ src/sha2.h | 69 +++++++ src/util.c | 123 +++++++++++ src/util.h | 34 ++++ util.c | 123 ----------- util.h | 34 ---- 13 files changed, 1059 insertions(+), 1059 deletions(-) delete mode 100644 musl-libgen-c.h delete mode 100644 random-seed.c delete mode 100644 sha2.c delete mode 100644 sha2.h create mode 100644 src/musl-libgen-c.h create mode 100644 src/random-seed.c create mode 100644 src/sha2.c create mode 100644 src/sha2.h create mode 100644 src/util.c create mode 100644 src/util.h delete mode 100644 util.c delete mode 100644 util.h diff --git a/Makefile.in b/Makefile.in index 0840671..18ef8ca 100644 --- a/Makefile.in +++ b/Makefile.in @@ -1,6 +1,6 @@ # @configure_input@ -VPATH = @abs_srcdir@ +VPATH = @abs_srcdir@:@abs_srcdir@/src abs_srcdir = @abs_srcdir@ prefix = @prefix@ diff --git a/musl-libgen-c.h b/musl-libgen-c.h deleted file mode 100644 index 99c90ee..0000000 --- a/musl-libgen-c.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copied from musl. - * - * Copyright © 2005-2014 Rich Felker, et al. - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -#ifndef MUSL_LIBGEN_H -#define MUSL_LIBGEN_H - -#include - -static inline char *mybasename(char *s) -{ - size_t i; - if (!s || !*s) return "."; - i = strlen(s)-1; - for (; i&&s[i]=='/'; i--) s[i] = 0; - for (; i&&s[i-1]!='/'; i--); - return s+i; -} - -static inline char *mydirname(char *s) -{ - size_t i; - if (!s || !*s) return "."; - i = strlen(s)-1; - for (; s[i]=='/'; i--) if (!i) return "/"; - for (; s[i]!='/'; i--) if (!i) return "."; - for (; s[i]=='/'; i--) if (!i) return "/"; - s[i+1] = 0; - return s; -} - -#endif diff --git a/random-seed.c b/random-seed.c deleted file mode 100644 index 0b7c5c4..0000000 --- a/random-seed.c +++ /dev/null @@ -1,575 +0,0 @@ -/* Copyright 2018 Alex Xu (aka Hello71, alxu) - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "musl-libgen-c.h" -#include "util.h" - -// musl forbids include/linux -#define RNDADDENTROPY _IOW( 'R', 0x03, int [2] ) - -/* 3 hours */ -#define DAEMONIZE_SLEEP_TIME (3*60*60) - -/* random pool is always the same size, so use a fixed size array */ -struct rand_pool_info_ { - int entropy_count; - int buf_size; - uint32_t buf[RAND_POOL_SIZE / sizeof(uint32_t)]; -}; - -static const char MAGIC[] = "RANDOM SEED FILE VERSION 1\n"; - -static sig_atomic_t sigint = 0; -static sig_atomic_t sigterm = 0; - -static bool noperms = false; - -static inline void usage() { - puts("usage: random-seed MODE FILE"); - puts("see random-seed(8) for more information."); -} - -static char *get_machine_id() { -#ifdef MACHINE_ID_PATH - FILE *machine_id_file = fopen(MACHINE_ID_PATH, "r"); -#else - const char *etc_machine_id = "/etc/machine-id"; - const char *var_lib_dbus_machine_id = "/var/lib/dbus/machine-id"; - FILE *machine_id_file = fopen(etc_machine_id, "r"); - if (!machine_id_file) { - if (errno != ENOENT) - fprintf(stderr, "error opening %s: %s, trying %s\n", - etc_machine_id, strerror(errno), var_lib_dbus_machine_id); - machine_id_file = fopen(var_lib_dbus_machine_id, "r"); - } -#endif - - if (!machine_id_file) { - perror("couldn't open any machine-id file, last error"); - return NULL; - } - - char *machine_id = NULL; - size_t machine_id_len = 0; - if (getdelim(&machine_id, &machine_id_len, '\0', machine_id_file) == -1) { - fputs("error reading machine id file\n", stderr); - free(machine_id); - return NULL; - } - - return machine_id; -} - -static bool get_machine_id_hash(const unsigned char salt[static SALT_LEN], unsigned char machine_id_digest[static HASH_LEN]) { - static char *c_machine_id; - if (!c_machine_id) - c_machine_id = get_machine_id(); - if (!c_machine_id) - return false; - - unsigned char c_machine_id_digest[HASH_LEN]; - hash(salt, c_machine_id_digest, c_machine_id, strlen(c_machine_id)); - memcpy(machine_id_digest, c_machine_id_digest, HASH_LEN); - return true; -} - -static inline bool get_fs_id_hash(const unsigned char salt[static SALT_LEN], unsigned char fsid_digest[static HASH_LEN], int seed_fd) { - struct statfs statfs_buf; - if (fstatfs(seed_fd, &statfs_buf) == -1) { - fprintf(stderr, "error statfs seed file: %s, " - "disabling entropy credit\n", strerror(errno)); - return false; - } - hash(salt, fsid_digest, &statfs_buf.f_fsid, sizeof(statfs_buf.f_fsid)); - return true; -} - -static bool run_seed_file_cmd(const char *cmd, const unsigned char *salt, FILE *seed_file) { - char *arg; - if (streq(cmd, "machine-id")) { - arg = strtok(NULL, " \t"); - if (!arg) { - fputs("error parsing seed file: expected argument " - "to 'machine-id'\n", stderr); - return false; - } - unsigned char machine_id_hash[HASH_LEN]; - if (!get_machine_id_hash(salt, machine_id_hash)) { - fputs("error getting machine id hash, disabling entropy credit\n", - stderr); - return false; - } - - if (!hash_match(machine_id_hash, arg)) { - fputs("machine-id does not match, disabling entropy credit\n", - stderr); - return false; - } - return true; - } - - if (streq(cmd, "fs-id")) { - arg = strtok(NULL, " \t"); - if (!arg) { - fputs("error parsing seed file: expected argument to 'fs-id'\n", stderr); - return false; - } - unsigned char fs_id_hash[HASH_LEN]; - if (!get_fs_id_hash(salt, fs_id_hash, fileno(seed_file))) { - fputs("error getting fs id hash, disabling entropy credit\n", stderr); - return false; - } - - if (!hash_match(fs_id_hash, arg)) { - fputs("fs id does not match, disabling entropy credit\n", stderr); - return false; - } - return true; - } - - fprintf(stderr, "error parsing seed file: unsupported command: %s\n", cmd); - return false; -} - -static bool load(FILE *seed_file) { - bool credit_entropy = true; - - struct rand_pool_info_ rpi = { - .entropy_count = RAND_POOL_SIZE * 8, - .buf_size = RAND_POOL_SIZE, - .buf = { 0 } - }; - - uint64_t linenum = 0; - char *line = NULL; - size_t n = 0; - bool done = false; - - if (fread(&rpi.buf, 1, RAND_POOL_SIZE, seed_file) != RAND_POOL_SIZE) { - if (feof(seed_file)) { - fputs("premature EOF on seed file\n", stderr); - } else if (ferror(seed_file)) { - fputs("error reading from seed file\n", stderr); - } - // else: we got signalled, so return to main loop to quit or whatever - return false; - } - - unsigned char *salt = (unsigned char *)rpi.buf; - - while (1) { - errno = 0; - ssize_t line_length = getline(&line, &n, seed_file); - if (line_length == -1) { - if (errno) { - perror("error reading from seed file"); - credit_entropy = false; - } - break; - } - - linenum++; - - if (linenum == 1) { - if (streq(line, MAGIC)) { - continue; - } else { - fputs("error parsing seed file: invalid magic\n", stderr); - credit_entropy = false; - break; - } - } - - char *nul = memchr(line, '\0', (size_t)line_length); - if (nul) { - fprintf(stderr, "error parsing seed file: encountered NUL byte in commands line %zu char %zu\n", linenum, nul - line + 1); - credit_entropy = false; - break; - } - - char *cmd = strtok(line, " \t\n"); - if (!cmd) - continue; - -#ifdef DEBUG - fprintf(stderr, "executing command: %s\n", cmd); -#endif - - if (streq(cmd, "done")) { - done = true; - continue; - } - - if (!run_seed_file_cmd(cmd, salt, seed_file)) { - credit_entropy = false; - break; - } - } - - if (!linenum) { - fputs("seed file has no commands, assuming legacy format. disabling entropy credit\n", stderr); - credit_entropy = false; - } - - if (credit_entropy && !done) { - fputs("missing done command, random seed file probably truncated. disabling entropy credit\n", stderr); - credit_entropy = false; - } - - int random_fd = open("/dev/random", O_RDWR, 0); - if (random_fd == -1) { - perror("error opening /dev/random"); - exit(1); - } - - if (credit_entropy) { - if (ioctl(random_fd, RNDADDENTROPY, &rpi) == -1) { - perror("ioctl(RNDADDENTROPY)"); - if (errno == EPERM) { - fputs("Continuing without crediting entropy.\n", stderr); - noperms = true; - } - credit_entropy = false; - } - } - - if (!credit_entropy) { - if (write(random_fd, &rpi.buf, RAND_POOL_SIZE) != RAND_POOL_SIZE) { - fputs("error writing entropy to /dev/random\n", stderr); - exit(1); - } - } - - return credit_entropy; -} - -static bool get_rand_pool(unsigned char *buf) { - size_t rand_bytes_gotten = 0; - - do { - long this_rand_bytes_gotten = random_get(buf + rand_bytes_gotten, RAND_POOL_SIZE - rand_bytes_gotten, 0); - if (this_rand_bytes_gotten == -1) { - switch (errno) { - case EAGAIN: continue; - case EINTR: return false; - default: - perror("getrandom"); - exit(1); - } - } else { - rand_bytes_gotten += (size_t)this_rand_bytes_gotten; - } - } while (rand_bytes_gotten < RAND_POOL_SIZE); - - return true; -} - -/** - * Save entropy to disk. - * - * \param seed_path the seed file path - * \param random_buf the random buffer. if NULL, get our own entropy. - * \return true means saved successfully, false means received EINTR - */ -static bool save(const char *seed_path, unsigned char *random_buf) { - assert(seed_path); - - bool rv = false; - - unsigned char *random_ptr; - if (random_buf) { - random_ptr = random_buf; - } else { - random_ptr = alloca(RAND_POOL_SIZE); - if (!get_rand_pool(random_ptr)) - return false; - } - - unsigned char machine_id_digest[HASH_LEN]; - if (!get_machine_id_hash(random_ptr, machine_id_digest)) { - fputs("cannot obtain machine id, aborting save\n", stderr); - return false; - } - char machine_id_hash[HASH_STR_LEN]; - mem2hex(machine_id_hash, machine_id_digest, HASH_STR_LEN); - machine_id_hash[HASH_STR_LEN-1] = '\0'; - - int seed_dir_fd = -1; - int seed_fd = -1; - FILE *seed_file = NULL; - - char *seed_path_tmp = strdup(seed_path); - const char *seed_dir, *seed_name; - char *seed_path_last_slash = strrchr(seed_path_tmp, '/'); - if (seed_path_last_slash) { - *seed_path_last_slash = '\0'; - seed_dir = seed_path_tmp; - seed_name = seed_path_last_slash + 1; - } else { - free(seed_path_tmp); - seed_path_tmp = NULL; - seed_dir = "."; - seed_name = seed_path; - } - char *seed_name_new = NULL; - - if (asprintf(&seed_name_new, ".%s.new", seed_name) == -1) { - fputs("out of memory\n", stderr); - goto out; - } - - seed_dir_fd = open(seed_dir, O_RDONLY | O_DIRECTORY); - if (seed_dir_fd == -1) { - perror("error opening seed directory"); - goto out; - } - - unsigned char fs_id_digest[HASH_LEN]; - if (!get_fs_id_hash(random_ptr, fs_id_digest, seed_dir_fd)) { - fputs("cannot obtain machine id, aborting save\n", stderr); - goto out; - } - char fs_id_hash[HASH_LEN*2+1]; - mem2hex(fs_id_hash, fs_id_digest, HASH_LEN); - fs_id_hash[sizeof(fs_id_hash)-1] = '\0'; - - seed_fd = openat(seed_dir_fd, seed_name_new, O_WRONLY | O_CREAT | O_TRUNC, 0600); - if (seed_fd == -1) { - perror("error opening new seed file"); - goto err; - } - seed_file = fdopen(seed_fd, "w"); - if (!seed_file) { - perror("error converting seed file fd to stream"); - goto err; - } - - if (fwrite(random_ptr, 1, RAND_POOL_SIZE, seed_file) != RAND_POOL_SIZE - || fputs(MAGIC, seed_file) == EOF - || fputs("machine-id ", seed_file) == EOF - || fputs(machine_id_hash, seed_file) == EOF - || fputs("\nfs-id ", seed_file) == EOF - || fputs(fs_id_hash, seed_file) == EOF - || fputs("\ndone\n", seed_file) == EOF) { - fputs("error writing new seed file\n", stderr); - goto err; - } - - if (fflush(seed_file) == EOF) { - perror("error flushing new seed file"); - goto err; - } - if (fsync(seed_fd) == -1) { - perror("error syncing new seed file"); - goto err; - } - if (renameat(seed_dir_fd, seed_name_new, seed_dir_fd, seed_name) == -1) { - perror("error installing new seed file"); - goto err; - } - if (fclose(seed_file) == EOF) { - perror("error closing new seed file"); - goto err; - } - if (fsync(seed_dir_fd) == -1) { - perror("error syncing seed directory"); - goto out; - } - - rv = true; - goto out; - -err: - if (seed_file) { - fclose(seed_file); - if (unlinkat(seed_dir_fd, seed_name_new, 0) == -1) { - perror("error removing temporary seed file"); - rv = false; - } - } - -out: - if (seed_dir_fd != -1) { - if (close(seed_dir_fd) == -1) { - perror("error closing seed directory"); - rv = false; - } - } - - free(seed_path_tmp); - free(seed_name_new); - - return rv; -} - -static void sighandler(int signum) { - switch (signum) { - case SIGHUP: - // do nothing; we just needed to interrupt sleep - break; - case SIGINT: - sigint = 1; - break; - case SIGTERM: - sigterm = 1; - break; - default: - abort(); - } -} - -noreturn static void run(const char *mode, const char *seed_path) { - FILE *seed_file; - int exit_status = 0; - - if (streq(mode, "load")) { - bool refresh_seed = true; - - if (streq(seed_path, "-")) { - fputs("warning: cannot refresh stdin seed\n", stderr); - seed_file = stdin; - refresh_seed = false; - } else { - seed_file = fopen(seed_path, "r"); - } - if (!seed_file) { - perror("error opening seed file"); - exit(1); - } - if (!load(seed_file)) { - if (noperms) - exit_status = 15; - else - exit_status = 1; - } - if (refresh_seed) { - unsigned char random_buf[RAND_POOL_SIZE]; - unsigned char *random_ptr = random_buf; - switch (random_get(random_buf, RAND_POOL_SIZE, GRND_NONBLOCK)) { - case RAND_POOL_SIZE: - goto save; - case -1: - if (errno != EAGAIN) { - perror("getrandom"); - exit(1); - } - } - if (daemon(0, 1) == -1) { - perror("error daemonizing, continuing without"); - } - close(0); - close(1); - random_ptr = NULL; -save: if (!save(seed_path, random_ptr)) - exit_status = 1; - } - exit(exit_status); - } else if (streq(mode, "save")) { - exit(!save(seed_path, NULL)); - } else if (streq(mode, "daemonize")) { - if (streq(seed_path, "-")) { - fputs("error: seed_path cannot be - for daemonize\n", stderr); - exit(2); - } - seed_file = fopen(seed_path, "r"); - if (!seed_file) { - perror("error opening seed file"); - exit(3); - } - if (!load(seed_file)) - fputs("warning: failed to load initial entropy\n", stderr); - - struct sigaction sa; - sa.sa_handler = sighandler; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - sigaction(SIGHUP, &sa, NULL); - sigaction(SIGINT, &sa, NULL); - sigaction(SIGTERM, &sa, NULL); - - if (daemon(seed_path[0] != '/', 1) == -1) { - perror("error daemonizing"); - exit(1); - } - close(0); - close(1); - // don't close stderr because we log there - - while (true) { - if (sigint) - exit(exit_status); - if (!save(seed_path, NULL)) { - exit_status = 1; - fputs("an error occurred while saving, trying again later\n", stderr); - } - if (sigterm) - exit(exit_status); - sleep(DAEMONIZE_SLEEP_TIME); - } - } else if (streq(mode, "daemonise")) { - fputs("invalid mode (did you mean `daemonize'?)\n", stderr); - exit(2); - } else { - fputs("invalid mode, expected load, save, or daemonize\n", stderr); - exit(2); - } -} - -int main(int argc, char *argv[]) { - char *mode, *seed_path; - - switch (argc) { - case 2: - if (streq(argv[1], "-h") || streq(argv[1], "--help")) { - usage(); - exit(0); - } - if (streq(argv[1], "-V") || streq(argv[1], "--version")) { - printf("random-seed %s\n", PACKAGE_VERSION); - exit(0); - } - mode = argv[1]; - seed_path = DEFAULT_SEED_PATH; - break; - case 3: - mode = argv[1]; - seed_path = argv[2]; - break; - default: - fprintf(stderr, "error: invalid arguments\n"); - usage(); - exit(2); - } - - umask(0); - run(mode, seed_path); -} diff --git a/sha2.c b/sha2.c deleted file mode 100644 index 4c2b615..0000000 --- a/sha2.c +++ /dev/null @@ -1,204 +0,0 @@ -/* - * FIPS 180-2 SHA-224/256/384/512 implementation - * Last update: 02/02/2007 - * Issue date: 04/30/2005 - * - * Copyright (C) 2013, Con Kolivas - * Copyright (C) 2005, 2007 Olivier Gay - * 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. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. - */ - -#include - -#include "sha2.h" - -#define UNPACK32(x, str) \ -{ \ - *((str) + 3) = (uint8_t) ((x) ); \ - *((str) + 2) = (uint8_t) ((x) >> 8); \ - *((str) + 1) = (uint8_t) ((x) >> 16); \ - *((str) + 0) = (uint8_t) ((x) >> 24); \ -} - -#define PACK32(str, x) \ -{ \ - *(x) = ((uint32_t) *((str) + 3) ) \ - | ((uint32_t) *((str) + 2) << 8) \ - | ((uint32_t) *((str) + 1) << 16) \ - | ((uint32_t) *((str) + 0) << 24); \ -} - -#define SHA256_SCR(i) \ -{ \ - w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ - + SHA256_F3(w[i - 15]) + w[i - 16]; \ -} - -static uint32_t sha256_h0[8] = - {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, - 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; - -uint32_t sha256_k[64] = - {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, - 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, - 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, - 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, - 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, - 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, - 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, - 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, - 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, - 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, - 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, - 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, - 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, - 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, - 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, - 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; - -/* SHA-256 functions */ - -void sha256_transf(sha256_ctx *ctx, const unsigned char *message, - unsigned int block_nb) -{ - uint32_t w[64]; - uint32_t wv[8]; - uint32_t t1, t2; - const unsigned char *sub_block; - int i; - - int j; - - for (i = 0; i < (int) block_nb; i++) { - sub_block = message + (i << 6); - - for (j = 0; j < 16; j++) { - PACK32(&sub_block[j << 2], &w[j]); - } - - for (j = 16; j < 64; j++) { - SHA256_SCR(j); - } - - for (j = 0; j < 8; j++) { - wv[j] = ctx->h[j]; - } - - for (j = 0; j < 64; j++) { - t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) - + sha256_k[j] + w[j]; - t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); - wv[7] = wv[6]; - wv[6] = wv[5]; - wv[5] = wv[4]; - wv[4] = wv[3] + t1; - wv[3] = wv[2]; - wv[2] = wv[1]; - wv[1] = wv[0]; - wv[0] = t1 + t2; - } - - for (j = 0; j < 8; j++) { - ctx->h[j] += wv[j]; - } - } -} - -void sha256(const unsigned char *message, unsigned int len, unsigned char *digest) -{ - sha256_ctx ctx; - - sha256_init(&ctx); - sha256_update(&ctx, message, len); - sha256_final(&ctx, digest); -} - -void sha256_init(sha256_ctx *ctx) -{ - memcpy(ctx->h, sha256_h0, sizeof(uint32_t)*8); - ctx->len = 0; - ctx->tot_len = 0; -} - -void sha256_update(sha256_ctx *ctx, const unsigned char *message, - unsigned int len) -{ - unsigned int block_nb; - unsigned int new_len, rem_len, tmp_len; - const unsigned char *shifted_message; - - tmp_len = SHA256_BLOCK_SIZE - ctx->len; - rem_len = len < tmp_len ? len : tmp_len; - - memcpy(&ctx->block[ctx->len], message, rem_len); - - if (ctx->len + len < SHA256_BLOCK_SIZE) { - ctx->len += len; - return; - } - - new_len = len - rem_len; - block_nb = new_len / SHA256_BLOCK_SIZE; - - shifted_message = message + rem_len; - - sha256_transf(ctx, ctx->block, 1); - sha256_transf(ctx, shifted_message, block_nb); - - rem_len = new_len % SHA256_BLOCK_SIZE; - - memcpy(ctx->block, &shifted_message[block_nb << 6], - rem_len); - - ctx->len = rem_len; - ctx->tot_len += (block_nb + 1) << 6; -} - -void sha256_final(sha256_ctx *ctx, unsigned char *digest) -{ - unsigned int block_nb; - unsigned int pm_len; - unsigned int len_b; - - int i; - - block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) - < (ctx->len % SHA256_BLOCK_SIZE))); - - len_b = (ctx->tot_len + ctx->len) << 3; - pm_len = block_nb << 6; - - memset(ctx->block + ctx->len, 0, pm_len - ctx->len); - ctx->block[ctx->len] = 0x80; - UNPACK32(len_b, ctx->block + pm_len - 4); - - sha256_transf(ctx, ctx->block, block_nb); - - for (i = 0 ; i < 8; i++) { - UNPACK32(ctx->h[i], &digest[i << 2]); - } -} diff --git a/sha2.h b/sha2.h deleted file mode 100644 index bd968ba..0000000 --- a/sha2.h +++ /dev/null @@ -1,69 +0,0 @@ -/* - * FIPS 180-2 SHA-224/256/384/512 implementation - * Last update: 02/02/2007 - * Issue date: 04/30/2005 - * - * Copyright (C) 2013, Con Kolivas - * Copyright (C) 2005, 2007 Olivier Gay - * 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. - * 3. Neither the name of the project nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. - */ - -#ifndef SHA2_H -#define SHA2_H - -#include - -#define SHA256_DIGEST_SIZE ( 256 / 8) -#define SHA256_BLOCK_SIZE ( 512 / 8) - -#define SHFR(x, n) (x >> n) -#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) -#define CH(x, y, z) ((x & y) ^ (~x & z)) -#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) - -#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) -#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) -#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) -#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) - -typedef struct { - unsigned int tot_len; - unsigned int len; - unsigned char block[2 * SHA256_BLOCK_SIZE]; - uint32_t h[8]; -} sha256_ctx; - -extern uint32_t sha256_k[64]; - -void sha256_init(sha256_ctx * ctx); -void sha256_update(sha256_ctx *ctx, const unsigned char *message, - unsigned int len); -void sha256_final(sha256_ctx *ctx, unsigned char *digest); -void sha256(const unsigned char *message, unsigned int len, - unsigned char *digest); - -#endif /* !SHA2_H */ diff --git a/src/musl-libgen-c.h b/src/musl-libgen-c.h new file mode 100644 index 0000000..99c90ee --- /dev/null +++ b/src/musl-libgen-c.h @@ -0,0 +1,53 @@ +/* + * Copied from musl. + * + * Copyright © 2005-2014 Rich Felker, et al. + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ + +#ifndef MUSL_LIBGEN_H +#define MUSL_LIBGEN_H + +#include + +static inline char *mybasename(char *s) +{ + size_t i; + if (!s || !*s) return "."; + i = strlen(s)-1; + for (; i&&s[i]=='/'; i--) s[i] = 0; + for (; i&&s[i-1]!='/'; i--); + return s+i; +} + +static inline char *mydirname(char *s) +{ + size_t i; + if (!s || !*s) return "."; + i = strlen(s)-1; + for (; s[i]=='/'; i--) if (!i) return "/"; + for (; s[i]!='/'; i--) if (!i) return "."; + for (; s[i]=='/'; i--) if (!i) return "/"; + s[i+1] = 0; + return s; +} + +#endif diff --git a/src/random-seed.c b/src/random-seed.c new file mode 100644 index 0000000..0b7c5c4 --- /dev/null +++ b/src/random-seed.c @@ -0,0 +1,575 @@ +/* Copyright 2018 Alex Xu (aka Hello71, alxu) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "musl-libgen-c.h" +#include "util.h" + +// musl forbids include/linux +#define RNDADDENTROPY _IOW( 'R', 0x03, int [2] ) + +/* 3 hours */ +#define DAEMONIZE_SLEEP_TIME (3*60*60) + +/* random pool is always the same size, so use a fixed size array */ +struct rand_pool_info_ { + int entropy_count; + int buf_size; + uint32_t buf[RAND_POOL_SIZE / sizeof(uint32_t)]; +}; + +static const char MAGIC[] = "RANDOM SEED FILE VERSION 1\n"; + +static sig_atomic_t sigint = 0; +static sig_atomic_t sigterm = 0; + +static bool noperms = false; + +static inline void usage() { + puts("usage: random-seed MODE FILE"); + puts("see random-seed(8) for more information."); +} + +static char *get_machine_id() { +#ifdef MACHINE_ID_PATH + FILE *machine_id_file = fopen(MACHINE_ID_PATH, "r"); +#else + const char *etc_machine_id = "/etc/machine-id"; + const char *var_lib_dbus_machine_id = "/var/lib/dbus/machine-id"; + FILE *machine_id_file = fopen(etc_machine_id, "r"); + if (!machine_id_file) { + if (errno != ENOENT) + fprintf(stderr, "error opening %s: %s, trying %s\n", + etc_machine_id, strerror(errno), var_lib_dbus_machine_id); + machine_id_file = fopen(var_lib_dbus_machine_id, "r"); + } +#endif + + if (!machine_id_file) { + perror("couldn't open any machine-id file, last error"); + return NULL; + } + + char *machine_id = NULL; + size_t machine_id_len = 0; + if (getdelim(&machine_id, &machine_id_len, '\0', machine_id_file) == -1) { + fputs("error reading machine id file\n", stderr); + free(machine_id); + return NULL; + } + + return machine_id; +} + +static bool get_machine_id_hash(const unsigned char salt[static SALT_LEN], unsigned char machine_id_digest[static HASH_LEN]) { + static char *c_machine_id; + if (!c_machine_id) + c_machine_id = get_machine_id(); + if (!c_machine_id) + return false; + + unsigned char c_machine_id_digest[HASH_LEN]; + hash(salt, c_machine_id_digest, c_machine_id, strlen(c_machine_id)); + memcpy(machine_id_digest, c_machine_id_digest, HASH_LEN); + return true; +} + +static inline bool get_fs_id_hash(const unsigned char salt[static SALT_LEN], unsigned char fsid_digest[static HASH_LEN], int seed_fd) { + struct statfs statfs_buf; + if (fstatfs(seed_fd, &statfs_buf) == -1) { + fprintf(stderr, "error statfs seed file: %s, " + "disabling entropy credit\n", strerror(errno)); + return false; + } + hash(salt, fsid_digest, &statfs_buf.f_fsid, sizeof(statfs_buf.f_fsid)); + return true; +} + +static bool run_seed_file_cmd(const char *cmd, const unsigned char *salt, FILE *seed_file) { + char *arg; + if (streq(cmd, "machine-id")) { + arg = strtok(NULL, " \t"); + if (!arg) { + fputs("error parsing seed file: expected argument " + "to 'machine-id'\n", stderr); + return false; + } + unsigned char machine_id_hash[HASH_LEN]; + if (!get_machine_id_hash(salt, machine_id_hash)) { + fputs("error getting machine id hash, disabling entropy credit\n", + stderr); + return false; + } + + if (!hash_match(machine_id_hash, arg)) { + fputs("machine-id does not match, disabling entropy credit\n", + stderr); + return false; + } + return true; + } + + if (streq(cmd, "fs-id")) { + arg = strtok(NULL, " \t"); + if (!arg) { + fputs("error parsing seed file: expected argument to 'fs-id'\n", stderr); + return false; + } + unsigned char fs_id_hash[HASH_LEN]; + if (!get_fs_id_hash(salt, fs_id_hash, fileno(seed_file))) { + fputs("error getting fs id hash, disabling entropy credit\n", stderr); + return false; + } + + if (!hash_match(fs_id_hash, arg)) { + fputs("fs id does not match, disabling entropy credit\n", stderr); + return false; + } + return true; + } + + fprintf(stderr, "error parsing seed file: unsupported command: %s\n", cmd); + return false; +} + +static bool load(FILE *seed_file) { + bool credit_entropy = true; + + struct rand_pool_info_ rpi = { + .entropy_count = RAND_POOL_SIZE * 8, + .buf_size = RAND_POOL_SIZE, + .buf = { 0 } + }; + + uint64_t linenum = 0; + char *line = NULL; + size_t n = 0; + bool done = false; + + if (fread(&rpi.buf, 1, RAND_POOL_SIZE, seed_file) != RAND_POOL_SIZE) { + if (feof(seed_file)) { + fputs("premature EOF on seed file\n", stderr); + } else if (ferror(seed_file)) { + fputs("error reading from seed file\n", stderr); + } + // else: we got signalled, so return to main loop to quit or whatever + return false; + } + + unsigned char *salt = (unsigned char *)rpi.buf; + + while (1) { + errno = 0; + ssize_t line_length = getline(&line, &n, seed_file); + if (line_length == -1) { + if (errno) { + perror("error reading from seed file"); + credit_entropy = false; + } + break; + } + + linenum++; + + if (linenum == 1) { + if (streq(line, MAGIC)) { + continue; + } else { + fputs("error parsing seed file: invalid magic\n", stderr); + credit_entropy = false; + break; + } + } + + char *nul = memchr(line, '\0', (size_t)line_length); + if (nul) { + fprintf(stderr, "error parsing seed file: encountered NUL byte in commands line %zu char %zu\n", linenum, nul - line + 1); + credit_entropy = false; + break; + } + + char *cmd = strtok(line, " \t\n"); + if (!cmd) + continue; + +#ifdef DEBUG + fprintf(stderr, "executing command: %s\n", cmd); +#endif + + if (streq(cmd, "done")) { + done = true; + continue; + } + + if (!run_seed_file_cmd(cmd, salt, seed_file)) { + credit_entropy = false; + break; + } + } + + if (!linenum) { + fputs("seed file has no commands, assuming legacy format. disabling entropy credit\n", stderr); + credit_entropy = false; + } + + if (credit_entropy && !done) { + fputs("missing done command, random seed file probably truncated. disabling entropy credit\n", stderr); + credit_entropy = false; + } + + int random_fd = open("/dev/random", O_RDWR, 0); + if (random_fd == -1) { + perror("error opening /dev/random"); + exit(1); + } + + if (credit_entropy) { + if (ioctl(random_fd, RNDADDENTROPY, &rpi) == -1) { + perror("ioctl(RNDADDENTROPY)"); + if (errno == EPERM) { + fputs("Continuing without crediting entropy.\n", stderr); + noperms = true; + } + credit_entropy = false; + } + } + + if (!credit_entropy) { + if (write(random_fd, &rpi.buf, RAND_POOL_SIZE) != RAND_POOL_SIZE) { + fputs("error writing entropy to /dev/random\n", stderr); + exit(1); + } + } + + return credit_entropy; +} + +static bool get_rand_pool(unsigned char *buf) { + size_t rand_bytes_gotten = 0; + + do { + long this_rand_bytes_gotten = random_get(buf + rand_bytes_gotten, RAND_POOL_SIZE - rand_bytes_gotten, 0); + if (this_rand_bytes_gotten == -1) { + switch (errno) { + case EAGAIN: continue; + case EINTR: return false; + default: + perror("getrandom"); + exit(1); + } + } else { + rand_bytes_gotten += (size_t)this_rand_bytes_gotten; + } + } while (rand_bytes_gotten < RAND_POOL_SIZE); + + return true; +} + +/** + * Save entropy to disk. + * + * \param seed_path the seed file path + * \param random_buf the random buffer. if NULL, get our own entropy. + * \return true means saved successfully, false means received EINTR + */ +static bool save(const char *seed_path, unsigned char *random_buf) { + assert(seed_path); + + bool rv = false; + + unsigned char *random_ptr; + if (random_buf) { + random_ptr = random_buf; + } else { + random_ptr = alloca(RAND_POOL_SIZE); + if (!get_rand_pool(random_ptr)) + return false; + } + + unsigned char machine_id_digest[HASH_LEN]; + if (!get_machine_id_hash(random_ptr, machine_id_digest)) { + fputs("cannot obtain machine id, aborting save\n", stderr); + return false; + } + char machine_id_hash[HASH_STR_LEN]; + mem2hex(machine_id_hash, machine_id_digest, HASH_STR_LEN); + machine_id_hash[HASH_STR_LEN-1] = '\0'; + + int seed_dir_fd = -1; + int seed_fd = -1; + FILE *seed_file = NULL; + + char *seed_path_tmp = strdup(seed_path); + const char *seed_dir, *seed_name; + char *seed_path_last_slash = strrchr(seed_path_tmp, '/'); + if (seed_path_last_slash) { + *seed_path_last_slash = '\0'; + seed_dir = seed_path_tmp; + seed_name = seed_path_last_slash + 1; + } else { + free(seed_path_tmp); + seed_path_tmp = NULL; + seed_dir = "."; + seed_name = seed_path; + } + char *seed_name_new = NULL; + + if (asprintf(&seed_name_new, ".%s.new", seed_name) == -1) { + fputs("out of memory\n", stderr); + goto out; + } + + seed_dir_fd = open(seed_dir, O_RDONLY | O_DIRECTORY); + if (seed_dir_fd == -1) { + perror("error opening seed directory"); + goto out; + } + + unsigned char fs_id_digest[HASH_LEN]; + if (!get_fs_id_hash(random_ptr, fs_id_digest, seed_dir_fd)) { + fputs("cannot obtain machine id, aborting save\n", stderr); + goto out; + } + char fs_id_hash[HASH_LEN*2+1]; + mem2hex(fs_id_hash, fs_id_digest, HASH_LEN); + fs_id_hash[sizeof(fs_id_hash)-1] = '\0'; + + seed_fd = openat(seed_dir_fd, seed_name_new, O_WRONLY | O_CREAT | O_TRUNC, 0600); + if (seed_fd == -1) { + perror("error opening new seed file"); + goto err; + } + seed_file = fdopen(seed_fd, "w"); + if (!seed_file) { + perror("error converting seed file fd to stream"); + goto err; + } + + if (fwrite(random_ptr, 1, RAND_POOL_SIZE, seed_file) != RAND_POOL_SIZE + || fputs(MAGIC, seed_file) == EOF + || fputs("machine-id ", seed_file) == EOF + || fputs(machine_id_hash, seed_file) == EOF + || fputs("\nfs-id ", seed_file) == EOF + || fputs(fs_id_hash, seed_file) == EOF + || fputs("\ndone\n", seed_file) == EOF) { + fputs("error writing new seed file\n", stderr); + goto err; + } + + if (fflush(seed_file) == EOF) { + perror("error flushing new seed file"); + goto err; + } + if (fsync(seed_fd) == -1) { + perror("error syncing new seed file"); + goto err; + } + if (renameat(seed_dir_fd, seed_name_new, seed_dir_fd, seed_name) == -1) { + perror("error installing new seed file"); + goto err; + } + if (fclose(seed_file) == EOF) { + perror("error closing new seed file"); + goto err; + } + if (fsync(seed_dir_fd) == -1) { + perror("error syncing seed directory"); + goto out; + } + + rv = true; + goto out; + +err: + if (seed_file) { + fclose(seed_file); + if (unlinkat(seed_dir_fd, seed_name_new, 0) == -1) { + perror("error removing temporary seed file"); + rv = false; + } + } + +out: + if (seed_dir_fd != -1) { + if (close(seed_dir_fd) == -1) { + perror("error closing seed directory"); + rv = false; + } + } + + free(seed_path_tmp); + free(seed_name_new); + + return rv; +} + +static void sighandler(int signum) { + switch (signum) { + case SIGHUP: + // do nothing; we just needed to interrupt sleep + break; + case SIGINT: + sigint = 1; + break; + case SIGTERM: + sigterm = 1; + break; + default: + abort(); + } +} + +noreturn static void run(const char *mode, const char *seed_path) { + FILE *seed_file; + int exit_status = 0; + + if (streq(mode, "load")) { + bool refresh_seed = true; + + if (streq(seed_path, "-")) { + fputs("warning: cannot refresh stdin seed\n", stderr); + seed_file = stdin; + refresh_seed = false; + } else { + seed_file = fopen(seed_path, "r"); + } + if (!seed_file) { + perror("error opening seed file"); + exit(1); + } + if (!load(seed_file)) { + if (noperms) + exit_status = 15; + else + exit_status = 1; + } + if (refresh_seed) { + unsigned char random_buf[RAND_POOL_SIZE]; + unsigned char *random_ptr = random_buf; + switch (random_get(random_buf, RAND_POOL_SIZE, GRND_NONBLOCK)) { + case RAND_POOL_SIZE: + goto save; + case -1: + if (errno != EAGAIN) { + perror("getrandom"); + exit(1); + } + } + if (daemon(0, 1) == -1) { + perror("error daemonizing, continuing without"); + } + close(0); + close(1); + random_ptr = NULL; +save: if (!save(seed_path, random_ptr)) + exit_status = 1; + } + exit(exit_status); + } else if (streq(mode, "save")) { + exit(!save(seed_path, NULL)); + } else if (streq(mode, "daemonize")) { + if (streq(seed_path, "-")) { + fputs("error: seed_path cannot be - for daemonize\n", stderr); + exit(2); + } + seed_file = fopen(seed_path, "r"); + if (!seed_file) { + perror("error opening seed file"); + exit(3); + } + if (!load(seed_file)) + fputs("warning: failed to load initial entropy\n", stderr); + + struct sigaction sa; + sa.sa_handler = sighandler; + sigemptyset(&sa.sa_mask); + sa.sa_flags = 0; + sigaction(SIGHUP, &sa, NULL); + sigaction(SIGINT, &sa, NULL); + sigaction(SIGTERM, &sa, NULL); + + if (daemon(seed_path[0] != '/', 1) == -1) { + perror("error daemonizing"); + exit(1); + } + close(0); + close(1); + // don't close stderr because we log there + + while (true) { + if (sigint) + exit(exit_status); + if (!save(seed_path, NULL)) { + exit_status = 1; + fputs("an error occurred while saving, trying again later\n", stderr); + } + if (sigterm) + exit(exit_status); + sleep(DAEMONIZE_SLEEP_TIME); + } + } else if (streq(mode, "daemonise")) { + fputs("invalid mode (did you mean `daemonize'?)\n", stderr); + exit(2); + } else { + fputs("invalid mode, expected load, save, or daemonize\n", stderr); + exit(2); + } +} + +int main(int argc, char *argv[]) { + char *mode, *seed_path; + + switch (argc) { + case 2: + if (streq(argv[1], "-h") || streq(argv[1], "--help")) { + usage(); + exit(0); + } + if (streq(argv[1], "-V") || streq(argv[1], "--version")) { + printf("random-seed %s\n", PACKAGE_VERSION); + exit(0); + } + mode = argv[1]; + seed_path = DEFAULT_SEED_PATH; + break; + case 3: + mode = argv[1]; + seed_path = argv[2]; + break; + default: + fprintf(stderr, "error: invalid arguments\n"); + usage(); + exit(2); + } + + umask(0); + run(mode, seed_path); +} diff --git a/src/sha2.c b/src/sha2.c new file mode 100644 index 0000000..4c2b615 --- /dev/null +++ b/src/sha2.c @@ -0,0 +1,204 @@ +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 02/02/2007 + * Issue date: 04/30/2005 + * + * Copyright (C) 2013, Con Kolivas + * Copyright (C) 2005, 2007 Olivier Gay + * 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. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. + */ + +#include + +#include "sha2.h" + +#define UNPACK32(x, str) \ +{ \ + *((str) + 3) = (uint8_t) ((x) ); \ + *((str) + 2) = (uint8_t) ((x) >> 8); \ + *((str) + 1) = (uint8_t) ((x) >> 16); \ + *((str) + 0) = (uint8_t) ((x) >> 24); \ +} + +#define PACK32(str, x) \ +{ \ + *(x) = ((uint32_t) *((str) + 3) ) \ + | ((uint32_t) *((str) + 2) << 8) \ + | ((uint32_t) *((str) + 1) << 16) \ + | ((uint32_t) *((str) + 0) << 24); \ +} + +#define SHA256_SCR(i) \ +{ \ + w[i] = SHA256_F4(w[i - 2]) + w[i - 7] \ + + SHA256_F3(w[i - 15]) + w[i - 16]; \ +} + +static uint32_t sha256_h0[8] = + {0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, + 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19}; + +uint32_t sha256_k[64] = + {0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, + 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, + 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, + 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, + 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, + 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, + 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, + 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, + 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, + 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, + 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, + 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, + 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, + 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, + 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, + 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2}; + +/* SHA-256 functions */ + +void sha256_transf(sha256_ctx *ctx, const unsigned char *message, + unsigned int block_nb) +{ + uint32_t w[64]; + uint32_t wv[8]; + uint32_t t1, t2; + const unsigned char *sub_block; + int i; + + int j; + + for (i = 0; i < (int) block_nb; i++) { + sub_block = message + (i << 6); + + for (j = 0; j < 16; j++) { + PACK32(&sub_block[j << 2], &w[j]); + } + + for (j = 16; j < 64; j++) { + SHA256_SCR(j); + } + + for (j = 0; j < 8; j++) { + wv[j] = ctx->h[j]; + } + + for (j = 0; j < 64; j++) { + t1 = wv[7] + SHA256_F2(wv[4]) + CH(wv[4], wv[5], wv[6]) + + sha256_k[j] + w[j]; + t2 = SHA256_F1(wv[0]) + MAJ(wv[0], wv[1], wv[2]); + wv[7] = wv[6]; + wv[6] = wv[5]; + wv[5] = wv[4]; + wv[4] = wv[3] + t1; + wv[3] = wv[2]; + wv[2] = wv[1]; + wv[1] = wv[0]; + wv[0] = t1 + t2; + } + + for (j = 0; j < 8; j++) { + ctx->h[j] += wv[j]; + } + } +} + +void sha256(const unsigned char *message, unsigned int len, unsigned char *digest) +{ + sha256_ctx ctx; + + sha256_init(&ctx); + sha256_update(&ctx, message, len); + sha256_final(&ctx, digest); +} + +void sha256_init(sha256_ctx *ctx) +{ + memcpy(ctx->h, sha256_h0, sizeof(uint32_t)*8); + ctx->len = 0; + ctx->tot_len = 0; +} + +void sha256_update(sha256_ctx *ctx, const unsigned char *message, + unsigned int len) +{ + unsigned int block_nb; + unsigned int new_len, rem_len, tmp_len; + const unsigned char *shifted_message; + + tmp_len = SHA256_BLOCK_SIZE - ctx->len; + rem_len = len < tmp_len ? len : tmp_len; + + memcpy(&ctx->block[ctx->len], message, rem_len); + + if (ctx->len + len < SHA256_BLOCK_SIZE) { + ctx->len += len; + return; + } + + new_len = len - rem_len; + block_nb = new_len / SHA256_BLOCK_SIZE; + + shifted_message = message + rem_len; + + sha256_transf(ctx, ctx->block, 1); + sha256_transf(ctx, shifted_message, block_nb); + + rem_len = new_len % SHA256_BLOCK_SIZE; + + memcpy(ctx->block, &shifted_message[block_nb << 6], + rem_len); + + ctx->len = rem_len; + ctx->tot_len += (block_nb + 1) << 6; +} + +void sha256_final(sha256_ctx *ctx, unsigned char *digest) +{ + unsigned int block_nb; + unsigned int pm_len; + unsigned int len_b; + + int i; + + block_nb = (1 + ((SHA256_BLOCK_SIZE - 9) + < (ctx->len % SHA256_BLOCK_SIZE))); + + len_b = (ctx->tot_len + ctx->len) << 3; + pm_len = block_nb << 6; + + memset(ctx->block + ctx->len, 0, pm_len - ctx->len); + ctx->block[ctx->len] = 0x80; + UNPACK32(len_b, ctx->block + pm_len - 4); + + sha256_transf(ctx, ctx->block, block_nb); + + for (i = 0 ; i < 8; i++) { + UNPACK32(ctx->h[i], &digest[i << 2]); + } +} diff --git a/src/sha2.h b/src/sha2.h new file mode 100644 index 0000000..bd968ba --- /dev/null +++ b/src/sha2.h @@ -0,0 +1,69 @@ +/* + * FIPS 180-2 SHA-224/256/384/512 implementation + * Last update: 02/02/2007 + * Issue date: 04/30/2005 + * + * Copyright (C) 2013, Con Kolivas + * Copyright (C) 2005, 2007 Olivier Gay + * 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. + * 3. Neither the name of the project nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE PROJECT 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 PROJECT 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. + */ + +#ifndef SHA2_H +#define SHA2_H + +#include + +#define SHA256_DIGEST_SIZE ( 256 / 8) +#define SHA256_BLOCK_SIZE ( 512 / 8) + +#define SHFR(x, n) (x >> n) +#define ROTR(x, n) ((x >> n) | (x << ((sizeof(x) << 3) - n))) +#define CH(x, y, z) ((x & y) ^ (~x & z)) +#define MAJ(x, y, z) ((x & y) ^ (x & z) ^ (y & z)) + +#define SHA256_F1(x) (ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22)) +#define SHA256_F2(x) (ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25)) +#define SHA256_F3(x) (ROTR(x, 7) ^ ROTR(x, 18) ^ SHFR(x, 3)) +#define SHA256_F4(x) (ROTR(x, 17) ^ ROTR(x, 19) ^ SHFR(x, 10)) + +typedef struct { + unsigned int tot_len; + unsigned int len; + unsigned char block[2 * SHA256_BLOCK_SIZE]; + uint32_t h[8]; +} sha256_ctx; + +extern uint32_t sha256_k[64]; + +void sha256_init(sha256_ctx * ctx); +void sha256_update(sha256_ctx *ctx, const unsigned char *message, + unsigned int len); +void sha256_final(sha256_ctx *ctx, unsigned char *digest); +void sha256(const unsigned char *message, unsigned int len, + unsigned char *digest); + +#endif /* !SHA2_H */ diff --git a/src/util.c b/src/util.c new file mode 100644 index 0000000..a5eb220 --- /dev/null +++ b/src/util.c @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include + +#include "util.h" + +static inline signed char hexchr2int(char c) { + /* No, nobody uses EBCDIC anymore. */ + return (c >= '0' && c <= '9') ? (c - '0') : + (c >= 'a' && c <= 'f') ? (c - 'a' + 10) : + (c >= 'A' && c <= 'F') ? (c - 'A' + 10) : + -1; +} + +/** + * Decode hex. + * + * \param dest where to store the decoded data + * \param size the maximum number of bytes to decode + * \param src the hex-encoded data + * + * \return 0 if an error occurred, otherwise the number of bytes written to + * dest + */ +size_t hex2mem(unsigned char *dest, size_t size, const char *src) { +#ifdef DEBUG + fprintf(stderr, "hex decoding %zu bytes\n", size); +#endif + size_t i; + for (i = 0; i < size; i++) { + int n1 = hexchr2int(src[2*i]); + if (n1 < 0) return 0; + int n2 = hexchr2int(src[2*i+1]); + if (n2 < 0) return 0; + dest[i] = (unsigned char)(n1 << 4 | n2); + } + return i; +} + +static const char *HEX_CHARS = "0123456789abcdef"; + +/** Encode hex. + * + * \param dest where to store the encoded data (must have at least size*2+1 bytes) + * \param src the data to encode + * \param size the number of bytes to encode + */ +void mem2hex(char *dest, const void *src, size_t size) { + for (size_t i = 0; i < size; i++) { + unsigned char c = *((const unsigned char *)src + i); + dest[2*i] = HEX_CHARS[c >> 4]; + dest[2*i+1] = HEX_CHARS[c & 0xf]; + } + dest[size*2] = '\0'; +} + +void hash(const unsigned char salt[static SALT_LEN], unsigned char *out, const void *in, size_t size) { + assert(size < INT_MAX - SALT_LEN - 100); +#ifdef DEBUG + fprintf(stderr, "hashing %zu bytes starting with 0x%x ending with 0x%x\n", size, (int)((unsigned char*)in)[0], (int)((unsigned char*)in)[size-1]); +#endif + sha256_ctx ctx; + sha256_init(&ctx); + sha256_update(&ctx, salt, SALT_LEN); + sha256_update(&ctx, in, (unsigned int)size); + sha256_final(&ctx, out); +} + +static void print_hash(const unsigned char digest[static HASH_LEN]) { +#ifdef DEBUG + char hash[HASH_LEN*2+1]; + mem2hex(hash, digest, HASH_LEN); + fprintf(stderr, "hash: %s\n", hash); +#else + (void)digest; +#endif +} + +bool hash_match(const unsigned char digest[static HASH_LEN], const char *arg) { + unsigned char theirdigest[HASH_LEN]; + if (hex2mem(theirdigest, sizeof(theirdigest), arg) == 0) { + fputs("error decoding hex hash\n", stderr); + exit(1); + } +#ifdef DEBUG + fprintf(stderr, "comparing hash, theirs: %s = 0x%02x..0x%02x, ours: 0x%02x..0x%02x\n", arg, (int)theirdigest[0], (int)theirdigest[HASH_LEN-1], (int)digest[0], (int)theirdigest[HASH_LEN-1]); + fputs(" our ", stderr); + print_hash(digest); + fputs("their ", stderr); + print_hash(theirdigest); +#endif + return !memcmp(digest, theirdigest, HASH_LEN); +} + +ssize_t random_get(void *buf, size_t buflen, unsigned int flags) { + memset(buf, 0, buflen); + + long rv = syscall(SYS_getrandom, buf, buflen, flags); + if (rv == -1) { + if (errno == ENOSYS) { + fputs("getrandom returned ENOSYS. random-seed requires Linux 3.17\n", stderr); + exit(1); + } + } else { + bool all_zero = true; + if (buflen > 32) { + for (size_t i = 0; i < buflen; i++) { + if (((unsigned char *)buf)[i] != 0) { + all_zero = false; + break; + } + } + } + if (all_zero) { + fputs("getrandom returned all zeros, probably broken\n", stderr); + exit(1); + } + } + + return rv; +} diff --git a/src/util.h b/src/util.h new file mode 100644 index 0000000..9cd2bd4 --- /dev/null +++ b/src/util.h @@ -0,0 +1,34 @@ +#ifndef UTIL_H +#define UTIL_H + +#include +#include +#include +#include +#include +#include + +#include "sha2.h" + +#define GRND_NONBLOCK 0x01 +#define GRND_RANDOM 0x02 + +/* The pool size is fixed at 4096 bits since Linux 2.6. */ +#define RAND_POOL_SIZE 512 +/* SHA-256 */ +#define HASH_LEN 32 +#define HASH_STR_LEN 65 +/* The salt is the random data */ +#define SALT_LEN RAND_POOL_SIZE + +static inline bool streq(const char *s1, const char *s2) { + return !strcmp(s1, s2); +} + +ssize_t random_get(void *buf, size_t buflen, unsigned int flags); +size_t hex2mem(unsigned char *dest, size_t size, const char *src); +void mem2hex(char *dest, const void *src, size_t size); +void hash(const unsigned char salt[static SALT_LEN], unsigned char *out, const void *in, size_t size); +bool hash_match(const unsigned char digest[static HASH_LEN], const char *arg); + +#endif diff --git a/util.c b/util.c deleted file mode 100644 index a5eb220..0000000 --- a/util.c +++ /dev/null @@ -1,123 +0,0 @@ -#include -#include -#include -#include -#include - -#include "util.h" - -static inline signed char hexchr2int(char c) { - /* No, nobody uses EBCDIC anymore. */ - return (c >= '0' && c <= '9') ? (c - '0') : - (c >= 'a' && c <= 'f') ? (c - 'a' + 10) : - (c >= 'A' && c <= 'F') ? (c - 'A' + 10) : - -1; -} - -/** - * Decode hex. - * - * \param dest where to store the decoded data - * \param size the maximum number of bytes to decode - * \param src the hex-encoded data - * - * \return 0 if an error occurred, otherwise the number of bytes written to - * dest - */ -size_t hex2mem(unsigned char *dest, size_t size, const char *src) { -#ifdef DEBUG - fprintf(stderr, "hex decoding %zu bytes\n", size); -#endif - size_t i; - for (i = 0; i < size; i++) { - int n1 = hexchr2int(src[2*i]); - if (n1 < 0) return 0; - int n2 = hexchr2int(src[2*i+1]); - if (n2 < 0) return 0; - dest[i] = (unsigned char)(n1 << 4 | n2); - } - return i; -} - -static const char *HEX_CHARS = "0123456789abcdef"; - -/** Encode hex. - * - * \param dest where to store the encoded data (must have at least size*2+1 bytes) - * \param src the data to encode - * \param size the number of bytes to encode - */ -void mem2hex(char *dest, const void *src, size_t size) { - for (size_t i = 0; i < size; i++) { - unsigned char c = *((const unsigned char *)src + i); - dest[2*i] = HEX_CHARS[c >> 4]; - dest[2*i+1] = HEX_CHARS[c & 0xf]; - } - dest[size*2] = '\0'; -} - -void hash(const unsigned char salt[static SALT_LEN], unsigned char *out, const void *in, size_t size) { - assert(size < INT_MAX - SALT_LEN - 100); -#ifdef DEBUG - fprintf(stderr, "hashing %zu bytes starting with 0x%x ending with 0x%x\n", size, (int)((unsigned char*)in)[0], (int)((unsigned char*)in)[size-1]); -#endif - sha256_ctx ctx; - sha256_init(&ctx); - sha256_update(&ctx, salt, SALT_LEN); - sha256_update(&ctx, in, (unsigned int)size); - sha256_final(&ctx, out); -} - -static void print_hash(const unsigned char digest[static HASH_LEN]) { -#ifdef DEBUG - char hash[HASH_LEN*2+1]; - mem2hex(hash, digest, HASH_LEN); - fprintf(stderr, "hash: %s\n", hash); -#else - (void)digest; -#endif -} - -bool hash_match(const unsigned char digest[static HASH_LEN], const char *arg) { - unsigned char theirdigest[HASH_LEN]; - if (hex2mem(theirdigest, sizeof(theirdigest), arg) == 0) { - fputs("error decoding hex hash\n", stderr); - exit(1); - } -#ifdef DEBUG - fprintf(stderr, "comparing hash, theirs: %s = 0x%02x..0x%02x, ours: 0x%02x..0x%02x\n", arg, (int)theirdigest[0], (int)theirdigest[HASH_LEN-1], (int)digest[0], (int)theirdigest[HASH_LEN-1]); - fputs(" our ", stderr); - print_hash(digest); - fputs("their ", stderr); - print_hash(theirdigest); -#endif - return !memcmp(digest, theirdigest, HASH_LEN); -} - -ssize_t random_get(void *buf, size_t buflen, unsigned int flags) { - memset(buf, 0, buflen); - - long rv = syscall(SYS_getrandom, buf, buflen, flags); - if (rv == -1) { - if (errno == ENOSYS) { - fputs("getrandom returned ENOSYS. random-seed requires Linux 3.17\n", stderr); - exit(1); - } - } else { - bool all_zero = true; - if (buflen > 32) { - for (size_t i = 0; i < buflen; i++) { - if (((unsigned char *)buf)[i] != 0) { - all_zero = false; - break; - } - } - } - if (all_zero) { - fputs("getrandom returned all zeros, probably broken\n", stderr); - exit(1); - } - } - - return rv; -} diff --git a/util.h b/util.h deleted file mode 100644 index 9cd2bd4..0000000 --- a/util.h +++ /dev/null @@ -1,34 +0,0 @@ -#ifndef UTIL_H -#define UTIL_H - -#include -#include -#include -#include -#include -#include - -#include "sha2.h" - -#define GRND_NONBLOCK 0x01 -#define GRND_RANDOM 0x02 - -/* The pool size is fixed at 4096 bits since Linux 2.6. */ -#define RAND_POOL_SIZE 512 -/* SHA-256 */ -#define HASH_LEN 32 -#define HASH_STR_LEN 65 -/* The salt is the random data */ -#define SALT_LEN RAND_POOL_SIZE - -static inline bool streq(const char *s1, const char *s2) { - return !strcmp(s1, s2); -} - -ssize_t random_get(void *buf, size_t buflen, unsigned int flags); -size_t hex2mem(unsigned char *dest, size_t size, const char *src); -void mem2hex(char *dest, const void *src, size_t size); -void hash(const unsigned char salt[static SALT_LEN], unsigned char *out, const void *in, size_t size); -bool hash_match(const unsigned char digest[static HASH_LEN], const char *arg); - -#endif -- cgit v1.2.3-54-g00ecf