tRead passphrase interactively and derivate a key from it - safe - password protected secret keeper
 (HTM) git clone git://git.z3bra.org/safe.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit bb6f5f8dfd93aea029e27e5c38964bbf05c649a2
 (DIR) parent d003bd0061a5dbb5d92bc0e71c0e35f69fdfe04a
 (HTM) Author: z3bra <contactatz3bradotorg>
       Date:   Tue, 21 May 2019 23:56:27 +0200
       
       Read passphrase interactively and derivate a key from it
       
       Diffstat:
         M mkfile                              |       2 +-
         A readpassphrase.c                    |     187 +++++++++++++++++++++++++++++++
         A readpassphrase.h                    |      31 +++++++++++++++++++++++++++++++
         M safe.c                              |     102 +++++++++++++++++++++++--------
       
       4 files changed, 296 insertions(+), 26 deletions(-)
       ---
 (DIR) diff --git a/mkfile b/mkfile
       t@@ -10,7 +10,7 @@ LDFLAGS =
        LDLIBS = -lsodium
        
        BIN = safe
       -SRC = ${BIN}.c
       +SRC = ${BIN}.c readpassphrase.c
        OBJ = ${SRC:%.c=%.o}
        
        ${BIN}: $OBJ
 (DIR) diff --git a/readpassphrase.c b/readpassphrase.c
       t@@ -0,0 +1,187 @@
       +/*        $OpenBSD: readpassphrase.c,v 1.25 2015/09/14 10:45:27 guenther Exp $        */
       +
       +/*
       + * Copyright (c) 2000-2002, 2007, 2010
       + *        Todd C. Miller <Todd.Miller@courtesan.com>
       + *
       + * Permission to use, copy, modify, and distribute this software for any
       + * purpose with or without fee is hereby granted, provided that the above
       + * copyright notice and this permission notice appear in all copies.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       + *
       + * Sponsored in part by the Defense Advanced Research Projects
       + * Agency (DARPA) and Air Force Research Laboratory, Air Force
       + * Materiel Command, USAF, under agreement number F39502-99-1-0512.
       + */
       +
       +#include <ctype.h>
       +#include <errno.h>
       +#include <fcntl.h>
       +#include <paths.h>
       +#include <pwd.h>
       +#include <signal.h>
       +#include <string.h>
       +#include <termios.h>
       +#include <unistd.h>
       +
       +#include "readpassphrase.h"
       +
       +#ifdef TCSASOFT
       +#define _T_FLUSH        (TCSAFLUSH|TCSASOFT)
       +#else
       +#define _T_FLUSH        (TCSAFLUSH)
       +#endif
       +
       +static volatile sig_atomic_t signo[_NSIG];
       +
       +static void handler(int);
       +
       +char *
       +readpassphrase(const char *prompt, char *buf, size_t bufsiz, int flags)
       +{
       +        ssize_t nr;
       +        int input, output, save_errno, i, need_restart;
       +        char ch, *p, *end;
       +        struct termios term, oterm;
       +        struct sigaction sa, savealrm, saveint, savehup, savequit, saveterm;
       +        struct sigaction savetstp, savettin, savettou, savepipe;
       +
       +        /* I suppose we could alloc on demand in this case (XXX). */
       +        if (bufsiz == 0) {
       +                errno = EINVAL;
       +                return(NULL);
       +        }
       +
       +restart:
       +        for (i = 0; i < _NSIG; i++)
       +                signo[i] = 0;
       +        nr = -1;
       +        save_errno = 0;
       +        need_restart = 0;
       +        /*
       +         * Read and write to /dev/tty if available.  If not, read from
       +         * stdin and write to stderr unless a tty is required.
       +         */
       +        if ((flags & RPP_STDIN) ||
       +            (input = output = open(_PATH_TTY, O_RDWR)) == -1) {
       +                if (flags & RPP_REQUIRE_TTY) {
       +                        errno = ENOTTY;
       +                        return(NULL);
       +                }
       +                input = STDIN_FILENO;
       +                output = STDERR_FILENO;
       +        }
       +
       +        /*
       +         * Turn off echo if possible.
       +         * If we are using a tty but are not the foreground pgrp this will
       +         * generate SIGTTOU, so do it *before* installing the signal handlers.
       +         */
       +        if (input != STDIN_FILENO && tcgetattr(input, &oterm) == 0) {
       +                memcpy(&term, &oterm, sizeof(term));
       +                if (!(flags & RPP_ECHO_ON))
       +                        term.c_lflag &= ~(ECHO | ECHONL);
       +#ifdef VSTATUS
       +                if (term.c_cc[VSTATUS] != _POSIX_VDISABLE)
       +                        term.c_cc[VSTATUS] = _POSIX_VDISABLE;
       +#endif
       +                (void)tcsetattr(input, _T_FLUSH, &term);
       +        } else {
       +                memset(&term, 0, sizeof(term));
       +                term.c_lflag |= ECHO;
       +                memset(&oterm, 0, sizeof(oterm));
       +                oterm.c_lflag |= ECHO;
       +        }
       +
       +        /*
       +         * Catch signals that would otherwise cause the user to end
       +         * up with echo turned off in the shell.  Don't worry about
       +         * things like SIGXCPU and SIGVTALRM for now.
       +         */
       +        sigemptyset(&sa.sa_mask);
       +        sa.sa_flags = 0;                /* don't restart system calls */
       +        sa.sa_handler = handler;
       +        (void)sigaction(SIGALRM, &sa, &savealrm);
       +        (void)sigaction(SIGHUP, &sa, &savehup);
       +        (void)sigaction(SIGINT, &sa, &saveint);
       +        (void)sigaction(SIGPIPE, &sa, &savepipe);
       +        (void)sigaction(SIGQUIT, &sa, &savequit);
       +        (void)sigaction(SIGTERM, &sa, &saveterm);
       +        (void)sigaction(SIGTSTP, &sa, &savetstp);
       +        (void)sigaction(SIGTTIN, &sa, &savettin);
       +        (void)sigaction(SIGTTOU, &sa, &savettou);
       +
       +        if (!(flags & RPP_STDIN))
       +                (void)write(output, prompt, strlen(prompt));
       +        end = buf + bufsiz - 1;
       +        p = buf;
       +        while ((nr = read(input, &ch, 1)) == 1 && ch != '\n' && ch != '\r') {
       +                if (p < end) {
       +                        if ((flags & RPP_SEVENBIT))
       +                                ch &= 0x7f;
       +                        if (isalpha((unsigned char)ch)) {
       +                                if ((flags & RPP_FORCELOWER))
       +                                        ch = (char)tolower((unsigned char)ch);
       +                                if ((flags & RPP_FORCEUPPER))
       +                                        ch = (char)toupper((unsigned char)ch);
       +                        }
       +                        *p++ = ch;
       +                }
       +        }
       +        *p = '\0';
       +        save_errno = errno;
       +        if (!(term.c_lflag & ECHO))
       +                (void)write(output, "\n", 1);
       +
       +        /* Restore old terminal settings and signals. */
       +        if (memcmp(&term, &oterm, sizeof(term)) != 0) {
       +                while (tcsetattr(input, _T_FLUSH, &oterm) == -1 &&
       +                    errno == EINTR && !signo[SIGTTOU])
       +                        continue;
       +        }
       +        (void)sigaction(SIGALRM, &savealrm, NULL);
       +        (void)sigaction(SIGHUP, &savehup, NULL);
       +        (void)sigaction(SIGINT, &saveint, NULL);
       +        (void)sigaction(SIGQUIT, &savequit, NULL);
       +        (void)sigaction(SIGPIPE, &savepipe, NULL);
       +        (void)sigaction(SIGTERM, &saveterm, NULL);
       +        (void)sigaction(SIGTSTP, &savetstp, NULL);
       +        (void)sigaction(SIGTTIN, &savettin, NULL);
       +        (void)sigaction(SIGTTOU, &savettou, NULL);
       +        if (input != STDIN_FILENO)
       +                (void)close(input);
       +
       +        /*
       +         * If we were interrupted by a signal, resend it to ourselves
       +         * now that we have restored the signal handlers.
       +         */
       +        for (i = 0; i < _NSIG; i++) {
       +                if (signo[i]) {
       +                        kill(getpid(), i);
       +                        switch (i) {
       +                        case SIGTSTP:
       +                        case SIGTTIN:
       +                        case SIGTTOU:
       +                                need_restart = 1;
       +                        }
       +                }
       +        }
       +        if (need_restart)
       +                goto restart;
       +
       +        if (save_errno)
       +                errno = save_errno;
       +        return(nr == -1 ? NULL : buf);
       +}
       +
       +static void handler(int s)
       +{
       +        signo[s] = 1;
       +}
 (DIR) diff --git a/readpassphrase.h b/readpassphrase.h
       t@@ -0,0 +1,31 @@
       +/*        $OpenBSD: readpassphrase.h,v 1.5 2003/06/17 21:56:23 millert Exp $        */
       +
       +/*
       + * Copyright (c) 2000, 2002 Todd C. Miller <Todd.Miller@courtesan.com>
       + *
       + * Permission to use, copy, modify, and distribute this software for any
       + * purpose with or without fee is hereby granted, provided that the above
       + * copyright notice and this permission notice appear in all copies.
       + *
       + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
       + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
       + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
       + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
       + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
       + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
       + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
       + *
       + * Sponsored in part by the Defense Advanced Research Projects
       + * Agency (DARPA) and Air Force Research Laboratory, Air Force
       + * Materiel Command, USAF, under agreement number F39502-99-1-0512.
       + */
       +
       +#define RPP_ECHO_OFF    0x00                /* Turn off echo (default). */
       +#define RPP_ECHO_ON     0x01                /* Leave echo on. */
       +#define RPP_REQUIRE_TTY 0x02                /* Fail if there is no tty. */
       +#define RPP_FORCELOWER  0x04                /* Force input to lower case. */
       +#define RPP_FORCEUPPER  0x08                /* Force input to upper case. */
       +#define RPP_SEVENBIT    0x10                /* Strip the high bit from input. */
       +#define RPP_STDIN       0x20                /* Read from stdin, not /dev/tty */
       +
       +char *readpassphrase(const char *, char *, size_t, int);
 (DIR) diff --git a/safe.c b/safe.c
       t@@ -13,12 +13,27 @@
        #include <sodium.h>
        
        #include "arg.h"
       +#include "readpassphrase.h"
        
        #define MDSIZE crypto_generichash_BYTES
       -#define SAFE ".safe.d"
       +#define SAFE ".secrets"
       +#define META ".meta"
       +
       +struct secret {
       +        char hex[256];
       +};
       +
       +struct safe {
       +        uint8_t salt[crypto_pwhash_SALTBYTES];
       +        struct secret *secrets;
       +};
        
        char *argv0;
        
       +uint8_t key[crypto_secretstream_xchacha20poly1305_KEYBYTES];
       +uint8_t *passphrase;
       +uint32_t pplen;
       +
        void
        str2bin(char *s, uint8_t *d, size_t size)
        {
       t@@ -80,10 +95,31 @@ xwrite(int fd, const void *buf, size_t nbytes)
        void
        usage(void)
        {
       -        fprintf(stderr, "usage: %s [-h] [-s safe] [-p pass] [[-a] entry]\n", argv0);
       +        fprintf(stderr, "usage: %s [-h] [-s safe] [[-a] entry]\n", argv0);
                exit(1);
        }
        
       +static int
       +readpass(const char *prompt, uint8_t **target, uint32_t *len)
       +{
       +        char pass[BUFSIZ], *p;
       +
       +        p = readpassphrase(prompt, pass, sizeof(pass), RPP_ECHO_OFF);
       +        if (!p)
       +                err(1, "readpassphrase:");
       +
       +        if (p[0] == '\0')
       +                return -1;
       +
       +        *target = realloc(*target, strlen(p)); /* not null-terminated */
       +        if (!*target)
       +                err(1, "realloc:");
       +
       +        memcpy(*target, p, strlen(p));
       +        *len = strlen(p);
       +        return 0;
       +}
       +
        void
        hash(uint8_t *buf, size_t size, uint8_t *md, size_t mdsize)
        {
       t@@ -91,21 +127,17 @@ hash(uint8_t *buf, size_t size, uint8_t *md, size_t mdsize)
        }
        
        void
       -hash_key(char *pass)
       +deriv(char *pw, struct safe *s)
        {
       -        size_t i;
       -        uint8_t md[MDSIZE];
       -        char key[MDSIZE * 2];
       -
       -        hash((uint8_t *)pass, strlen(pass), md, sizeof(md));
       -        bin2str(md, key, MDSIZE);
       -
       -        for (i = 0; i < sizeof(key); i++)
       -                key[i] &= 1;
       +        if (crypto_pwhash(key, sizeof(key), pw, strlen(pw),
       +                        s->salt, crypto_pwhash_OPSLIMIT_INTERACTIVE,
       +                        crypto_pwhash_MEMLIMIT_INTERACTIVE,
       +                        crypto_pwhash_ALG_DEFAULT))
       +                err(1, "crypto_pwhash");
        }
        
        int
       -store_secret(int fd, char *name)
       +store_secret(struct safe *s, int fd, char *name)
        {
                int sfd;
                ssize_t n;
       t@@ -128,7 +160,7 @@ store_secret(int fd, char *name)
        }
        
        int
       -show_secret(int fd, char *name)
       +show_secret(struct safe *s, int fd, char *name)
        {
                int sfd;
                ssize_t n;
       t@@ -151,19 +183,41 @@ show_secret(int fd, char *name)
                return 0;
        }
        
       +void
       +init(struct safe *s)
       +{
       +        struct stat sb;
       +        if (sodium_init() < 0)
       +                err(1, "sodium: failed to initialize library");
       +
       +        if (stat(META, &sb)) {
       +                randombytes_buf(s->salt, sizeof(s->salt));
       +                s->secrets = NULL;
       +        }
       +}
       +
       +void
       +deinit(struct safe *s)
       +{
       +        int fd;
       +
       +        fd = open(META, O_RDWR | O_CREAT | O_EXCL, 0600);
       +        write(fd, s, sizeof(*s));
       +        fsync(fd);
       +        close(fd);
       +}
       +
        int
        main(int argc, char *argv[])
        {
                int aflag = 0;
       -        char *secret = NULL, *pass = NULL, *safe = SAFE;
       +        char *secret = NULL, *safe = SAFE;
       +        struct safe s;
        
                ARGBEGIN {
                case 'a':
                        aflag = 1;
                        break;
       -        case 'p':
       -                pass = EARGF(usage());
       -                break;
                case 's':
                        safe = EARGF(usage());
                        break;
       t@@ -174,25 +228,23 @@ main(int argc, char *argv[])
                if (argc != 1)
                        usage();
        
       -
                if (safe) {
                        mkdir(safe, 0700);
                        if (chdir(safe) < 0)
                                err(1, "chdir: %s", safe);
                }
        
       -        if (sodium_init() < 0)
       -                err(1, "sodium: failed to initialize library");
       +        init(&s);
        
       -        if (pass)
       -                hash_key(pass);
       +        readpass("Passphrase:", &passphrase, &pplen);
       +        deriv((char *)passphrase, &s);
        
                secret = argv[0];
        
                if (aflag) {
       -                store_secret(STDIN_FILENO, secret);
       +                store_secret(&s, STDIN_FILENO, secret);
                } else {
       -                show_secret(STDOUT_FILENO, secret);
       +                show_secret(&s, STDOUT_FILENO, secret);
                }
        
                return 0;