summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau2024-12-13 16:56:36 +0000
committerFelix Fietkau2024-12-15 15:11:33 +0000
commitb17164751fc784fc126de844aa7c4a2edfd16dc2 (patch)
tree2edf1632105b55d3e33499f1de6bc8bac6a26201
parentbbd3e0eb14191962b2a7197f2f5fa0bbddde9607 (diff)
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 <nbd@nbd.name>
-rw-r--r--cli.c82
-rw-r--r--sha512.c33
-rw-r--r--sha512.h3
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 <stdlib.h>
#include <fcntl.h>
#include <errno.h>
+#include <pwd.h>
+#include <unistd.h>
#include <libubox/utils.h>
#include <libubox/uloop.h>
#include <libubox/blobmsg.h>
@@ -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 <keyfile>|-: Set secret key from file or stdin\n"
" -h <keyfile>|- Set peer private key from file or stdin\n"
" (for network data down-/upload)\n"
+ " -s <n>,<salt> Generating secret key from seed using <n> rounds and <salt>\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 <nbd@openwrt.org>
+ * Copyright (C) 2015-2024 Felix Fietkau <nbd@nbd.name>
*
* 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