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 --- 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 ++++ 6 files changed, 1058 insertions(+) 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 (limited to 'src') 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 -- cgit v1.2.3-70-g09d2