#if defined(HAVE_CONFIG_H)
#include <config.h>
#endif
#include <stdio.h>
#include <string.h>
#if defined(HAVE_GETOPT_H)
#include <getopt.h>
#endif
#if defined(HAVE_ISATTY)
#include <unistd.h>
#endif
#include "fileops.h"
#include "readpass.h"
#define ASCON_PBKDF2_ROUNDS 8192
#define ASCON_BUFSIZ BUFSIZ
#define ASCON_PWSIZ 1024
#define ASCON_MAX_FILE_SIZE (1024ULL * 1024ULL * 1024ULL * 1024ULL)
#define MODE_DECRYPT 0
#define MODE_ENCRYPT 1
#define MODE_DETECT 2
#define MODE_GENERATE 3
static char full_password[ASCON_PWSIZ];
static char confirm_password[ASCON_PWSIZ];
static char temp_filename[ASCON_BUFSIZ];
static void usage(const char *progname);
static void password_error(const char *filename);
static int generate_password(const char *keyfile);
static int read_keyfile(const char *keyfile);
static int is_encrypted_filename(const char *filename);
static const char *strip_suffix(const char *filename);
static const char *add_suffix(const char *filename, const char *suffix);
static int encrypt_file(const char *infilename, const char *outfilename);
static int decrypt_file(const char *infilename, const char *outfilename);
int main(int argc, char *argv[])
{
const char *progname = argv[0];
int opt_mode = MODE_DETECT;
const char *opt_password = NULL;
const char *opt_keyfile = NULL;
const char *opt_output = NULL;
int opt, posn;
int exit_val = 0;
#if defined(HAVE_GETOPT)
while ((opt = getopt(argc, argv, "edp:k:o:g:")) != -1) {
switch (opt) {
case 'e': opt_mode = MODE_ENCRYPT; break;
case 'd': opt_mode = MODE_DECRYPT; break;
case 'p': opt_password = optarg; break;
case 'k': opt_keyfile = optarg; break;
case 'o': opt_output = optarg; break;
case 'g':
opt_mode = MODE_GENERATE;
opt_keyfile = optarg;
break;
default:
usage(progname);
return 1;
}
}
#else
#define GET_OPTARG(var) \
do { \
(var) = NULL; \
if (opts[0] == '\0') { \
if ((optind + 1) < argc) { \
(var) = argv[optind + 1]; \
++optind; \
} else { \
error = 1; \
} \
} else { \
(var) = opts; \
opts = ""; \
} \
} while (0)
int optind = 1;
while (optind < argc && argv[optind][0] == '-' &&
argv[optind][1] != '\0') {
const char *opts = argv[optind] + 1;
while ((opt = *opts++) != '\0') {
int error = 0;
switch (opt) {
case 'e': opt_mode = MODE_ENCRYPT; break;
case 'd': opt_mode = MODE_DECRYPT; break;
case 'p': GET_OPTARG(opt_password); break;
case 'k': GET_OPTARG(opt_keyfile); break;
case 'o': GET_OPTARG(opt_output); break;
case 'g':
opt_mode = MODE_GENERATE;
GET_OPTARG(opt_keyfile);
break;
default: error = 1; break;
}
if (error) {
usage(progname);
return 1;
}
}
++optind;
}
#endif
if ((opt_mode == MODE_GENERATE && optind < argc) ||
(opt_mode != MODE_GENERATE && optind >= argc)) {
usage(progname);
return 1;
}
if (opt_mode != MODE_GENERATE) {
if (opt_password && opt_keyfile) {
fprintf(stderr, "%s: cannot specify both -p and -k\n", progname);
return 1;
}
if (opt_output && optind < (argc - 1)) {
fprintf(stderr, "%s: only one input file allowed with -o\n", progname);
return 1;
}
}
if (opt_mode == MODE_GENERATE) {
if (!generate_password(opt_keyfile))
return 1;
return 0;
}
if (opt_mode == MODE_DETECT) {
int mixture = 0;
for (posn = optind; posn < argc; ++posn) {
if (is_encrypted_filename(argv[posn])) {
if (opt_mode == MODE_ENCRYPT)
mixture = 1;
else
opt_mode = MODE_DECRYPT;
} else {
if (opt_mode == MODE_DECRYPT)
mixture = 1;
else
opt_mode = MODE_ENCRYPT;
}
}
if (mixture) {
fprintf(stderr,
"%s: cannot determine direction; specify -e or -d\n",
progname);
return 1;
}
}
if (opt_password) {
if (strlen(opt_password) >= sizeof(full_password)) {
password_error(progname);
return 1;
}
strncpy(full_password, opt_password, sizeof(full_password));
full_password[sizeof(full_password) - 1] = '\0';
} else if (opt_keyfile) {
if (!read_keyfile(opt_keyfile)) {
return 1;
}
} else {
int have_password;
#if defined(HAVE_ISATTY)
if (!isatty(0) || !isatty(1)) {
fprintf(stderr, "%s: cannot prompt for a password without a terminal\n",
progname);
return 1;
}
#endif
have_password = 0;
if (opt_mode == MODE_DECRYPT) {
have_password = read_password
("Password: ", full_password, sizeof(full_password));
} else {
have_password = read_password
("Password: ", full_password, sizeof(full_password));
if (have_password) {
have_password = read_password
("Confirm Password: ", confirm_password,
sizeof(confirm_password));
if (!have_password || strcmp(full_password, confirm_password) != 0) {
fprintf(stderr, "%s: passwords do not match\n", progname);
ascon_clean(confirm_password,
sizeof(confirm_password));
return 1;
}
}
}
if (!have_password) {
ascon_clean(confirm_password,
sizeof(confirm_password));
usage(progname);
return 1;
}
}
for (posn = optind; posn < argc; ++posn) {
const char *filename;
if (!strcmp(argv[posn], "-") && !opt_output) {
opt_output = "-";
}
if (opt_mode == MODE_DECRYPT) {
if (opt_output)
filename = opt_output;
else if (is_encrypted_filename(argv[posn]))
filename = strip_suffix(argv[posn]);
else
filename = add_suffix(argv[posn], ".decrypted");
if (!decrypt_file(argv[posn], filename))
exit_val = 1;
} else {
if (opt_output)
filename = opt_output;
else
filename = add_suffix(argv[posn], ".ascon");
if (!encrypt_file(argv[posn], filename))
exit_val = 1;
}
opt_output = NULL;
}
ascon_clean(confirm_password,
sizeof(confirm_password));
return exit_val;
}
static void usage(const char *progname)
{
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [-e|-d] [-p PASSWORD | -k KEYFILE] [-o OUTPUT] INPUT ...\n", progname);
fprintf(stderr, " or: %s -g KEYFILE\n", progname);
fprintf(stderr, "\n");
fprintf(stderr, "-e Encrypt the file(s)\n");
fprintf(stderr, "-d Decrypt the file(s)\n");
fprintf(stderr, "-p PASSWORD Provides the password on the command-line.\n");
fprintf(stderr, "-k KEYFILE Provides the password via a key file.\n");
fprintf(stderr, "-o OUTPUT Output file to write to.\n");
fprintf(stderr, "-g KEYFILE Generate a random password and write it to KEYFILE.\n");
fprintf(stderr, "\n");
}
static void password_error(const char *filename)
{
fprintf(stderr, "%s: password is too long, maximum is %d bytes\n",
filename, ASCON_PWSIZ - 1);
}
#define DEFAULT_PASSWORD_LENGTH 40
static int generate_password(const char *keyfile)
{
static char const pw_chars[] =
"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ%$";
unsigned char password[DEFAULT_PASSWORD_LENGTH];
unsigned posn;
SAFEFILE file;
if (!safe_file_open_write(&file, keyfile))
return 0;
fprintf(stderr, "FATAL: system random number generator does not appear to be working!\n");
safe_file_delete(&file);
return 0;
}
for (posn = 0; posn < sizeof(password); ++posn)
password[posn] = (unsigned char)pw_chars[password[posn] & 0x3F];
safe_file_write(&file, password, sizeof(password));
safe_file_write(&file, "\n", 1);
safe_file_close(&file);
return 1;
}
static int read_keyfile(const char *keyfile)
{
SAFEFILE file;
int len, posn;
if (!safe_file_open_read(&file, keyfile))
return 0;
len = safe_file_read(&file, full_password, sizeof(full_password));
safe_file_close(&file);
if (len < 0)
return 0;
posn = 0;
while (posn < len && full_password[posn] != '\n' &&
full_password[posn] != '\r') {
if (full_password[posn] == '\0') {
fprintf(stderr, "%s: password value contains a NUL\n", keyfile);
return 0;
}
++posn;
}
if (posn >= len && len >= (int)sizeof(full_password)) {
password_error(keyfile);
return 0;
}
full_password[posn] = '\0';
return 1;
}
static int is_encrypted_filename(const char *filename)
{
size_t len = strlen(filename);
if (len >= 6)
return !strncmp(filename + len - 6, ".ascon", 6);
else
return 1;
}
static const char *strip_suffix(const char *filename)
{
size_t len = strlen(filename) - 6;
if (len >= sizeof(temp_filename))
len = sizeof(temp_filename);
memcpy(temp_filename, filename, len);
temp_filename[len] = '\0';
return temp_filename;
}
static const char *add_suffix(const char *filename, const char *suffix)
{
snprintf(temp_filename, sizeof(temp_filename), "%s%s", filename, suffix);
return temp_filename;
}
struct asconcrypt_header
{
char magic[10];
unsigned char version[2];
unsigned char salt[16];
};
struct asconcrypt_siv_block
{
unsigned char key[20];
unsigned char nonce[16];
unsigned char tag[16];
};
struct asconcrypt_full_header
{
struct asconcrypt_header header;
struct asconcrypt_siv_block siv;
};
static int encrypt_file(const char *infilename, const char *outfilename)
{
struct asconcrypt_header header;
struct asconcrypt_siv_block siv;
struct asconcrypt_siv_block siv_copy;
unsigned char kn[20 + 16];
int exit_val = 0;
size_t clen = 0;
SAFEFILE input;
SAFEFILE output;
unsigned char data[ASCON_BUFSIZ];
uint64_t size;
int len;
if (!safe_file_open_read(&input, infilename)) {
return 0;
}
if (!safe_file_open_write(&output, outfilename)) {
safe_file_close(&input);
return 0;
}
memcpy(header.magic, "ASCONcrypt", 10);
header.version[0] = 0;
header.version[1] = 1;
memset(&siv, 0, sizeof(siv));
!
ascon_random(siv.key,
sizeof(siv.key) +
sizeof(siv.nonce))) {
fprintf(stderr, "FATAL: system random number generator does not appear to be working!\n");
goto cleanup;
}
siv_copy = siv;
(const unsigned char *)full_password, strlen(full_password),
header.salt, sizeof(header.salt), ASCON_PBKDF2_ROUNDS);
(siv.key, &clen, siv.key, sizeof(siv.key) + sizeof(siv.nonce),
(const unsigned char *)&header, sizeof(header), kn + 20, kn);
exit_val = 1;
if (!safe_file_write(&output, &header, sizeof(header)) ||
!safe_file_write(&output, &siv, sizeof(siv))) {
exit_val = 0;
}
(&
state, (
const unsigned char *)&siv,
sizeof(siv),
siv_copy.nonce, siv_copy.key);
size = 0;
while (exit_val) {
len = safe_file_read(&input,
data,
sizeof(
data));
if (len < 0) {
exit_val = 0;
} else if (len == 0) {
break;
} else {
size += (unsigned)len;
if (size > ASCON_MAX_FILE_SIZE) {
fprintf(stderr, "%s: maximum file size exceeded\n", infilename);
exit_val = 0;
break;
}
if (!safe_file_write(&output,
data, len))
exit_val = 0;
if (len < (
int)
sizeof(
data))
break;
}
}
if (exit_val) {
exit_val = 0;
}
cleanup:
safe_file_close(&input);
safe_file_close(&output);
if (!exit_val)
safe_file_delete(&output);
return exit_val;
}
static int decrypt_file(const char *infilename, const char *outfilename)
{
struct asconcrypt_full_header header;
struct asconcrypt_siv_block siv_copy;
unsigned char kn[20 + 16];
int exit_val = 0;
size_t mlen = 0;
int bad_format;
SAFEFILE input;
SAFEFILE output;
unsigned char data[ASCON_BUFSIZ];
uint64_t size;
int len;
if (!safe_file_open_read(&input, infilename)) {
return 0;
}
if (!safe_file_open_write(&output, outfilename)) {
safe_file_close(&input);
return 0;
}
bad_format = 0;
if (safe_file_read(&input, &header, sizeof(header)) != (int)sizeof(header)) {
bad_format = 1;
} else {
if (memcmp(header.header.magic, "ASCONcrypt", 10) != 0 ||
header.header.version[0] != 0 ||
header.header.version[1] != 1) {
bad_format = 1;
}
}
if (bad_format) {
fprintf(stderr, "%s: unrecognized encrypted file format\n",
infilename);
goto cleanup;
}
(const unsigned char *)full_password, strlen(full_password),
header.header.salt, sizeof(header.header.salt),
ASCON_PBKDF2_ROUNDS);
siv_copy = header.siv;
(header.siv.key, &mlen, header.siv.key, sizeof(header.siv),
(const unsigned char *)&(header.header), sizeof(header.header),
kn + 20, kn) != 0) {
fprintf(stderr, "%s: password is incorrect\n", infilename);
goto cleanup;
}
if (safe_file_read(&input,
data, 16) != 16) {
fprintf(stderr, "%s: encrypted data is truncated\n", infilename);
goto cleanup;
}
(&
state, (
const unsigned char *)&siv_copy,
sizeof(siv_copy),
header.siv.nonce, header.siv.key);
size = 0;
exit_val = 1;
while (exit_val) {
len = safe_file_read(&input,
data + 16,
sizeof(
data) - 16);
if (len < 0) {
exit_val = 0;
} else if (len == 0) {
break;
} else {
size += (unsigned)len;
if (size > ASCON_MAX_FILE_SIZE) {
fprintf(stderr, "%s: maximum file size exceeded\n", infilename);
exit_val = 0;
break;
}
if (!safe_file_write(&output,
data, len))
exit_val = 0;
if (len < (
int)(
sizeof(
data) - 16))
break;
}
}
exit_val = 0;
fprintf(stderr, "%s: file is corrupt and failed to decrypt\n",
infilename);
goto cleanup;
}
cleanup:
safe_file_close(&input);
safe_file_close(&output);
if (!exit_val)
safe_file_delete(&output);
return exit_val;
}
ASCON-128 encryption algorithm and related family members.
#define ASCON80PQ_TAG_SIZE
Size of the authentication tag for ASCON-80pq.
void ascon80pq_aead_start(ascon80pq_state_t *state, const unsigned char *ad, size_t adlen, const unsigned char *npub, const unsigned char *k)
Starts encrypting or decrypting a packet with ASCON-80pq in incremental mode.
void ascon80pq_aead_encrypt_block(ascon80pq_state_t *state, const unsigned char *in, unsigned char *out, size_t len)
Encrypts a block of data with ASCON-80pq in incremental mode.
void ascon80pq_aead_decrypt_block(ascon80pq_state_t *state, const unsigned char *in, unsigned char *out, size_t len)
Decrypts a block of data with ASCON-80pq in incremental mode.
void ascon80pq_aead_encrypt_finalize(ascon80pq_state_t *state, unsigned char *tag)
Finalizes an incremental ASCON-80pq encryption operation and generates the authentication tag.
int ascon80pq_aead_decrypt_finalize(ascon80pq_state_t *state, const unsigned char *tag)
Finalizes an incremental ASCON-80pq decryption operation and checks the authentication tag.
Password-based key derivation function based on ASCON.
void ascon_pbkdf2(unsigned char *out, size_t outlen, const unsigned char *password, size_t passwordlen, const unsigned char *salt, size_t saltlen, unsigned long count)
Derives key material using ASCON-PBKDF2.
Pseudorandom number generator (PRNG) built around ASCON.
int ascon_random(unsigned char *out, size_t outlen)
Gets a block of random data from the system.
SIV encryption primitives built around the ASCON permutation.
void ascon80pq_siv_encrypt(unsigned char *c, size_t *clen, const unsigned char *m, size_t mlen, const unsigned char *ad, size_t adlen, const unsigned char *npub, const unsigned char *k)
Encrypts and authenticates a packet with ASCON-80pq-SIV.
int ascon80pq_siv_decrypt(unsigned char *m, size_t *mlen, const unsigned char *c, size_t clen, const unsigned char *ad, size_t adlen, const unsigned char *npub, const unsigned char *k)
Decrypts and authenticates a packet with ASCON-80pq-SIV.
ascon_state_t state
[snippet_key]
unsigned char data[8]
[snippet_key]
State information for the incremental version of ASCON-80pq.
System utilities of use to applications that use ASCON.
void ascon_clean(void *buf, unsigned size)
Cleans a buffer that contains sensitive material.