mirror of
https://github.com/angt/secret
synced 2024-12-25 21:58:25 +01:00
Import totp
Signed-off-by: Adrien Gallouët <adrien@gallouet.fr>
This commit is contained in:
parent
be7fdba32a
commit
4074c9b13b
2 changed files with 99 additions and 7 deletions
17
README.md
17
README.md
|
@ -14,6 +14,7 @@ But it does have some interesting features:
|
||||||
- Secret names completion is available after calling the secret agent.
|
- Secret names completion is available after calling the secret agent.
|
||||||
- Supports unstored secrets. Derived from some simple keys and a passphrase.
|
- Supports unstored secrets. Derived from some simple keys and a passphrase.
|
||||||
- Supports multiple passphrases. A confirmation is requested for each new passphrase.
|
- Supports multiple passphrases. A confirmation is requested for each new passphrase.
|
||||||
|
- Supports TOTP natively. The name must contain the word `totp`.
|
||||||
- Depends only on the [libhydrogen](https://libhydrogen.org/) library.
|
- Depends only on the [libhydrogen](https://libhydrogen.org/) library.
|
||||||
- Small, simple and non obfuscated C code. Well, I hope so :)
|
- Small, simple and non obfuscated C code. Well, I hope so :)
|
||||||
|
|
||||||
|
@ -148,6 +149,15 @@ Pipe a secret:
|
||||||
Passphrase:
|
Passphrase:
|
||||||
xdhhnsd
|
xdhhnsd
|
||||||
|
|
||||||
|
Add a TOTP token:
|
||||||
|
|
||||||
|
$ echo -n JBSWY3DPEHPK3PXP | base32 -d | secret set test/totp
|
||||||
|
Passphrase:
|
||||||
|
|
||||||
|
$ secret show test/totp
|
||||||
|
Passphrase:
|
||||||
|
$ 123456
|
||||||
|
|
||||||
Derive a deterministic (a.k.a. unstored) secret:
|
Derive a deterministic (a.k.a. unstored) secret:
|
||||||
|
|
||||||
$ secret pass me@domain.com
|
$ secret pass me@domain.com
|
||||||
|
@ -160,13 +170,11 @@ Subkeys are also supported, this allows to update your secret in a clean way:
|
||||||
Passphrase:
|
Passphrase:
|
||||||
F"1j;-X]t.Pi>.xf5hG,]dUMz
|
F"1j;-X]t.Pi>.xf5hG,]dUMz
|
||||||
|
|
||||||
Storing binary secrets is supported:
|
Add a binary secret:
|
||||||
|
|
||||||
$ dd if=/dev/urandom bs=1 count=32 2>/dev/null | secret set mykey
|
$ dd if=/dev/urandom bs=1 count=32 2>/dev/null | secret set mykey
|
||||||
Passphrase:
|
Passphrase:
|
||||||
|
|
||||||
Then, use a pipe to get it:
|
|
||||||
|
|
||||||
$ secret show mykey | xxd
|
$ secret show mykey | xxd
|
||||||
Passphrase:
|
Passphrase:
|
||||||
00000000: 0ee9 cdb3 de0a 3e71 b623 726d 5d7e eb23 ......>q.#rm]~.#
|
00000000: 0ee9 cdb3 de0a 3e71 b623 726d 5d7e eb23 ......>q.#rm]~.#
|
||||||
|
@ -182,9 +190,6 @@ Now, the passphrase is not requested and completion fully works!
|
||||||
If you don't use `bash` but still want completion,
|
If you don't use `bash` but still want completion,
|
||||||
run `secret agent bash` or (much better) send a PR to add support for your shiny shell :)
|
run `secret agent bash` or (much better) send a PR to add support for your shiny shell :)
|
||||||
|
|
||||||
Finally, if you want to generate TOTP tokens go check out [totp](https://github.com/angt/totp).
|
|
||||||
You can also use the [totp branch](https://github.com/angt/secret/tree/totp).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
For feature requests and bug reports,
|
For feature requests and bug reports,
|
||||||
please create an [issue](https://github.com/angt/secret/issues).
|
please create an [issue](https://github.com/angt/secret/issues).
|
||||||
|
|
89
secret.c
89
secret.c
|
@ -13,6 +13,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
#include <time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#define S_COUNT(x) (sizeof(x) / sizeof((x)[0]))
|
#define S_COUNT(x) (sizeof(x) / sizeof((x)[0]))
|
||||||
|
@ -444,6 +445,87 @@ s_do(int argc, char **argv, void *data)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
s_sha1_process(const uint8_t *buf, uint32_t x[5])
|
||||||
|
{
|
||||||
|
uint32_t w[80];
|
||||||
|
uint32_t a = x[0], b = x[1], c = x[2], d = x[3], e = x[4];
|
||||||
|
|
||||||
|
for (int i = 0; i < 16; i++)
|
||||||
|
w[i] = load32_be(&buf[i << 2]);
|
||||||
|
|
||||||
|
for (int i = 16; i < 80; i++)
|
||||||
|
w[i] = ROTL32(w[i - 3] ^ w[i - 8] ^ w[i - 14] ^ w[i - 16], 1);
|
||||||
|
|
||||||
|
for (int i = 0; i < 80; i++) {
|
||||||
|
uint32_t t = ROTL32(a, 5) + e + w[i];
|
||||||
|
if (i < 20) t += 0x5A827999 + ((b & c) | ((~b) & d));
|
||||||
|
else if (i < 40) t += 0x6ED9EBA1 + (b ^ c ^ d);
|
||||||
|
else if (i < 60) t += 0x8F1BBCDC + ((b & c) | (b & d) | (c & d));
|
||||||
|
else t += 0xCA62C1D6 + (b ^ c ^ d);
|
||||||
|
e = d; d = c; c = ROTL32(b, 30); b = a; a = t;
|
||||||
|
}
|
||||||
|
x[0] += a; x[1] += b; x[2] += c; x[3] += d; x[4] += e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
s_sha1(uint8_t *digest, uint8_t *buf, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t tmp[64] = {0};
|
||||||
|
uint32_t x[] = {0x67452301, 0xEFCDAB89, 0x98BADCFE, 0x10325476, 0xC3D2E1F0};
|
||||||
|
size_t p = 0;
|
||||||
|
|
||||||
|
for (; p + 64 <= len; p += 64)
|
||||||
|
s_sha1_process(buf + p, x);
|
||||||
|
|
||||||
|
if (len > p)
|
||||||
|
memcpy(tmp, buf + p, len - p);
|
||||||
|
|
||||||
|
p = len - p;
|
||||||
|
tmp[p++] = 0x80;
|
||||||
|
|
||||||
|
if (p > 56) {
|
||||||
|
s_sha1_process(tmp, x);
|
||||||
|
memset(tmp, 0, sizeof(tmp));
|
||||||
|
}
|
||||||
|
store64_be(tmp + 56, len << 3);
|
||||||
|
s_sha1_process(tmp, x);
|
||||||
|
|
||||||
|
for (int i = 0; i < 5; i++)
|
||||||
|
store32_be(&digest[i << 2], x[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
s_totp(const char *secret, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t h[20];
|
||||||
|
uint8_t ki[64 + 8] = {0};
|
||||||
|
uint8_t ko[64 + 20] = {0};
|
||||||
|
|
||||||
|
if (!len || len > 64)
|
||||||
|
return;
|
||||||
|
|
||||||
|
memcpy(ki, secret, len);
|
||||||
|
memcpy(ko, secret, len);
|
||||||
|
|
||||||
|
for (int i = 0; i < 64; i++) {
|
||||||
|
ki[i] ^= 0x36;
|
||||||
|
ko[i] ^= 0x5c;
|
||||||
|
}
|
||||||
|
store64_be(&ki[64], ((uint64_t)time(NULL)) / 30);
|
||||||
|
s_sha1(&ko[64], ki, sizeof(ki));
|
||||||
|
s_sha1(h, ko, sizeof(ko));
|
||||||
|
|
||||||
|
hydro_memzero(ki, sizeof(ki));
|
||||||
|
hydro_memzero(ko, sizeof(ko));
|
||||||
|
|
||||||
|
uint32_t ret = (load32_be(&h[h[19] & 0xF]) & ~(UINT32_C(1) << 31))
|
||||||
|
% UINT32_C(1000000);
|
||||||
|
char tmp[7];
|
||||||
|
if (snprintf(tmp, sizeof(tmp), "%06" PRIu32, ret) == 6)
|
||||||
|
s_write(1, tmp, 6);
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
s_show(int argc, char **argv, void *data)
|
s_show(int argc, char **argv, void *data)
|
||||||
{
|
{
|
||||||
|
@ -459,7 +541,12 @@ s_show(int argc, char **argv, void *data)
|
||||||
const char *secret = s_get_secret(fd, argv[1], 0);
|
const char *secret = s_get_secret(fd, argv[1], 0);
|
||||||
|
|
||||||
if (secret) {
|
if (secret) {
|
||||||
s_write(1, secret, load16_le(s.x.entry.slen));
|
size_t len = load16_le(s.x.entry.slen);
|
||||||
|
if (strstr(argv[1], "totp")) {
|
||||||
|
s_totp(secret, len);
|
||||||
|
} else {
|
||||||
|
s_write(1, secret, len);
|
||||||
|
}
|
||||||
if (isatty(1)) s_write(1, "\n", 1);
|
if (isatty(1)) s_write(1, "\n", 1);
|
||||||
}
|
}
|
||||||
close(fd);
|
close(fd);
|
||||||
|
|
Loading…
Reference in a new issue