Add -g to support prompting for password on the console
This commit is contained in:
parent
80920cd226
commit
e894bbc160
5
Makefile
5
Makefile
@ -8,6 +8,11 @@ CFLAGS += -Wall -Wextra -Werror -std=c89 -pedantic \
|
|||||||
-Wno-missing-prototypes -Wno-missing-noreturn -Wno-format \
|
-Wno-missing-prototypes -Wno-missing-noreturn -Wno-format \
|
||||||
-O3
|
-O3
|
||||||
|
|
||||||
|
# for now we want termios used by default unless explicitly disabled
|
||||||
|
ifndef NO_TERMIOS
|
||||||
|
CFLAGS += -D_POSIX_SOURCE
|
||||||
|
endif
|
||||||
|
|
||||||
# build or grab from https://curl.haxx.se/windows/
|
# build or grab from https://curl.haxx.se/windows/
|
||||||
ifdef PEGH_OPENSSL_WIN
|
ifdef PEGH_OPENSSL_WIN
|
||||||
|
|
||||||
|
@ -78,6 +78,7 @@ usage: pegh [options...] password
|
|||||||
almost linearly scale with -N, if too low operation will fail,
|
almost linearly scale with -N, if too low operation will fail,
|
||||||
default: 64
|
default: 64
|
||||||
-f <filename> read password from file instead of argument, - means stdin
|
-f <filename> read password from file instead of argument, - means stdin
|
||||||
|
-g prompt for password, confirm on encryption, max characters: 64
|
||||||
-N <num> scrypt parameter N, only applies for encryption, default 32768
|
-N <num> scrypt parameter N, only applies for encryption, default 32768
|
||||||
this is rounded up to the next highest power of 2
|
this is rounded up to the next highest power of 2
|
||||||
-r <num> scrypt parameter r, only applies for encryption, default 8
|
-r <num> scrypt parameter r, only applies for encryption, default 8
|
||||||
|
199
pegh.c
199
pegh.c
@ -25,6 +25,14 @@
|
|||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
|
|
||||||
|
#if !defined(_WIN32) && (_POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE)
|
||||||
|
#define USE_TERMIOS 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifdef USE_TERMIOS
|
||||||
|
#include <termios.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
|
|
||||||
#include <windows.h>
|
#include <windows.h>
|
||||||
@ -75,6 +83,8 @@ const uint32_t DEFAULT_CHUNK_SIZE_MB = 32;
|
|||||||
* to allow old archives to be decrypted with shorter passwords
|
* to allow old archives to be decrypted with shorter passwords
|
||||||
*/
|
*/
|
||||||
const size_t MINIMUM_PASSWORD_LEN = 12;
|
const size_t MINIMUM_PASSWORD_LEN = 12;
|
||||||
|
/* technically they can only enter at most 2 less than this */
|
||||||
|
const size_t MANUAL_ENTRY_PASSWORD_MAX_LEN = 66;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* pegh file format, numbers are inclusive 0-based byte array indices
|
* pegh file format, numbers are inclusive 0-based byte array indices
|
||||||
@ -975,6 +985,10 @@ usage: pegh [options...] password\n\
|
|||||||
default: %d\n", CHUNK_SIZE_MAX_GCM / 1024 / 1024, CHUNK_SIZE_MAX_CHACHA / 1024 / 1024, DEFAULT_CHUNK_SIZE_MB, SCRYPT_MAX_MEM / 1024 / 1024);
|
default: %d\n", CHUNK_SIZE_MAX_GCM / 1024 / 1024, CHUNK_SIZE_MAX_CHACHA / 1024 / 1024, DEFAULT_CHUNK_SIZE_MB, SCRYPT_MAX_MEM / 1024 / 1024);
|
||||||
fprintf(stderr, "\
|
fprintf(stderr, "\
|
||||||
-f <filename> read password from file instead of argument, - means stdin\n\
|
-f <filename> read password from file instead of argument, - means stdin\n\
|
||||||
|
-g prompt for password, confirm on encryption, max characters: %lu\n",
|
||||||
|
MANUAL_ENTRY_PASSWORD_MAX_LEN - 2
|
||||||
|
);
|
||||||
|
fprintf(stderr, "\
|
||||||
-N <num> scrypt parameter N, only applies for encryption, default %d\n\
|
-N <num> scrypt parameter N, only applies for encryption, default %d\n\
|
||||||
this is rounded up to the next highest power of 2\n\
|
this is rounded up to the next highest power of 2\n\
|
||||||
-r <num> scrypt parameter r, only applies for encryption, default %d\n\
|
-r <num> scrypt parameter r, only applies for encryption, default %d\n\
|
||||||
@ -1062,6 +1076,12 @@ char* read_file_fully(const char *in_filename, size_t *file_len) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#ifdef _WIN32
|
||||||
|
else {
|
||||||
|
/* windows in/out is text and mangles certain bytes by default... */
|
||||||
|
setmode(STDIN, O_BINARY);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
contents = malloc(total_size);
|
contents = malloc(total_size);
|
||||||
if(!contents) {
|
if(!contents) {
|
||||||
@ -1103,10 +1123,143 @@ char* read_file_fully(const char *in_filename, size_t *file_len) {
|
|||||||
return contents;
|
return contents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* returns -1 on error, or 0+ for length of string read into buff */
|
||||||
|
int read_passwd(char *prompt, char *buff, int buff_len, FILE *in, FILE *out)
|
||||||
|
{
|
||||||
|
int pass_len;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Turn echoing off and fail if we can’t.
|
||||||
|
* we support POSIX and Windows here
|
||||||
|
*/
|
||||||
|
#ifdef USE_TERMIOS
|
||||||
|
struct termios orig_input_flags, new_input_flags;
|
||||||
|
|
||||||
|
if (tcgetattr (fileno (in), &orig_input_flags) != 0)
|
||||||
|
return -1;
|
||||||
|
new_input_flags = orig_input_flags;
|
||||||
|
new_input_flags.c_lflag &= (tcflag_t) ~ECHO;
|
||||||
|
if (tcsetattr (fileno (in), TCSAFLUSH, &new_input_flags) != 0)
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
HANDLE hStdin;
|
||||||
|
DWORD orig_input_flags;
|
||||||
|
|
||||||
|
hStdin = GetStdHandle(STD_INPUT_HANDLE);
|
||||||
|
if (hStdin == INVALID_HANDLE_VALUE)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
/* Save the current input mode, to be restored on exit. */
|
||||||
|
if (! GetConsoleMode(hStdin, &orig_input_flags) )
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (! SetConsoleMode(hStdin, orig_input_flags & (DWORD) ~ENABLE_ECHO_INPUT) )
|
||||||
|
return -1;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* Display the prompt */
|
||||||
|
fprintf(out, "%s", prompt);
|
||||||
|
|
||||||
|
/* Read the password. cast is safe because buff_len is int */
|
||||||
|
pass_len = fgets (buff, buff_len, in) == NULL ? 0 : (int) strlen(buff);
|
||||||
|
|
||||||
|
/* Remove the line feed */
|
||||||
|
if (pass_len >= 1 && buff[pass_len - 1] == '\n') {
|
||||||
|
buff[--pass_len] = 0;
|
||||||
|
}
|
||||||
|
/* Remove the carriage return */
|
||||||
|
if (pass_len >= 1 && buff[pass_len - 1] == '\r') {
|
||||||
|
buff[--pass_len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Restore terminal. we could check failure here but if it failed, what could we do? */
|
||||||
|
#ifdef USE_TERMIOS
|
||||||
|
tcsetattr (fileno (in), TCSAFLUSH, &orig_input_flags);
|
||||||
|
fprintf(out, "\n"); /* windows does this automatically */
|
||||||
|
#endif
|
||||||
|
#ifdef _WIN32
|
||||||
|
SetConsoleMode(hStdin, orig_input_flags);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return pass_len;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns -1 on error, or 0+ for length of string read into buff */
|
||||||
|
int read_passwd_console(char *prompt, char *buff, int buff_len) {
|
||||||
|
int pass_len;
|
||||||
|
FILE *in;
|
||||||
|
#ifdef _WIN32
|
||||||
|
in = fopen("CONIN$", "r+");
|
||||||
|
/* this *should* work here but does not, just using stderr for now
|
||||||
|
out = fopen("CONOUT$", "w");
|
||||||
|
*/
|
||||||
|
if(in) {
|
||||||
|
pass_len = read_passwd(prompt, buff, buff_len, in, stderr);
|
||||||
|
fclose(in);
|
||||||
|
return pass_len;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
in = fopen("/dev/tty", "r+");
|
||||||
|
if(in) {
|
||||||
|
pass_len = read_passwd(prompt, buff, buff_len, in, in);
|
||||||
|
fclose(in);
|
||||||
|
return pass_len;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
/* oh well, we tried */
|
||||||
|
return read_passwd(prompt, buff, buff_len, stdin, stderr);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* returns -1 on error, or 0+ for length of string read into buff */
|
||||||
|
int read_passwd_console_confirm(char *buff, int buff_len, int min_passwd_length) {
|
||||||
|
int pass_len, confirm_len;
|
||||||
|
char *confirm_passwd;
|
||||||
|
|
||||||
|
confirm_passwd = malloc((size_t) buff_len);
|
||||||
|
if(!confirm_passwd)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
while(1) {
|
||||||
|
pass_len = read_passwd_console("Enter encryption password: ", buff, buff_len);
|
||||||
|
if(-1 == pass_len)
|
||||||
|
break; /* fail */
|
||||||
|
if(pass_len < min_passwd_length) {
|
||||||
|
fprintf(stderr, "Error: Minimum password length is %d but was only %d, try again...\n", min_passwd_length, pass_len);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if(pass_len == (buff_len - 1)) {
|
||||||
|
fprintf(stderr, "Error: Maximum password length for manual entry (%d) exceeded, try again...\n", buff_len - 2);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
confirm_len = read_passwd_console("Confirm password: ", confirm_passwd, buff_len);
|
||||||
|
if(-1 == confirm_len)
|
||||||
|
break; /* fail */
|
||||||
|
if(pass_len == confirm_len && strcmp(buff, confirm_passwd) == 0)
|
||||||
|
break; /* finally success! */
|
||||||
|
|
||||||
|
fprintf(stderr, "Error: Entered passwords do not match, try again...\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* wipe confirm password */
|
||||||
|
wipe_memory(confirm_passwd, (size_t) buff_len);
|
||||||
|
if(pass_len == -1 || pass_len == 0) {
|
||||||
|
/* wipe entire password buffer */
|
||||||
|
wipe_memory(buff, (size_t) buff_len);
|
||||||
|
} else {
|
||||||
|
/* wipe the unused portion of the read password too, in case some secrets exist there */
|
||||||
|
wipe_memory(buff + (pass_len + 1), (size_t) (buff_len - pass_len - 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
free(confirm_passwd);
|
||||||
|
return pass_len;
|
||||||
|
}
|
||||||
|
|
||||||
/* returns 0 on success, 1 on cryptography failure, 19 on "libsodium only and CPU does not support AES" error, 2 on other failure */
|
/* returns 0 on success, 1 on cryptography failure, 19 on "libsodium only and CPU does not support AES" error, 2 on other failure */
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
int optind, decrypt = 0, append = 0, exit_code = 2, version = -1;
|
int optind, decrypt = 0, append = 0, passwd_prompt = 0, exit_code = 2, version = -1;
|
||||||
char *password = NULL;
|
char *password = NULL;
|
||||||
uint32_t N = SCRYPT_N, scrypt_max_mem = SCRYPT_MAX_MEM, buffer_size = DEFAULT_CHUNK_SIZE_MB * 1024 * 1024, scale = 1;
|
uint32_t N = SCRYPT_N, scrypt_max_mem = SCRYPT_MAX_MEM, buffer_size = DEFAULT_CHUNK_SIZE_MB * 1024 * 1024, scale = 1;
|
||||||
uint8_t r = SCRYPT_R, p = SCRYPT_P;
|
uint8_t r = SCRYPT_R, p = SCRYPT_P;
|
||||||
@ -1150,6 +1303,9 @@ int main(int argc, char **argv)
|
|||||||
case 'f':
|
case 'f':
|
||||||
password = read_file_fully(parse_filename_arg(++optind, argc, argv), &password_len);
|
password = read_file_fully(parse_filename_arg(++optind, argc, argv), &password_len);
|
||||||
break;
|
break;
|
||||||
|
case 'g':
|
||||||
|
passwd_prompt = 1;
|
||||||
|
break;
|
||||||
case 'c':
|
case 'c':
|
||||||
buffer_size = parse_int_arg(++optind, argc, argv) * 1024 * 1024;
|
buffer_size = parse_int_arg(++optind, argc, argv) * 1024 * 1024;
|
||||||
break;
|
break;
|
||||||
@ -1210,17 +1366,40 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(password == NULL) {
|
if(password == NULL) {
|
||||||
if(argc == optind) {
|
if(passwd_prompt) {
|
||||||
fprintf (stderr, "Error: no password provided\n");
|
password = malloc(MANUAL_ENTRY_PASSWORD_MAX_LEN);
|
||||||
return help(exit_code);
|
if(!password) {
|
||||||
}
|
fprintf (stderr, "Error: cannot allocate memory to prompt for password\n");
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
if(decrypt) {
|
||||||
|
/* for decryption, we just read the password once, maybe print a warning, try to decrypt */
|
||||||
|
optind = read_passwd_console("Enter decryption password: ", password, MANUAL_ENTRY_PASSWORD_MAX_LEN);
|
||||||
|
if(optind == (MANUAL_ENTRY_PASSWORD_MAX_LEN - 1)) {
|
||||||
|
/* possibly truncated */
|
||||||
|
fprintf (stderr, "Warning: read maximum characters (%d) for password, possibly truncated, attempting decryption anyway...!\n", optind);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
optind = read_passwd_console_confirm(password, MANUAL_ENTRY_PASSWORD_MAX_LEN, MINIMUM_PASSWORD_LEN);
|
||||||
|
}
|
||||||
|
if(optind == -1) {
|
||||||
|
fprintf (stderr, "Error: cannot prompt user for password!\n");
|
||||||
|
return exit_code;
|
||||||
|
}
|
||||||
|
password_len = (size_t) optind;
|
||||||
|
} else {
|
||||||
|
if(argc == optind) {
|
||||||
|
fprintf (stderr, "Error: no password provided\n");
|
||||||
|
return help(exit_code);
|
||||||
|
}
|
||||||
|
|
||||||
if((argc - optind) != 1) {
|
if((argc - optind) != 1) {
|
||||||
fprintf (stderr, "Error: more than one password provided\n");
|
fprintf (stderr, "Error: more than one password provided\n");
|
||||||
return help(exit_code);
|
return help(exit_code);
|
||||||
|
}
|
||||||
|
password = argv[optind];
|
||||||
|
password_len = strlen(password);
|
||||||
}
|
}
|
||||||
password = argv[optind];
|
|
||||||
password_len = strlen(password);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(0 == decrypt && password_len < MINIMUM_PASSWORD_LEN) {
|
if(0 == decrypt && password_len < MINIMUM_PASSWORD_LEN) {
|
||||||
|
Loading…
Reference in New Issue
Block a user