Compare commits
9 Commits
741c0d0bb5
...
a3079f5eda
Author | SHA1 | Date |
---|---|---|
Juhani Krekelä | a3079f5eda | |
Juhani Krekelä | 41a74402f0 | |
Juhani Krekelä | 5393e64c18 | |
Juhani Krekelä | 6079530e1d | |
Juhani Krekelä | 4b5ef70bce | |
Juhani Krekelä | f445783a44 | |
Juhani Krekelä | 0cb02aaf14 | |
Juhani Krekelä | 138cc5d2f5 | |
Juhani Krekelä | 4ec4a06776 |
410
puer.c
410
puer.c
|
@ -10,6 +10,10 @@
|
|||
#include <termios.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]) {
|
||||
// Encryption half of the XXTEA algorithm, with block size limited
|
||||
// to 128 bits or 4 words. This avoids all the weaknesses that
|
||||
|
@ -268,9 +272,8 @@ void hmac(unsigned char output[32], unsigned char key[], size_t keylen, unsigned
|
|||
finalize_hash(&state, output);
|
||||
}
|
||||
|
||||
// KDF_ROUNDS must be at least 2
|
||||
#define KDF_ROUNDS 100000
|
||||
unsigned char kdf_buf[KDF_ROUNDS * 32];
|
||||
#define KDF_ROUNDS (sizeof(workbuf) / 32)
|
||||
|
||||
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 achieved by storing all the hashes in a buffer and the
|
||||
|
@ -288,23 +291,26 @@ void kdf(unsigned char key[16], unsigned char salt[32], unsigned char passphrase
|
|||
// include the counter i from PBKDF2 since we will ever only
|
||||
// produce one block of output
|
||||
size_t index = KDF_ROUNDS*32 - 32;
|
||||
hmac(&kdf_buf[index], passphrase, passphraselen, salt, 32);
|
||||
hmac(&workbuf[index], passphrase, passphraselen, salt, 32);
|
||||
index -= 32;
|
||||
|
||||
// Walk back along the buffer, at each step hashing the previous
|
||||
// hashes
|
||||
while (index > 0) {
|
||||
hmac(&kdf_buf[index], passphrase, passphraselen, &kdf_buf[index+32], 32);
|
||||
hmac(&workbuf[index], passphrase, passphraselen, &workbuf[index+32], 32);
|
||||
index -= 32;
|
||||
}
|
||||
hmac(kdf_buf, passphrase, passphraselen, &kdf_buf[32], 32);
|
||||
hmac(workbuf, passphrase, passphraselen, &workbuf[32], 32);
|
||||
|
||||
// Perform the final hash
|
||||
unsigned char final_hash[32];
|
||||
hmac(final_hash, passphrase, passphraselen, kdf_buf, KDF_ROUNDS * 32);
|
||||
hmac(final_hash, passphrase, passphraselen, workbuf, KDF_ROUNDS * 32);
|
||||
|
||||
// Use first 128 bits of final hash as the key
|
||||
memcpy(key, final_hash, 16);
|
||||
|
||||
// Empty the buffer
|
||||
explicit_bzero(workbuf, sizeof(workbuf));
|
||||
}
|
||||
|
||||
// 16 bit authentication tag
|
||||
|
@ -312,7 +318,7 @@ const int mprime = (16-2)/2;
|
|||
// 32 bit = 4 byte length field
|
||||
const int lprime = 4-1;
|
||||
|
||||
void ccm_mac(unsigned char mac[16], uint32_t key[4], uint32_t messageindex, unsigned char message[], uint32_t length) {
|
||||
void ccm_mac(unsigned char mac[16], uint32_t key[4], uint64_t messageindex, unsigned char message[], uint32_t length) {
|
||||
// CCM specifies that the length field is big endian while we are
|
||||
// natively little endian. Flip it.
|
||||
unsigned char length_bytes[4];
|
||||
|
@ -323,7 +329,7 @@ void ccm_mac(unsigned char mac[16], uint32_t key[4], uint32_t messageindex, unsi
|
|||
uint32_t be_length = bytes2word(length_bytes);
|
||||
|
||||
// First block is special
|
||||
uint32_t mac_words[4] = {mprime<<3 | lprime, 0, messageindex, be_length};
|
||||
uint32_t mac_words[4] = {mprime<<3 | lprime, messageindex, messageindex >> 32, be_length};
|
||||
xxtea128(key, mac_words);
|
||||
|
||||
// Process all full blocks
|
||||
|
@ -358,7 +364,7 @@ void ccm_mac(unsigned char mac[16], uint32_t key[4], uint32_t messageindex, unsi
|
|||
words2block(mac, mac_words);
|
||||
}
|
||||
|
||||
void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint32_t messageindex, uint32_t counter) {
|
||||
void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint64_t messageindex, uint32_t counter) {
|
||||
// CCM specifies that the counter field is big endian while we are
|
||||
// natively little endian. Flip it.
|
||||
unsigned char counter_bytes[4];
|
||||
|
@ -368,7 +374,7 @@ void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint32_t messageind
|
|||
counter_bytes[3] = counter;
|
||||
uint32_t be_counter = bytes2word(counter_bytes);
|
||||
|
||||
uint32_t words[4] = {lprime, 0, messageindex, be_counter};
|
||||
uint32_t words[4] = {lprime, messageindex, messageindex >> 32, be_counter};
|
||||
xxtea128(key, words);
|
||||
unsigned char keystream[16];
|
||||
words2block(keystream, words);
|
||||
|
@ -378,10 +384,7 @@ void ccm_xor_block(unsigned char block[16], uint32_t key[4], uint32_t messageind
|
|||
}
|
||||
}
|
||||
|
||||
// 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]) {
|
||||
void ccm_encrypt(unsigned char key[16], uint64_t messageindex, unsigned char message[], uint32_t length, unsigned char mac[16]) {
|
||||
uint32_t key_words[4];
|
||||
block2words(key_words, key);
|
||||
|
||||
|
@ -391,25 +394,44 @@ void ccm_encrypt(unsigned char key[16], uint32_t messageindex, unsigned char mes
|
|||
// Encrypt
|
||||
// MAC is xored with first block of keystream
|
||||
ccm_xor_block(mac, key_words, messageindex, 0);
|
||||
// Xor the message
|
||||
for (uint32_t index = 0; index < length; index += 16) {
|
||||
// Message blocks are numbered from index 1 onwards
|
||||
ccm_xor_block(&message[index], key_words, messageindex, index + 1);
|
||||
|
||||
// Xor full blocks
|
||||
size_t index = 0;
|
||||
uint32_t counter = 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);
|
||||
}
|
||||
}
|
||||
|
||||
// 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]) {
|
||||
bool ccm_decrypt(unsigned char key[16], uint64_t messageindex, unsigned char message[], uint32_t length, unsigned char mac[16]) {
|
||||
uint32_t key_words[4];
|
||||
block2words(key_words, key);
|
||||
|
||||
// Decrypt
|
||||
// MAC is xored with first block of keystream
|
||||
ccm_xor_block(mac, key_words, messageindex, 0);
|
||||
// Xor the message
|
||||
for (uint32_t index = 0; index < length; index += 16) {
|
||||
|
||||
// Xor full blocks
|
||||
size_t index = 0;
|
||||
uint32_t counter = 1;
|
||||
for (; index + 16 <= length; index += 16) {
|
||||
// Message blocks are numbered from index 1 onwards
|
||||
ccm_xor_block(&message[index], key_words, messageindex, index + 1);
|
||||
ccm_xor_block(&message[index], key_words, messageindex, counter++);
|
||||
}
|
||||
// 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
|
||||
|
@ -433,31 +455,34 @@ bool ccm_decrypt(unsigned char key[16], uint32_t messageindex, unsigned char mes
|
|||
return true;
|
||||
}
|
||||
|
||||
size_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *prompt) {
|
||||
// Read from controlling TTY, even if stdion has been redirected
|
||||
ssize_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *prompt) {
|
||||
// Read from controlling TTY, even if stdio has been redirected
|
||||
int tty = open("/dev/tty", O_RDWR);
|
||||
if (tty == -1) {
|
||||
perror("Failed to open controlling tty");
|
||||
exit(1);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write(tty, prompt, strlen(prompt)) == -1) {
|
||||
perror("Failed to write to terminal");
|
||||
exit(1);
|
||||
close(tty);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Turn off echo
|
||||
struct termios saved;
|
||||
if (tcgetattr(tty, &saved) != 0) {
|
||||
perror("tcgetattr");
|
||||
exit(1);
|
||||
perror("Failed to get terminal attributes");
|
||||
close(tty);
|
||||
return -1;
|
||||
}
|
||||
struct termios altered;
|
||||
altered = saved;
|
||||
altered.c_lflag &= ~ECHO;
|
||||
if (tcsetattr(tty, TCSANOW, &altered) != 0) {
|
||||
perror("tcsetattr");
|
||||
exit(1);
|
||||
perror("Failed to turn echoing off");
|
||||
close(tty);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Read until newline
|
||||
|
@ -476,18 +501,21 @@ size_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *pro
|
|||
}
|
||||
}
|
||||
tcsetattr(tty, TCSANOW, &saved);
|
||||
exit(1);
|
||||
close(tty);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ssize_t bytes_read = read(tty, &passphrase[index], size - index);
|
||||
if (bytes_read == -1) {
|
||||
perror("Failed to read passphrase");
|
||||
tcsetattr(tty, TCSANOW, &saved);
|
||||
exit(1);
|
||||
close(tty);
|
||||
return -1;
|
||||
} else if (bytes_read == 0) {
|
||||
fprintf(stderr, "Unexpected EOF\n");
|
||||
tcsetattr(tty, TCSANOW, &saved);
|
||||
exit(1);
|
||||
close(tty);
|
||||
return -1;
|
||||
}
|
||||
|
||||
index += bytes_read;
|
||||
|
@ -501,52 +529,300 @@ size_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *pro
|
|||
// terminal settings
|
||||
if (write(tty, "\n", 1) == -1) {
|
||||
perror("Failed to write to terminal");
|
||||
exit(1);
|
||||
tcsetattr(tty, TCSANOW, &saved);
|
||||
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);
|
||||
|
||||
return index - 1;
|
||||
}
|
||||
|
||||
int main(void) {
|
||||
unsigned char salt[32];
|
||||
if (getentropy(salt, 32) != 0) {
|
||||
perror("getentropy");
|
||||
ssize_t passphrase_file(char *passfilepath, unsigned char passphrase[], size_t size) {
|
||||
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");
|
||||
}
|
||||
|
||||
unsigned char passphrase[128] = {0};
|
||||
size_t passphrase_len = passphrase_prompt(passphrase, sizeof(passphrase), "passphrase: ");
|
||||
// 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];
|
||||
if (encrypting) {
|
||||
// Generate salt randomly
|
||||
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];
|
||||
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];
|
||||
kdf(key, salt, passphrase, passphrase_len);
|
||||
for (size_t i = 0; i < 16; i++) {
|
||||
printf("%02hhx ", key[i]);
|
||||
}
|
||||
printf("\n\n");
|
||||
explicit_bzero(passphrase, sizeof(passphrase));
|
||||
|
||||
char *message = "Syökää parsaa ja palvokaa saatanaa";
|
||||
unsigned char buf[64] = {69};
|
||||
strcpy((char*)buf, message);
|
||||
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);
|
||||
}
|
||||
|
||||
ccm_encrypt(key, 25, buf, strlen(message), &buf[48]);
|
||||
for (size_t i = 0; i < sizeof(buf); i++) {
|
||||
printf("%02hhx ", buf[i]);
|
||||
if (i % 16 == 15) printf("\n");
|
||||
}
|
||||
printf("\n");
|
||||
// MAC is after the message
|
||||
unsigned char *mac = &workbuf[bytes];
|
||||
|
||||
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");
|
||||
ccm_encrypt(key, messageindex++, workbuf, bytes, mac);
|
||||
|
||||
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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Remove the key from memory
|
||||
explicit_bzero(key, sizeof(key));
|
||||
|
||||
if (fflush(stdout) != 0) {
|
||||
perror("Failure writing");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue