Compare commits
No commits in common. "a3079f5eda61a24d5ac861ad53cc85b6626ecefe" and "741c0d0bb5e2e656000771681b4adf10e3cfb798" have entirely different histories.
a3079f5eda
...
741c0d0bb5
1 changed files with 66 additions and 342 deletions
408
puer.c
408
puer.c
|
@ -10,10 +10,6 @@
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
// Adjusting this will render the file format incompatible
|
|
||||||
// The minimum possible buffer size is 64
|
|
||||||
unsigned char workbuf[8 * 1024 * 1024];
|
|
||||||
|
|
||||||
void xxtea128(uint32_t const key[4], uint32_t block[4]) {
|
void xxtea128(uint32_t const key[4], uint32_t block[4]) {
|
||||||
// Encryption half of the XXTEA algorithm, with block size limited
|
// Encryption half of the XXTEA algorithm, with block size limited
|
||||||
// to 128 bits or 4 words. This avoids all the weaknesses that
|
// to 128 bits or 4 words. This avoids all the weaknesses that
|
||||||
|
@ -272,8 +268,9 @@ void hmac(unsigned char output[32], unsigned char key[], size_t keylen, unsigned
|
||||||
finalize_hash(&state, output);
|
finalize_hash(&state, output);
|
||||||
}
|
}
|
||||||
|
|
||||||
#define KDF_ROUNDS (sizeof(workbuf) / 32)
|
// KDF_ROUNDS must be at least 2
|
||||||
|
#define KDF_ROUNDS 100000
|
||||||
|
unsigned char kdf_buf[KDF_ROUNDS * 32];
|
||||||
void kdf(unsigned char key[16], unsigned char salt[32], unsigned char passphrase[], size_t passphraselen) {
|
void kdf(unsigned char key[16], unsigned char salt[32], unsigned char passphrase[], size_t passphraselen) {
|
||||||
// This is based on the design of PBKDF2 but aims to be memory hard
|
// This is based on the design of PBKDF2 but aims to be memory hard
|
||||||
// This is achieved by storing all the hashes in a buffer and the
|
// This is achieved by storing all the hashes in a buffer and the
|
||||||
|
@ -291,26 +288,23 @@ void kdf(unsigned char key[16], unsigned char salt[32], unsigned char passphrase
|
||||||
// include the counter i from PBKDF2 since we will ever only
|
// include the counter i from PBKDF2 since we will ever only
|
||||||
// produce one block of output
|
// produce one block of output
|
||||||
size_t index = KDF_ROUNDS*32 - 32;
|
size_t index = KDF_ROUNDS*32 - 32;
|
||||||
hmac(&workbuf[index], passphrase, passphraselen, salt, 32);
|
hmac(&kdf_buf[index], passphrase, passphraselen, salt, 32);
|
||||||
index -= 32;
|
index -= 32;
|
||||||
|
|
||||||
// Walk back along the buffer, at each step hashing the previous
|
// Walk back along the buffer, at each step hashing the previous
|
||||||
// hashes
|
// hashes
|
||||||
while (index > 0) {
|
while (index > 0) {
|
||||||
hmac(&workbuf[index], passphrase, passphraselen, &workbuf[index+32], 32);
|
hmac(&kdf_buf[index], passphrase, passphraselen, &kdf_buf[index+32], 32);
|
||||||
index -= 32;
|
index -= 32;
|
||||||
}
|
}
|
||||||
hmac(workbuf, passphrase, passphraselen, &workbuf[32], 32);
|
hmac(kdf_buf, passphrase, passphraselen, &kdf_buf[32], 32);
|
||||||
|
|
||||||
// Perform the final hash
|
// Perform the final hash
|
||||||
unsigned char final_hash[32];
|
unsigned char final_hash[32];
|
||||||
hmac(final_hash, passphrase, passphraselen, workbuf, KDF_ROUNDS * 32);
|
hmac(final_hash, passphrase, passphraselen, kdf_buf, KDF_ROUNDS * 32);
|
||||||
|
|
||||||
// Use first 128 bits of final hash as the key
|
// Use first 128 bits of final hash as the key
|
||||||
memcpy(key, final_hash, 16);
|
memcpy(key, final_hash, 16);
|
||||||
|
|
||||||
// Empty the buffer
|
|
||||||
explicit_bzero(workbuf, sizeof(workbuf));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 16 bit authentication tag
|
// 16 bit authentication tag
|
||||||
|
@ -318,7 +312,7 @@ const int mprime = (16-2)/2;
|
||||||
// 32 bit = 4 byte length field
|
// 32 bit = 4 byte length field
|
||||||
const int lprime = 4-1;
|
const int lprime = 4-1;
|
||||||
|
|
||||||
void ccm_mac(unsigned char mac[16], uint32_t key[4], uint64_t messageindex, unsigned char message[], uint32_t length) {
|
void ccm_mac(unsigned char mac[16], uint32_t key[4], uint32_t messageindex, unsigned char message[], uint32_t length) {
|
||||||
// CCM specifies that the length field is big endian while we are
|
// CCM specifies that the length field is big endian while we are
|
||||||
// natively little endian. Flip it.
|
// natively little endian. Flip it.
|
||||||
unsigned char length_bytes[4];
|
unsigned char length_bytes[4];
|
||||||
|
@ -329,7 +323,7 @@ void ccm_mac(unsigned char mac[16], uint32_t key[4], uint64_t messageindex, unsi
|
||||||
uint32_t be_length = bytes2word(length_bytes);
|
uint32_t be_length = bytes2word(length_bytes);
|
||||||
|
|
||||||
// First block is special
|
// First block is special
|
||||||
uint32_t mac_words[4] = {mprime<<3 | lprime, messageindex, messageindex >> 32, be_length};
|
uint32_t mac_words[4] = {mprime<<3 | lprime, 0, messageindex, be_length};
|
||||||
xxtea128(key, mac_words);
|
xxtea128(key, mac_words);
|
||||||
|
|
||||||
// Process all full blocks
|
// Process all full blocks
|
||||||
|
@ -364,7 +358,7 @@ void ccm_mac(unsigned char mac[16], uint32_t key[4], uint64_t messageindex, unsi
|
||||||
words2block(mac, mac_words);
|
words2block(mac, mac_words);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint64_t messageindex, uint32_t counter) {
|
void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint32_t messageindex, uint32_t counter) {
|
||||||
// CCM specifies that the counter field is big endian while we are
|
// CCM specifies that the counter field is big endian while we are
|
||||||
// natively little endian. Flip it.
|
// natively little endian. Flip it.
|
||||||
unsigned char counter_bytes[4];
|
unsigned char counter_bytes[4];
|
||||||
|
@ -374,7 +368,7 @@ void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint64_t messageind
|
||||||
counter_bytes[3] = counter;
|
counter_bytes[3] = counter;
|
||||||
uint32_t be_counter = bytes2word(counter_bytes);
|
uint32_t be_counter = bytes2word(counter_bytes);
|
||||||
|
|
||||||
uint32_t words[4] = {lprime, messageindex, messageindex >> 32, be_counter};
|
uint32_t words[4] = {lprime, 0, messageindex, be_counter};
|
||||||
xxtea128(key, words);
|
xxtea128(key, words);
|
||||||
unsigned char keystream[16];
|
unsigned char keystream[16];
|
||||||
words2block(keystream, words);
|
words2block(keystream, words);
|
||||||
|
@ -384,7 +378,10 @@ void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint64_t messageind
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ccm_encrypt(unsigned char key[16], uint64_t messageindex, unsigned char message[], uint32_t length, unsigned char mac[16]) {
|
// IMPORTANT: The underlying message[] must be addressable until the index
|
||||||
|
// ceil(length / 16)*16, but the length only reflects the actual message
|
||||||
|
// length
|
||||||
|
void ccm_encrypt(unsigned char key[16], uint32_t messageindex, unsigned char message[], uint32_t length, unsigned char mac[16]) {
|
||||||
uint32_t key_words[4];
|
uint32_t key_words[4];
|
||||||
block2words(key_words, key);
|
block2words(key_words, key);
|
||||||
|
|
||||||
|
@ -394,44 +391,25 @@ void ccm_encrypt(unsigned char key[16], uint64_t messageindex, unsigned char mes
|
||||||
// Encrypt
|
// Encrypt
|
||||||
// MAC is xored with first block of keystream
|
// MAC is xored with first block of keystream
|
||||||
ccm_xor_block(mac, key_words, messageindex, 0);
|
ccm_xor_block(mac, key_words, messageindex, 0);
|
||||||
|
// Xor the message
|
||||||
// Xor full blocks
|
for (uint32_t index = 0; index < length; index += 16) {
|
||||||
size_t index = 0;
|
// Message blocks are numbered from index 1 onwards
|
||||||
uint32_t counter = 1;
|
ccm_xor_block(&message[index], key_words, messageindex, index + 1);
|
||||||
for (; index + 16 <= length; index += 16) {
|
|
||||||
ccm_xor_block(&message[index], key_words, messageindex, counter++);
|
|
||||||
}
|
|
||||||
// Xor partial block, if any
|
|
||||||
if (index < length) {
|
|
||||||
unsigned char fullblock[16];
|
|
||||||
memcpy(fullblock, &message[index], length - index);
|
|
||||||
ccm_xor_block(fullblock, key_words, messageindex, counter++);
|
|
||||||
memcpy(&message[index], fullblock, length - index);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ccm_decrypt(unsigned char key[16], uint64_t messageindex, unsigned char message[], uint32_t length, unsigned char mac[16]) {
|
// Same requirements for message[] hold as with ccm_encrypt
|
||||||
|
bool ccm_decrypt(unsigned char key[16], uint32_t messageindex, unsigned char message[], uint32_t length, unsigned char mac[16]) {
|
||||||
uint32_t key_words[4];
|
uint32_t key_words[4];
|
||||||
block2words(key_words, key);
|
block2words(key_words, key);
|
||||||
|
|
||||||
// Decrypt
|
// Decrypt
|
||||||
// MAC is xored with first block of keystream
|
// MAC is xored with first block of keystream
|
||||||
ccm_xor_block(mac, key_words, messageindex, 0);
|
ccm_xor_block(mac, key_words, messageindex, 0);
|
||||||
|
// Xor the message
|
||||||
// Xor full blocks
|
for (uint32_t index = 0; index < length; index += 16) {
|
||||||
size_t index = 0;
|
|
||||||
uint32_t counter = 1;
|
|
||||||
for (; index + 16 <= length; index += 16) {
|
|
||||||
// Message blocks are numbered from index 1 onwards
|
// Message blocks are numbered from index 1 onwards
|
||||||
ccm_xor_block(&message[index], key_words, messageindex, counter++);
|
ccm_xor_block(&message[index], key_words, messageindex, index + 1);
|
||||||
}
|
|
||||||
// Xor partial block, if any
|
|
||||||
if (index < length) {
|
|
||||||
unsigned char fullblock[16];
|
|
||||||
memset(fullblock, 0, 16);
|
|
||||||
memcpy(fullblock, &message[index], length - index);
|
|
||||||
ccm_xor_block(fullblock, key_words, messageindex, counter++);
|
|
||||||
memcpy(&message[index], fullblock, length - index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compute the expected authentication tag
|
// Compute the expected authentication tag
|
||||||
|
@ -455,34 +433,31 @@ bool ccm_decrypt(unsigned char key[16], uint64_t messageindex, unsigned char mes
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *prompt) {
|
size_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *prompt) {
|
||||||
// Read from controlling TTY, even if stdio has been redirected
|
// Read from controlling TTY, even if stdion has been redirected
|
||||||
int tty = open("/dev/tty", O_RDWR);
|
int tty = open("/dev/tty", O_RDWR);
|
||||||
if (tty == -1) {
|
if (tty == -1) {
|
||||||
perror("Failed to open controlling tty");
|
perror("Failed to open controlling tty");
|
||||||
return -1;
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write(tty, prompt, strlen(prompt)) == -1) {
|
if (write(tty, prompt, strlen(prompt)) == -1) {
|
||||||
perror("Failed to write to terminal");
|
perror("Failed to write to terminal");
|
||||||
close(tty);
|
exit(1);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn off echo
|
// Turn off echo
|
||||||
struct termios saved;
|
struct termios saved;
|
||||||
if (tcgetattr(tty, &saved) != 0) {
|
if (tcgetattr(tty, &saved) != 0) {
|
||||||
perror("Failed to get terminal attributes");
|
perror("tcgetattr");
|
||||||
close(tty);
|
exit(1);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
struct termios altered;
|
struct termios altered;
|
||||||
altered = saved;
|
altered = saved;
|
||||||
altered.c_lflag &= ~ECHO;
|
altered.c_lflag &= ~ECHO;
|
||||||
if (tcsetattr(tty, TCSANOW, &altered) != 0) {
|
if (tcsetattr(tty, TCSANOW, &altered) != 0) {
|
||||||
perror("Failed to turn echoing off");
|
perror("tcsetattr");
|
||||||
close(tty);
|
exit(1);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read until newline
|
// Read until newline
|
||||||
|
@ -501,21 +476,18 @@ ssize_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *pr
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
tcsetattr(tty, TCSANOW, &saved);
|
tcsetattr(tty, TCSANOW, &saved);
|
||||||
close(tty);
|
exit(1);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t bytes_read = read(tty, &passphrase[index], size - index);
|
ssize_t bytes_read = read(tty, &passphrase[index], size - index);
|
||||||
if (bytes_read == -1) {
|
if (bytes_read == -1) {
|
||||||
perror("Failed to read passphrase");
|
perror("Failed to read passphrase");
|
||||||
tcsetattr(tty, TCSANOW, &saved);
|
tcsetattr(tty, TCSANOW, &saved);
|
||||||
close(tty);
|
exit(1);
|
||||||
return -1;
|
|
||||||
} else if (bytes_read == 0) {
|
} else if (bytes_read == 0) {
|
||||||
fprintf(stderr, "Unexpected EOF\n");
|
fprintf(stderr, "Unexpected EOF\n");
|
||||||
tcsetattr(tty, TCSANOW, &saved);
|
tcsetattr(tty, TCSANOW, &saved);
|
||||||
close(tty);
|
exit(1);
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
index += bytes_read;
|
index += bytes_read;
|
||||||
|
@ -529,300 +501,52 @@ ssize_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *pr
|
||||||
// terminal settings
|
// terminal settings
|
||||||
if (write(tty, "\n", 1) == -1) {
|
if (write(tty, "\n", 1) == -1) {
|
||||||
perror("Failed to write to terminal");
|
perror("Failed to write to terminal");
|
||||||
tcsetattr(tty, TCSANOW, &saved);
|
exit(1);
|
||||||
close(tty);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (tcsetattr(tty, TCSANOW, &saved) != 0) {
|
|
||||||
perror("Failed to restore terminal state");
|
|
||||||
close(tty);
|
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
tcsetattr(tty, TCSANOW, &saved);
|
||||||
close(tty);
|
close(tty);
|
||||||
|
|
||||||
return index - 1;
|
return index - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
ssize_t passphrase_file(char *passfilepath, unsigned char passphrase[], size_t size) {
|
int main(void) {
|
||||||
int file = open(passfilepath, O_RDONLY);
|
|
||||||
|
|
||||||
// Check permissions
|
|
||||||
struct stat statbuf;
|
|
||||||
if (fstat(file, &statbuf) != 0) {
|
|
||||||
perror("Could not stat passphrase file");
|
|
||||||
close(file);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
if (statbuf.st_mode & S_IROTH) {
|
|
||||||
fprintf(stderr, "Warning: Passphrase file is world-readable\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read until newline
|
|
||||||
size_t index = 0;
|
|
||||||
for (;;) {
|
|
||||||
if (index >= size) {
|
|
||||||
fprintf(stderr, "Passphrase too long, maximum size is %zu bytes\n", size - 1);
|
|
||||||
close(file);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t bytes_read = read(file, &passphrase[index], size - index);
|
|
||||||
if (bytes_read == -1) {
|
|
||||||
perror("Failed to read passphrase");
|
|
||||||
close(file);
|
|
||||||
return -1;
|
|
||||||
} else if (bytes_read == 0) {
|
|
||||||
fprintf(stderr, "Unexpected EOF\n");
|
|
||||||
close(file);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
index += bytes_read;
|
|
||||||
if (passphrase[index-1] == '\n') {
|
|
||||||
// Got end of line
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
close(file);
|
|
||||||
|
|
||||||
return index - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
void usage(char *name) {
|
|
||||||
fprintf(stderr, "Usage: %s -d | -e [-f] [-p passfile] [-i infile] [-o outfile]\n\n", name);
|
|
||||||
fprintf(stderr, "-d Decrypt\n");
|
|
||||||
fprintf(stderr, "-e Encrypt\n");
|
|
||||||
fprintf(stderr, "-f Force output to terminal\n");
|
|
||||||
fprintf(stderr, "-p passfile Read passphrase from a file instead of the terminal.\n");
|
|
||||||
fprintf(stderr, "-i infile Read from a file instead of the terminal.\n");
|
|
||||||
fprintf(stderr, "-o outfile Write to a file instead of the terminal.\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
|
||||||
bool encrypting = false;
|
|
||||||
bool decrypting = false;
|
|
||||||
bool force = false;
|
|
||||||
char *passfilepath = NULL;
|
|
||||||
char *infilepath = NULL;
|
|
||||||
char *outfilepath = NULL;
|
|
||||||
|
|
||||||
int opt;
|
|
||||||
while ((opt = getopt(argc, argv, "defp:i:o:")) != -1) {
|
|
||||||
switch (opt) {
|
|
||||||
case 'd':
|
|
||||||
decrypting = true;
|
|
||||||
break;
|
|
||||||
case 'e':
|
|
||||||
encrypting = true;
|
|
||||||
break;
|
|
||||||
case 'f':
|
|
||||||
force = true;
|
|
||||||
break;
|
|
||||||
case 'p':
|
|
||||||
passfilepath = optarg;
|
|
||||||
break;
|
|
||||||
case 'i':
|
|
||||||
infilepath = optarg;
|
|
||||||
break;
|
|
||||||
case 'o':
|
|
||||||
outfilepath = optarg;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
usage(argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (optind != argc) {
|
|
||||||
usage(argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((!encrypting && !decrypting) || (encrypting && decrypting)) {
|
|
||||||
usage(argv[0]);
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *infile = stdin;
|
|
||||||
FILE *outfile = stdout;
|
|
||||||
|
|
||||||
if (infilepath != NULL) {
|
|
||||||
infile = fopen(infilepath, "r");
|
|
||||||
if (infile == NULL) {
|
|
||||||
perror("Failed to open input file");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (outfilepath != NULL) {
|
|
||||||
outfile = fopen(outfilepath, "w");
|
|
||||||
if (outfile == NULL) {
|
|
||||||
perror("Failed to open output file");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encrypting && !force && isatty(fileno(outfile))) {
|
|
||||||
fprintf(stderr, "Refusing to print encrypted (binary) data to terminal. Use -f to force output.\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the salt for key derivation
|
|
||||||
unsigned char salt[32];
|
unsigned char salt[32];
|
||||||
if (encrypting) {
|
if (getentropy(salt, 32) != 0) {
|
||||||
// Generate salt randomly
|
perror("getentropy");
|
||||||
if (getentropy(salt, 32) != 0) {
|
|
||||||
perror("Could not generate salt (getentropy)");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
// Write salt to the beginning of the file
|
|
||||||
if (fwrite(&salt, 32, 1, outfile) != 1) {
|
|
||||||
fprintf(stderr, "Could not write salt\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Read salt from the beginning of the file
|
|
||||||
if (fread(&salt, 32, 1, infile) != 1) {
|
|
||||||
fprintf(stderr, "Could not read salt\n");
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read passphrase
|
unsigned char passphrase[128] = {0};
|
||||||
unsigned char passphrase[128];
|
size_t passphrase_len = passphrase_prompt(passphrase, sizeof(passphrase), "passphrase: ");
|
||||||
ssize_t passphrase_len;
|
|
||||||
if (passfilepath == NULL) {
|
|
||||||
// Read from terminal if no passfile specified
|
|
||||||
passphrase_len = passphrase_prompt(passphrase, sizeof(passphrase), "passphrase: ");
|
|
||||||
if (passphrase_len == -1) {
|
|
||||||
explicit_bzero(passphrase, sizeof(passphrase));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (encrypting) {
|
|
||||||
// Have the user confirm the passphrase if encrypting, to avoid losing data
|
|
||||||
unsigned char confirm[sizeof(passphrase)];
|
|
||||||
ssize_t confirm_len = passphrase_prompt(confirm, sizeof(confirm), "confirm passphrase: ");
|
|
||||||
if (confirm_len == -1) {
|
|
||||||
explicit_bzero(passphrase, sizeof(passphrase));
|
|
||||||
explicit_bzero(confirm, sizeof(confirm));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (confirm_len != passphrase_len || memcmp(passphrase, confirm, passphrase_len) != 0) {
|
|
||||||
fprintf(stderr, "Passphrases do not match\n");
|
|
||||||
explicit_bzero(passphrase, sizeof(passphrase));
|
|
||||||
explicit_bzero(confirm, sizeof(confirm));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
explicit_bzero(confirm, sizeof(confirm));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
passphrase_len = passphrase_file(passfilepath, passphrase, sizeof(passphrase));
|
|
||||||
if (passphrase_len == -1) {
|
|
||||||
explicit_bzero(passphrase, sizeof(passphrase));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Derive key
|
|
||||||
unsigned char key[16];
|
unsigned char key[16];
|
||||||
kdf(key, salt, passphrase, passphrase_len);
|
kdf(key, salt, passphrase, passphrase_len);
|
||||||
explicit_bzero(passphrase, sizeof(passphrase));
|
for (size_t i = 0; i < 16; i++) {
|
||||||
|
printf("%02hhx ", key[i]);
|
||||||
uint64_t messageindex = 0;
|
|
||||||
if (encrypting) {
|
|
||||||
for (;;) {
|
|
||||||
// Leave space for the MAC in the work buffer
|
|
||||||
size_t bytes = fread(workbuf, 1, sizeof(workbuf) - 16, infile);
|
|
||||||
if (bytes == 0 && ferror(infile)) {
|
|
||||||
perror("Failure reading");
|
|
||||||
explicit_bzero(key, sizeof(key));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MAC is after the message
|
|
||||||
unsigned char *mac = &workbuf[bytes];
|
|
||||||
|
|
||||||
ccm_encrypt(key, messageindex++, workbuf, bytes, mac);
|
|
||||||
|
|
||||||
size_t written = fwrite(workbuf, 1, bytes + 16, outfile);
|
|
||||||
if (written != bytes + 16) {
|
|
||||||
perror("Failure writing");
|
|
||||||
explicit_bzero(key, sizeof(key));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this chunk was short, that means we're done
|
|
||||||
if (bytes < sizeof(workbuf) - 16) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageindex == 0) {
|
|
||||||
// We will not run into this, but I feel like it's cleaner to check
|
|
||||||
fprintf(stderr, "Chunk counter overflow\n");
|
|
||||||
explicit_bzero(key, sizeof(key));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (;;) {
|
|
||||||
size_t bytes = fread(workbuf, 1, sizeof(workbuf), infile);
|
|
||||||
if (bytes == 0 && ferror(infile)) {
|
|
||||||
perror("Failure reading");
|
|
||||||
explicit_bzero(key, sizeof(key));
|
|
||||||
exit(1);
|
|
||||||
} else if (bytes < 16) {
|
|
||||||
fprintf(stderr, "Chunk too short. File likely corrupt.\n");
|
|
||||||
explicit_bzero(key, sizeof(key));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// MAC is after the message
|
|
||||||
unsigned char *mac = &workbuf[bytes - 16];
|
|
||||||
|
|
||||||
bool auth = ccm_decrypt(key, messageindex++, workbuf, bytes - 16, mac);
|
|
||||||
|
|
||||||
if (!auth) {
|
|
||||||
if (messageindex == 1) {
|
|
||||||
// First chunk
|
|
||||||
fprintf(stderr, "Authentication failed. Either the passphrase is wrong or the file is corrupt.\n");
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Authentication failed. The file is likely corrupt.\n");
|
|
||||||
}
|
|
||||||
explicit_bzero(key, sizeof(key));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t written = fwrite(workbuf, 1, bytes - 16, outfile);
|
|
||||||
if (written != bytes - 16) {
|
|
||||||
perror("Failure writing");
|
|
||||||
explicit_bzero(key, sizeof(key));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// If this chunk was short, that means we're done
|
|
||||||
if (bytes < sizeof(workbuf)) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (messageindex == 0) {
|
|
||||||
// We will not run into this, but I feel like it's cleaner to check
|
|
||||||
fprintf(stderr, "Chunk counter overflow\n");
|
|
||||||
explicit_bzero(key, sizeof(key));
|
|
||||||
exit(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
printf("\n\n");
|
||||||
|
|
||||||
// Remove the key from memory
|
char *message = "Syökää parsaa ja palvokaa saatanaa";
|
||||||
explicit_bzero(key, sizeof(key));
|
unsigned char buf[64] = {69};
|
||||||
|
strcpy((char*)buf, message);
|
||||||
|
|
||||||
if (fflush(stdout) != 0) {
|
ccm_encrypt(key, 25, buf, strlen(message), &buf[48]);
|
||||||
perror("Failure writing");
|
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||||
exit(1);
|
printf("%02hhx ", buf[i]);
|
||||||
|
if (i % 16 == 15) printf("\n");
|
||||||
}
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
return 0;
|
memset(&buf[strlen(message)], 0x0f, 48-strlen(message));
|
||||||
|
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||||
|
printf("%02hhx ", buf[i]);
|
||||||
|
if (i % 16 == 15) printf("\n");
|
||||||
|
}
|
||||||
|
printf("\n");
|
||||||
|
|
||||||
|
bool auth = ccm_decrypt(key, 25, buf, strlen(message), &buf[48]);
|
||||||
|
printf("auth %s\n", auth ? "succeeded" : "failed");
|
||||||
|
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||||
|
printf("%02hhx ", buf[i]);
|
||||||
|
if (i % 16 == 15) printf("\n");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue