mirror of https://github.com/skeeto/enchive.git
Add support for pinentry protocol
This feature is now used by Emacs so that Emacs doesn't need to handle the passphrase itself.pull/10/head
parent
e9a50022c0
commit
a407afcdaf
|
@ -258,6 +258,14 @@ time with an optional argument to `--agent`.
|
||||||
Whether or not to enable the agent by default. This can be explicitly
|
Whether or not to enable the agent by default. This can be explicitly
|
||||||
overridden at run time with `--agent` and `--no-agent`.
|
overridden at run time with `--agent` and `--no-agent`.
|
||||||
|
|
||||||
|
#### `ENCHIVE_PINENTRY_DEFAULT`
|
||||||
|
|
||||||
|
The default program to use for `pinentry`.
|
||||||
|
|
||||||
|
#### `ENCHIVE_PINENTRY_DEFAULT_ENABLED`
|
||||||
|
|
||||||
|
Whether or not to use `pinentry` by default when reading passphrases.
|
||||||
|
|
||||||
#### `ENCHIVE_KEY_DERIVE_ITERATIONS`
|
#### `ENCHIVE_KEY_DERIVE_ITERATIONS`
|
||||||
|
|
||||||
Power-of-two exponent for protection key derivation. Can be configured
|
Power-of-two exponent for protection key derivation. Can be configured
|
||||||
|
|
8
config.h
8
config.h
|
@ -35,6 +35,14 @@
|
||||||
# define ENCHIVE_AGENT_DEFAULT_ENABLED 0
|
# define ENCHIVE_AGENT_DEFAULT_ENABLED 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENCHIVE_PINENTRY_DEFAULT
|
||||||
|
# define ENCHIVE_PINENTRY_DEFAULT pinentry
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef ENCHIVE_PINENTRY_DEFAULT_ENABLED
|
||||||
|
# define ENCHIVE_PINENTRY_DEFAULT_ENABLED 0
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifndef ENCHIVE_PASSPHRASE_MAX
|
#ifndef ENCHIVE_PASSPHRASE_MAX
|
||||||
# define ENCHIVE_PASSPHRASE_MAX 1024
|
# define ENCHIVE_PASSPHRASE_MAX 1024
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -4,12 +4,8 @@
|
||||||
|
|
||||||
;;; Commentary:
|
;;; Commentary:
|
||||||
|
|
||||||
;; Load this file, then M-x `enchive-mode' to enable automatic
|
;; Load this file, then M-x `enchive-mode' (global minor mode) to
|
||||||
;; encryption and decryption of Enchive files.
|
;; enable automatic encryption and decryption of Enchive files.
|
||||||
|
|
||||||
;; The agent *must* be started before any encrypted files are opened
|
|
||||||
;; because this mode doesn't (yet) know how to prompt for a
|
|
||||||
;; passphrase.
|
|
||||||
|
|
||||||
;;; Code:
|
;;; Code:
|
||||||
|
|
||||||
|
@ -28,7 +24,8 @@
|
||||||
(let ((file-name-handler-alist ()))
|
(let ((file-name-handler-alist ()))
|
||||||
(cond ((eq operation 'insert-file-contents)
|
(cond ((eq operation 'insert-file-contents)
|
||||||
(let ((file (car args)))
|
(let ((file (car args)))
|
||||||
(unless (= 0 (call-process "enchive" file t "--agent" "extract"))
|
(unless (= 0 (call-process "enchive" file t nil
|
||||||
|
"--pinentry" "extract"))
|
||||||
(error "Enchive subprocess failed"))
|
(error "Enchive subprocess failed"))
|
||||||
(setf buffer-file-name file)
|
(setf buffer-file-name file)
|
||||||
(list file (buffer-size))))
|
(list file (buffer-size))))
|
||||||
|
|
|
@ -40,7 +40,7 @@ Like GnuPG, you can safely encrypt files on systems that you don't trust with yo
|
||||||
Files are secured with ChaCha20, Curve25519, and HMAC-SHA256.
|
Files are secured with ChaCha20, Curve25519, and HMAC-SHA256.
|
||||||
.SH OPTIONS
|
.SH OPTIONS
|
||||||
.TP
|
.TP
|
||||||
\fB\-a\fR \fIseconds\fR, \fB\-\-agent\fR[=\fIseconds\fR]
|
\fB\-a\fR\fIseconds\fR, \fB\-\-agent\fR[=\fIseconds\fR]
|
||||||
Runs the key agent for awhile after successfully reading the passphrase.
|
Runs the key agent for awhile after successfully reading the passphrase.
|
||||||
The agent will remain resident in memory until a period of inactivity passes.
|
The agent will remain resident in memory until a period of inactivity passes.
|
||||||
Default is 900 seconds (15 minutes).
|
Default is 900 seconds (15 minutes).
|
||||||
|
@ -48,6 +48,10 @@ Default is 900 seconds (15 minutes).
|
||||||
\fB\-A\fB, \fB\-\-no\-agent\fR
|
\fB\-A\fB, \fB\-\-no\-agent\fR
|
||||||
Do not start the key agent (default).
|
Do not start the key agent (default).
|
||||||
.TP
|
.TP
|
||||||
|
\fB\-e\fR\fIprogram\fR, \fB\-\-pinentry\fR[=\fIprogram\fR]
|
||||||
|
Read passphrases using the system's pinentry program. By default
|
||||||
|
Enchive uses the program named "pinentry".
|
||||||
|
.TP
|
||||||
\fB\-p, \-\-pubkey\fR \fIfile\fR
|
\fB\-p, \-\-pubkey\fR \fIfile\fR
|
||||||
Specifies the public key file to use for encryption.
|
Specifies the public key file to use for encryption.
|
||||||
.TP
|
.TP
|
||||||
|
|
|
@ -24,6 +24,12 @@ static const char *docs_usage[] = {
|
||||||
" (default)",
|
" (default)",
|
||||||
# endif
|
# endif
|
||||||
#endif
|
#endif
|
||||||
|
" -e, --pinentry[=program] use pinentry to read passphrases"
|
||||||
|
#if ENCHIVE_PINENTRY_DEFAULT_ENABLED
|
||||||
|
" (default)",
|
||||||
|
#else
|
||||||
|
"",
|
||||||
|
#endif
|
||||||
" --version display version information",
|
" --version display version information",
|
||||||
" --help display this usage information",
|
" --help display this usage information",
|
||||||
"",
|
"",
|
||||||
|
|
112
src/enchive.c
112
src/enchive.c
|
@ -22,6 +22,12 @@ static int global_agent_timeout = ENCHIVE_AGENT_TIMEOUT;
|
||||||
static int global_agent_timeout = 0;
|
static int global_agent_timeout = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if ENCHIVE_PINENTRY_DEFAULT_ENABLED
|
||||||
|
static char *pinentry_path = STR(ENCHIVE_PINENTRY_DEFAULT);
|
||||||
|
#else
|
||||||
|
static char *pinentry_path = 0;
|
||||||
|
#endif
|
||||||
|
|
||||||
static const char enchive_suffix[] = ".enchive";
|
static const char enchive_suffix[] = ".enchive";
|
||||||
|
|
||||||
static struct {
|
static struct {
|
||||||
|
@ -432,10 +438,107 @@ get_passphrase_dumb(char *buf, size_t len, char *prompt)
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
pinentry_decode(char *buf, size_t blen, const char *str)
|
||||||
|
{
|
||||||
|
size_t i, j;
|
||||||
|
|
||||||
|
for (i = 0, j = 0; str[i] && str[i] != '\n' && j < blen - 1; i++) {
|
||||||
|
int c = str[i];
|
||||||
|
if (c == '%') {
|
||||||
|
static const char *hex = "0123456789ABCDEF";
|
||||||
|
char *nibh, *nibl;
|
||||||
|
if (!str[i + 1] || !str[i + 2])
|
||||||
|
fatal("invalid data from pinentry");
|
||||||
|
nibh = memchr(hex, str[i + 1], 16);
|
||||||
|
nibl = memchr(hex, str[i + 2], 16);
|
||||||
|
if (!nibh || !nibl)
|
||||||
|
fatal("invalid data from pinentry");
|
||||||
|
buf[j++] = (nibh - hex) * 16 + (nibl - hex);
|
||||||
|
i += 2;
|
||||||
|
} else {
|
||||||
|
buf[j++] = c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
buf[j] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
invoke_pinentry(char *buf, size_t len, char *prompt)
|
||||||
|
{
|
||||||
|
int pin[2];
|
||||||
|
int pout[2];
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
if (pipe(pin) != 0)
|
||||||
|
fatal("could not start pinentry -- %s", strerror(errno));
|
||||||
|
if (pipe(pout) != 0)
|
||||||
|
fatal("could not start pinentry -- %s", strerror(errno));
|
||||||
|
|
||||||
|
pid = fork();
|
||||||
|
if (pid == -1)
|
||||||
|
fatal("pinentry fork() failed -- %s", strerror(errno));
|
||||||
|
if (pid) {
|
||||||
|
FILE *pfi, *pfo;
|
||||||
|
char line[ENCHIVE_PASSPHRASE_MAX * 3 + 32];
|
||||||
|
|
||||||
|
close(pin[0]);
|
||||||
|
close(pout[1]);
|
||||||
|
|
||||||
|
if (!(pfi = fdopen(pin[1], "w")))
|
||||||
|
fatal("fdopen() input -- %s", strerror(errno));
|
||||||
|
if (!(pfo = fdopen(pout[0], "r")))
|
||||||
|
fatal("fdopen() output -- %s", strerror(errno));
|
||||||
|
|
||||||
|
if (!fgets(line, sizeof(line), pfo))
|
||||||
|
/* Likely caused by exec() failure, so exit quietly. */
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
if (strncmp(line, "OK", 2) != 0)
|
||||||
|
fatal("pinentry startup failure");
|
||||||
|
|
||||||
|
if (fprintf(pfi, "SETPROMPT %s\n", prompt) < 0 || fflush(pfi) < 0)
|
||||||
|
fatal("pinentry write() -- %s", strerror(errno));
|
||||||
|
|
||||||
|
if (!fgets(line, sizeof(line), pfo))
|
||||||
|
fatal("pinentry read() -- %s", strerror(errno));
|
||||||
|
if (strncmp(line, "OK", 2) != 0)
|
||||||
|
fatal("pinentry protocol failure");
|
||||||
|
|
||||||
|
if (fprintf(pfi, "GETPIN\n") < 0 || fflush(pfi) < 0)
|
||||||
|
fatal("pinentry write() -- %s", strerror(errno));
|
||||||
|
|
||||||
|
if (!fgets(line, sizeof(line), pfo))
|
||||||
|
fatal("pinentry read() -- %s", strerror(errno));
|
||||||
|
if (strncmp(line, "ERR ", 4) == 0)
|
||||||
|
fatal("passphrase entry canceled");
|
||||||
|
else if (strncmp(line, "OK", 2) == 0)
|
||||||
|
buf[0] = 0;
|
||||||
|
else if (strncmp(line, "D ", 2) == 0)
|
||||||
|
pinentry_decode(buf, len, line + 2);
|
||||||
|
else
|
||||||
|
fatal("pinentry protocol failure");
|
||||||
|
} else {
|
||||||
|
close(pin[1]);
|
||||||
|
close(pout[0]);
|
||||||
|
dup2(pin[0], STDIN_FILENO);
|
||||||
|
dup2(pout[1], STDOUT_FILENO);
|
||||||
|
if (execlp(pinentry_path, pinentry_path, (char *)0))
|
||||||
|
fatal("exec(\"%s\") failed -- %s",
|
||||||
|
pinentry_path, strerror(errno));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
get_passphrase(char *buf, size_t len, char *prompt)
|
get_passphrase(char *buf, size_t len, char *prompt)
|
||||||
{
|
{
|
||||||
int tty = open("/dev/tty", O_RDWR);
|
int tty;
|
||||||
|
|
||||||
|
if (pinentry_path) {
|
||||||
|
invoke_pinentry(buf, len, prompt);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
tty = open("/dev/tty", O_RDWR);
|
||||||
if (tty == -1) {
|
if (tty == -1) {
|
||||||
get_passphrase_dumb(buf, len, prompt);
|
get_passphrase_dumb(buf, len, prompt);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1401,6 +1504,7 @@ main(int argc, char **argv)
|
||||||
{"agent", 'a', OPTPARSE_OPTIONAL},
|
{"agent", 'a', OPTPARSE_OPTIONAL},
|
||||||
{"no-agent", 'A', OPTPARSE_NONE},
|
{"no-agent", 'A', OPTPARSE_NONE},
|
||||||
#endif
|
#endif
|
||||||
|
{"pinentry", 'e', OPTPARSE_OPTIONAL},
|
||||||
{"pubkey", 'p', OPTPARSE_REQUIRED},
|
{"pubkey", 'p', OPTPARSE_REQUIRED},
|
||||||
{"seckey", 's', OPTPARSE_REQUIRED},
|
{"seckey", 's', OPTPARSE_REQUIRED},
|
||||||
{"version", 'V', OPTPARSE_NONE},
|
{"version", 'V', OPTPARSE_NONE},
|
||||||
|
@ -1433,6 +1537,12 @@ main(int argc, char **argv)
|
||||||
global_agent_timeout = 0;
|
global_agent_timeout = 0;
|
||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
|
case 'e':
|
||||||
|
if (options->optarg)
|
||||||
|
pinentry_path = options->optarg;
|
||||||
|
else
|
||||||
|
pinentry_path = STR(ENCHIVE_PINENTRY_DEFAULT);
|
||||||
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
global_pubkey = options->optarg;
|
global_pubkey = options->optarg;
|
||||||
break;
|
break;
|
||||||
|
|
Loading…
Reference in New Issue