From d771c320f330b68c907fda5595284f27c1d66b5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adrien=20Gallou=C3=ABt?= Date: Wed, 29 Apr 2020 11:18:55 +0000 Subject: [PATCH] Add command pass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Adrien Gallouët --- README.md | 12 ++++++++++-- secret.c | 56 ++++++++++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 0a95e6a..4a8a9ee 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Keep your little secrets, publicly. - No configuration. Get back your file and you're done. - Secret's name (hostname, mail, login, etc.) are also encrypted. - Secret agent only trusts subprocesses. Not all user processes! How nice is that? + - Unstored secrets! Derived from a simple key and a passphrase. - Supports multiple passphrases. Not super user-friendly but nice to have. - Depends only on the [libhydrogen](https://libhydrogen.org/) library. - Small, simple and non obfuscated C code. Well, I hope so :) @@ -46,6 +47,7 @@ Available commands: set Set a new secret renew Regenerate an existing secret reset Update an existing secret + pass Derivate a new secret agent Run a process in a trusted zone version Show version @@ -64,13 +66,19 @@ Add a new randomly generated secret: $ secret new test Passphrase: - 9{6u0ue>5&W2+z#OR:`X<@-# + ^>f.8%]_zoN^jSi0CO_{(yYY5 Show the secret: $ secret show test Passphrase: - 9{6u0ue>5&W2+z#OR:`X<@-# + ^>f.8%]_zoN^jSi0CO_{(yYY5 + +Derive a deterministic (a.k.a. unstored) secret: + + $ secret pass me@domain.com + Passphrase: + a`4$B2mJ=|"HD?b4:/y"?wOaQ Storing binary secrets is supported: diff --git a/secret.c b/secret.c index 2935f51..2984177 100644 --- a/secret.c +++ b/secret.c @@ -22,6 +22,7 @@ #define S_VER_MAJOR 0U #define S_VER_MINOR 1U #define S_ENTRYSIZE 512U +#define S_PWDGENLEN 25U #define S_ENV_AGENT "SECRET_AGENT" #define S_ENV_STORE "SECRET_STORE" @@ -46,10 +47,12 @@ struct { uint8_t enc[S_ENTRYSIZE]; char ctx_master[hydro_pwhash_CONTEXTBYTES]; char ctx_secret[hydro_secretbox_CONTEXTBYTES]; + char ctx_passwd[hydro_pwhash_CONTEXTBYTES]; } s = { .pipe = {-1, -1}, .ctx_master = "MASTER", .ctx_secret = "SECRET", + .ctx_passwd = "PASSWD", }; _Noreturn static void @@ -87,6 +90,13 @@ s_fatal(const char *fmt, ...) s_exit(1); } +_Noreturn static void +s_oops(const int line) +{ + s_fatal("Oops at line %i", line); + s_exit(2); +} + static size_t s_read(int fd, void *data, size_t size) { @@ -223,7 +233,7 @@ s_open_secret(int use_tty) hydro_memzero(pass, sizeof(pass)); if (r) - s_fatal("Call of the Jedi..."); + s_oops(__LINE__); return fd; } @@ -240,7 +250,7 @@ s_print_keys(int use_tty) continue; size_t len = strnlen(s.x.entry.msg, sizeof(s.x.entry.msg)); if (len >= sizeof(s.x.entry.msg)) - s_fatal("Luckily I was paranoid..."); + s_oops(__LINE__); s_write(1, s.x.entry.msg, len); s_write(1, "\n", 1); } @@ -356,6 +366,16 @@ s_help_keys(int argc, char **argv, int print_keys) s_exit(0); } +static void +s_normalize_and_show(unsigned char *buf, size_t size) +{ + for (size_t i = 0; i < size; i++) + buf[i] = '!' + buf[i] % (1U + '~' - '!'); + + s_write(1, buf, size); + if (isatty(1)) s_write(1, "\n", 1); +} + struct s_op { int create; int generate; @@ -375,17 +395,12 @@ s_do(int argc, char **argv, void *data) s_get_secret(fd, argv[1], op->create); unsigned char secret[S_ENTRYSIZE]; - size_t len = 25; + size_t len = S_PWDGENLEN; if (op->generate) { hydro_memzero(secret, sizeof(secret)); hydro_random_buf(secret, len); - - for (size_t i = 0; i < len; i++) - secret[i] = '!' + secret[i] % (1U + '~' - '!'); - - s_write(1, secret, len); - if (isatty(1)) s_write(1, "\n", 1); + s_normalize_and_show(secret, len); } else { len = isatty(0) ? s_input(secret, sizeof(secret), "Secret: ") : s_read(0, secret, sizeof(secret)); @@ -417,6 +432,28 @@ s_show(int argc, char **argv, void *data) return 0; } +static int +s_pass(int argc, char **argv, void *data) +{ + s_help_keys(argc, argv, 0); + + if (argc != 2) + return argc; + + close(s_open_secret(1)); + + unsigned char secret[S_PWDGENLEN]; + int r = hydro_pwhash_deterministic(secret, sizeof(secret), + argv[1], strlen(argv[1]), + s.ctx_passwd, s.x.key, + load64_le(s.hdr.opslimit), 0, 1); + if (r) + s_oops(__LINE__); + + s_normalize_and_show(secret, sizeof(secret)); + return 0; +} + static void s_cloexec(int fd) { @@ -622,6 +659,7 @@ main(int argc, char **argv) {"set", "Set a new secret", &s_do, &s_set, .grp = 1}, {"renew", "Regenerate an existing secret", &s_do, &s_rnw, .grp = 1}, {"reset", "Update an existing secret", &s_do, &s_rst, .grp = 1}, + {"pass", "Derivate a new secret", &s_pass, NULL, .grp = 1}, {"agent", "Run a process in a trusted zone", &s_agent, NULL, .grp = 1}, {"version", "Show version", &s_version, NULL, .grp = 1}, {0}};