Add UI to puer

This commit is contained in:
Juhani Krekelä 2021-04-09 22:35:10 +03:00
parent 4b5ef70bce
commit 6079530e1d
1 changed files with 180 additions and 36 deletions

216
puer.c
View File

@ -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,7 +272,6 @@ void hmac(unsigned char output[32], unsigned char key[], size_t keylen, unsigned
finalize_hash(&state, output);
}
unsigned char workbuf[8 * 1024 * 1024];
#define KDF_ROUNDS (sizeof(workbuf) / 32)
void kdf(unsigned char key[16], unsigned char salt[32], unsigned char passphrase[], size_t passphraselen) {
@ -540,54 +543,195 @@ ssize_t passphrase_prompt(unsigned char *passphrase, size_t size, const char *pr
return index - 1;
}
int main(void) {
unsigned char salt[32];
if (getentropy(salt, 32) != 0) {
perror("getentropy");
void usage(char *name) {
fprintf(stderr, "Usage: %s -d | -e [-f]\n\n", name);
fprintf(stderr, "-d Decrypt\n");
fprintf(stderr, "-e Encrypt\n");
fprintf(stderr, "-f Force output to terminal\n");
}
int main(int argc, char *argv[]) {
bool encrypting = false;
bool decrypting = false;
bool force = false;
int opt;
while ((opt = getopt(argc, argv, "def")) != -1) {
switch (opt) {
case 'd':
decrypting = true;
break;
case 'e':
encrypting = true;
break;
case 'f':
force = true;
break;
default:
usage(argv[0]);
exit(1);
}
}
unsigned char passphrase[128] = {0};
if ((!encrypting && !decrypting) || (encrypting && decrypting)) {
usage(argv[0]);
exit(1);
}
FILE *infile = stdin;
FILE *outfile = stdout;
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 = 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[128];
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));
}
// 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];
memset(buf, 0xab, sizeof(buf));
strcpy((char*)buf, message);
for (size_t i = 0; i < sizeof(buf); i++) {
printf("%02hhx ", buf[i]);
if (i % 16 == 15) printf("\n");
}
printf("\n");
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;
}