Implement reading password from file

This commit is contained in:
Travis Burtrum 2020-01-02 00:42:22 -05:00
parent 355c05219e
commit f75e3871be
3 changed files with 97 additions and 26 deletions

View File

@ -30,8 +30,8 @@ $ pegh -h
usage: pegh [options...] password
-e encrypt input to output, default mode
-d decrypt input to output
-i <filename> file to use for input, default stdin
-o <filename> file to use for output, default stdout
-i <filename> file to use for input, - means stdin, default stdin
-o <filename> file to use for output, - means stdout, default stdout
-a append to -o instead of truncate
-v pegh file format version to output,
either 0 (AES-256-GCM) or 1 (Chacha20-Poly1305),
@ -48,6 +48,7 @@ usage: pegh [options...] password
with scrypt, applies for encryption AND decryption, must
almost linearly scale with -N, if too low operation will fail,
default: 64
-f <filename> read password from file instead of argument, - means stdin
-N <num> scrypt parameter N, only applies for encryption, default 32768
this is rounded up to the next highest power of 2
-r <num> scrypt parameter r, only applies for encryption, default 8

112
pegh.c
View File

@ -318,7 +318,7 @@ int chacha_decrypt(const unsigned char *ciphertext, const size_t ciphertext_len,
}
/* returns 1 on success, 0 on error */
int scrypt_derive_key(char *password, size_t password_len,
int scrypt_derive_key(char *password, const size_t password_len,
uint32_t scrypt_max_mem, uint32_t N,
uint8_t r, uint8_t p, unsigned char *salt, unsigned char *key, FILE *err) {
/* derive key using salt, password, and scrypt parameters */
@ -480,7 +480,7 @@ int chacha_decrypt(const unsigned char *ciphertext, const size_t ciphertext_len,
}
/* returns 1 on success, 0 on error */
int scrypt_derive_key(char *password, size_t password_len,
int scrypt_derive_key(char *password, const size_t password_len,
uint32_t scrypt_max_mem, uint32_t N,
uint8_t r, uint8_t p, unsigned char *salt, unsigned char *key, FILE *err) {
size_t needed_memory;
@ -706,15 +706,13 @@ void write_uint32_big_endian(uint32_t val, unsigned char *buf) {
}
/* returns 1 on success, 0 on failure */
int scrypt_derive_key_stream(const stream_func crypt_stream, const aead_func aead, char *password,
int scrypt_derive_key_stream(const stream_func crypt_stream, const aead_func aead,
char *password, const size_t password_len,
uint32_t scrypt_max_mem, size_t buffer_size,
FILE *in, FILE *out, FILE *err,
uint32_t N, uint8_t r, uint8_t p, unsigned char *salt) {
unsigned char key[KEY_LEN] = {0};
int ret;
size_t password_len;
password_len = strlen(password);
ret = scrypt_derive_key(password, password_len, scrypt_max_mem, N, r, p, salt, key, err);
wipe_memory(password, password_len);
@ -757,7 +755,7 @@ int check_version(uint8_t version, FILE *err) {
}
/* returns 1 on success, 0 on failure */
int pegh_encrypt(char *password,
int pegh_encrypt(char *password, const size_t password_len,
uint32_t scrypt_max_mem, size_t buffer_size,
FILE *in, FILE *out, FILE *err,
uint8_t version,
@ -790,11 +788,13 @@ int pegh_encrypt(char *password,
}
fwrite(salt, 1, SALT_LEN, out);
return scrypt_derive_key_stream(encrypt_stream, version == 0 ? gcm_encrypt : chacha_encrypt, password, scrypt_max_mem, buffer_size, in, out, err, N, r, p, salt);
return scrypt_derive_key_stream(encrypt_stream, version == 0 ? gcm_encrypt : chacha_encrypt,
password, password_len,
scrypt_max_mem, buffer_size, in, out, err, N, r, p, salt);
}
/* returns 1 on success, 0 on failure */
int pegh_decrypt(char *password,
int pegh_decrypt(char *password, const size_t password_len,
uint32_t scrypt_max_mem, size_t max_buffer_size,
FILE *in, FILE *out, FILE *err)
{
@ -838,7 +838,8 @@ int pegh_decrypt(char *password,
}
return scrypt_derive_key_stream(decrypt_stream, version == 0 ? gcm_decrypt : chacha_decrypt,
password, scrypt_max_mem, buffer_size, in, out, err, N, r, p, salt);
password, password_len,
scrypt_max_mem, buffer_size, in, out, err, N, r, p, salt);
}
int help(int exit_code) {
@ -847,8 +848,8 @@ int help(int exit_code) {
usage: pegh [options...] password\n\
-e encrypt input to output, default mode\n\
-d decrypt input to output\n\
-i <filename> file to use for input, default stdin\n\
-o <filename> file to use for output, default stdout\n\
-i <filename> file to use for input, - means stdin, default stdin\n\
-o <filename> file to use for output, - means stdout, default stdout\n\
-a append to -o instead of truncate\n\
-v pegh file format version to output,\n");
fprintf(stderr, "\
@ -868,6 +869,7 @@ usage: pegh [options...] password\n\
almost linearly scale with -N, if too low operation will fail,\n\
default: %d\n", CHUNK_SIZE_MAX_GCM / 1024 / 1024, CHUNK_SIZE_MAX_CHACHA / 1024 / 1024, BUFFER_SIZE_MB, SCRYPT_MAX_MEM / 1024 / 1024);
fprintf(stderr, "\
-f <filename> read password from file instead of argument, - means stdin\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\
-r <num> scrypt parameter r, only applies for encryption, default %d\n\
@ -922,6 +924,16 @@ uint8_t parse_byte_arg(int optind, int argc, char **argv) {
return (uint8_t) tmp;
}
char* parse_filename_arg(int optind, int argc, char **argv) {
if(optind >= argc) {
fprintf(stderr, "Error: %s requires an argument\n", argv[optind - 1]);
help_exit(2);
return 0;
}
/* - means stdin or stdout, we return NULL */
return strlen(argv[optind]) == 1 && argv[optind][0] == '-' ? NULL : argv[optind];
}
uint32_t next_highest_power_of_2(uint32_t v) {
--v;
v |= v >> 1;
@ -932,6 +944,60 @@ uint32_t next_highest_power_of_2(uint32_t v) {
return ++v;
}
/* in_filename NULL means stdin */
char* read_file_fully(const char *in_filename, size_t *file_len) {
char* contents;
size_t read, actual_size = 0, total_size = 1024;
FILE *in = stdin;
if(NULL != in_filename) {
in = fopen(in_filename, "rb");
if(!in) {
fprintf (stderr, "Error: file '%s' cannot be opened for reading\n", in_filename);
return NULL;
}
}
contents = malloc(total_size);
if(!contents) {
fprintf (stderr, "Error: memory cannot be allocated to read file '%s'\n", in_filename);
if(NULL != in_filename) {
fclose(in);
}
return NULL;
}
while ((read = fread(contents + actual_size, 1, 512, in)) > 0) {
actual_size += read;
if ((actual_size + 512) > total_size) {
total_size = total_size * 2;
contents = realloc(contents, total_size);
if(!contents) {
fprintf (stderr, "Error: memory cannot be allocated to read file '%s'\n", in_filename);
if(NULL != in_filename) {
fclose(in);
}
return NULL;
}
}
}
if(NULL != in_filename) {
fclose(in);
}
/* now let's size it down to the minimum size */
contents = realloc(contents, actual_size);
if (!contents) {
fprintf (stderr, "Error: memory cannot be allocated to read file '%s'\n", in_filename);
return NULL;
}
*file_len = actual_size;
return contents;
}
/* 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)
{
@ -939,6 +1005,7 @@ int main(int argc, char **argv)
char *password = NULL;
uint32_t N = SCRYPT_N, scrypt_max_mem = SCRYPT_MAX_MEM, buffer_size = BUFFER_SIZE_MB * 1024 * 1024, scale = 1;
uint8_t r = SCRYPT_R, p = SCRYPT_P;
size_t password_len;
FILE *in = stdin, *out = stdout, *err = stderr;
char *in_filename = NULL, *out_filename = NULL;
@ -970,18 +1037,13 @@ int main(int argc, char **argv)
append = 1;
break;
case 'i':
if(++optind >= argc) {
fprintf(stderr, "Error: %s requires an argument\n", argv[optind - 1]);
return help(2);
}
in_filename = argv[optind];
in_filename = parse_filename_arg(++optind, argc, argv);
break;
case 'o':
if(++optind >= argc) {
fprintf(stderr, "Error: %s requires an argument\n", argv[optind - 1]);
return help(2);
}
out_filename = argv[optind];
out_filename = parse_filename_arg(++optind, argc, argv);
break;
case 'f':
password = read_file_fully(parse_filename_arg(++optind, argc, argv), &password_len);
break;
case 'c':
buffer_size = parse_int_arg(++optind, argc, argv) * 1024 * 1024;
@ -1035,6 +1097,7 @@ int main(int argc, char **argv)
}
} else if (password == NULL) {
password = argv[optind];
password_len = strlen(password);
} else {
fprintf (stderr, "Error: more than one password provided\n");
return help(exit_code);
@ -1052,6 +1115,7 @@ int main(int argc, char **argv)
return help(exit_code);
}
password = argv[optind];
password_len = strlen(password);
}
/* apply scale */
@ -1082,7 +1146,7 @@ int main(int argc, char **argv)
}
if(decrypt)
exit_code = pegh_decrypt(password, scrypt_max_mem, buffer_size, in, out, err);
exit_code = pegh_decrypt(password, password_len, scrypt_max_mem, buffer_size, in, out, err);
else {
if(-1 == version) {
/* they left this as default, so attempt to pick best (fastest) version */
@ -1106,7 +1170,7 @@ int main(int argc, char **argv)
#endif
#endif /* PEGH_OPENSSL */
}
exit_code = pegh_encrypt(password, scrypt_max_mem, buffer_size, in, out, err, (uint8_t) version, N, r, p);
exit_code = pegh_encrypt(password, password_len, scrypt_max_mem, buffer_size, in, out, err, (uint8_t) version, N, r, p);
}
if(NULL != in_filename) {

View File

@ -62,11 +62,17 @@ test () {
# can send -s 32 or -m 2048 to decrypt command with identical effect
"$bin" -e "$@" "$key" -s 32 < "$dummy_file" | "$bin_decrypt" -d "$key" -m 2048 | cmp - "$dummy_file"
echo 'encrypting/decrypting with key in file should work, even when key has leading 0s and a trailing newline'
"$bin" -e "$@" -f <(cat <(dd if=/dev/zero bs=1M count=1) <(echo "$key")) < "$dummy_file" | "$bin_decrypt" -d -f <(cat <(dd if=/dev/zero bs=1M count=1) <(echo "$key")) | cmp - "$dummy_file"
set +e
# these should fail
echo 'encrypting with one key and decrypting with another should fail'
"$bin" -e "$@" "$key" -i "$dummy_file" | "$bin_decrypt" -d "$key-wrongkey" | cmp - "$dummy_file" && echo "ERROR: appending -wrongkey to key somehow still worked" && exit 1
echo 'encrypting/decrypting with key in file where last byte is different should fail'
"$bin" -e "$@" -f <(cat <(dd if=/dev/zero bs=1M count=1) <(echo "$key") <(echo -n a)) < "$dummy_file" | "$bin_decrypt" -d -f <(cat <(dd if=/dev/zero bs=1M count=1) <(echo "$key") <(echo -n b)) | cmp - "$dummy_file" && echo "ERROR: differing last byte in password file somehow still worked" && exit 1
echo 'large values of N without enough memory should fail'
"$bin" -e "$@" "$key" -N 2000000 -i "$dummy_file" >/dev/null && echo "ERROR: N of 2 million without extra memory worked" && exit 1
"$bin_decrypt" -d "$key" -N 2000000 -i "$dummy_file" >/dev/null && echo "ERROR: N of 2 million without extra memory worked" && exit 1