From b17164751fc784fc126de844aa7c4a2edfd16dc2 Mon Sep 17 00:00:00 2001 From: Felix Fietkau Date: Fri, 13 Dec 2024 17:56:36 +0100 Subject: [PATCH] unet-tool: add support for generating keys from salt + seed passphrase Uses PBKDF2-HMAC-SHA512 with configurable number of rounds to derive the key Signed-off-by: Felix Fietkau --- cli.c | 82 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- sha512.c | 33 ++++++++++++++++++++++- sha512.h | 3 +++ 3 files changed, 115 insertions(+), 3 deletions(-) diff --git a/cli.c b/cli.c index e3654f6..12d3bd5 100644 --- a/cli.c +++ b/cli.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include #include @@ -35,6 +37,8 @@ static struct blob_buf b; static FILE *out_file; static bool quiet; static bool sync_done; +static bool has_key; +static bool password_prompt; static enum { CMD_UNKNOWN, CMD_GENERATE, @@ -82,6 +86,9 @@ static int usage(const char *progname) " -K |-: Set secret key from file or stdin\n" " -h |- Set peer private key from file or stdin\n" " (for network data down-/upload)\n" + " -s , Generating secret key from seed using rounds and \n" + " (passphrase is read from stdin)\n" + " -p Prompt for seed password\n" "\n", progname); return 1; } @@ -429,6 +436,9 @@ static int cmd_generate(int argc, char **argv) FILE *f; int ret; + if (has_key) + goto out; + f = fopen("/dev/urandom", "r"); if (!f) { INFO("Can't open /dev/urandom\n"); @@ -444,6 +454,8 @@ static int cmd_generate(int argc, char **argv) } ed25519_prepare(seckey); + +out: print_key(seckey); return 0; @@ -479,6 +491,58 @@ static bool parse_key(uint8_t *dest, const char *str) return true; } +static void +pbkdf2_hmac_sha512(uint8_t *dest, const void *key, size_t key_len, + const void *salt, size_t salt_len, + unsigned int rounds) +{ + uint8_t hash[SHA512_HASH_SIZE]; + + hmac_sha512(dest, key, key_len, salt, salt_len); + + for (size_t i = 0; i < rounds - 1; i++) { + hmac_sha512(hash, key, key_len, dest, SHA512_HASH_SIZE); + for (size_t k = 0; k < SHA512_HASH_SIZE; k++) + dest[k] ^= hash[k]; + } +} + +static bool parse_seed(uint8_t *dest, const char *salt) +{ + uint8_t hash[SHA512_HASH_SIZE]; + char buf[256], *pw = buf; + unsigned long rounds; + size_t len = 0; + char *sep; + + rounds = strtoul(salt, &sep, 0); + if (!rounds || *sep != ',') { + INFO("Invalid number of rounds\n"); + return false; + } + + if (password_prompt) { + pw = getpass("Password: "); + if (pw) + len = strlen(pw); + } else { + len = fread(buf, 1, sizeof(buf), stdin); + if (!feof(stdin)) { + INFO("Key data too long\n"); + return false; + } + } + if (len < 12) { + INFO("Key data too short\n"); + return false; + } + + pbkdf2_hmac_sha512(hash, pw, len, salt, strlen(salt), rounds); + memcpy(dest, hash, EDSIGN_PUBLIC_KEY_SIZE); + + return true; +} + static bool cmd_needs_peerkey(void) { switch (cmd) { @@ -530,11 +594,11 @@ int main(int argc, char **argv) const char *progname = argv[0]; const char *out_filename = NULL; const char *cmd_arg = NULL; - bool has_key = false, has_pubkey = false; + bool has_pubkey = false; bool has_peerkey = false; int ret, ch; - while ((ch = getopt(argc, argv, "h:k:K:o:qD:GHPSU:V")) != -1) { + while ((ch = getopt(argc, argv, "h:k:K:o:qD:GHpPs:SU:V")) != -1) { switch (ch) { case 'D': case 'U': @@ -567,6 +631,17 @@ int main(int argc, char **argv) has_peerkey = true; break; + case 's': + if (has_pubkey) + return usage(progname); + + if (!parse_seed(seckey, optarg)) + return 1; + + has_key = true; + edsign_sec_to_pub(pubkey, seckey); + has_pubkey = true; + break; case 'k': if (has_pubkey) return usage(progname); @@ -590,6 +665,9 @@ int main(int argc, char **argv) edsign_sec_to_pub(pubkey, seckey); has_pubkey = true; break; + case 'p': + password_prompt = true; + break; case 'U': cmd = CMD_UPLOAD; cmd_arg = optarg; diff --git a/sha512.c b/sha512.c index d06d65b..57d3221 100644 --- a/sha512.c +++ b/sha512.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2015 Felix Fietkau + * Copyright (C) 2015-2024 Felix Fietkau * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -256,3 +256,34 @@ void sha512_final(struct sha512_state *s, uint8_t *hash) memcpy(hash, tmp, len); } } + +void hmac_sha512(void *dest, const void *key, size_t key_len, + const void *data, size_t data_len) +{ + uint8_t k_pad[2 * SHA512_HASH_SIZE] = {}; + struct sha512_state s; + + if (key_len > 128) { + sha512_init(&s); + sha512_add(&s, key, key_len); + sha512_final(&s, k_pad); + } else { + memcpy(k_pad, key, key_len); + } + + for (size_t i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36; + + sha512_init(&s); + sha512_add(&s, k_pad, sizeof(k_pad)); + sha512_add(&s, data, data_len); + sha512_final(&s, dest); + + for (size_t i = 0; i < sizeof(k_pad); i++) + k_pad[i] ^= 0x36 ^ 0x5c; + + sha512_init(&s); + sha512_add(&s, k_pad, sizeof(k_pad)); + sha512_add(&s, dest, SHA512_HASH_SIZE); + sha512_final(&s, dest); +} diff --git a/sha512.h b/sha512.h index 7fb0054..1909ae3 100644 --- a/sha512.h +++ b/sha512.h @@ -60,4 +60,7 @@ sha512_final_get(struct sha512_state *s) return s->partial; } +void hmac_sha512(void *dest, const void *key, size_t key_len, + const void *data, size_t data_len); + #endif -- 2.30.2