I loved locking up folders as a kid on the Windows 7 computers using batch scripts I didn't understand.

This is a small program written in C to help me consolidate by understanding of `FILE` streams, bitwise operations and memory representations of data.

## What does bytelocker do?

Bytelocker takes in 2 arguments, a file and a password. It then encrypts this file in place with an ECB cipher.

## Why is this useful?

Whilst you can use this binary from the command line to encrypt a standalone file, as is the case with many `C` programs, you will find this integrates nicely within larger workflows. See below.

### My usecase.

I spend my day in vim and vimwiki. And sometimes I need to document slightly sensitive information which I don't feel comfortable leaving in plaintext. As such, I have added a binding to my vimrc calling the bytelocker utility.

The code snippet looks like: `nnoremap E :silent ! '/Users/aayushbajaj/Google Drive/2. - code/202. - c/202.6 - bytelocker/bytelocker' '%' '$bl_pass'<CR>:set noro<CR>`

here pressing capital E in normal mode will encrypt the file.

  • `silent` suppresses output
  • `!` is the execution of a shell command, which then runs the bytelocker executable from my google drive
  • `%` is a vim macro for the current file
  • and the password is retrieved from an environmental variable defined in `.zprofile`.

    • the definition looks like `export bl_pass="passwordpassword"`
    • NOTE: the password must be 16 characters!!
    • NOTE: if you choose to define the password in another file, make sure your viwrc sources the file

      • the line in .vimrc would look something like `so "~/.config/zsh/.zprofile"

### Demo ![](img/demo.gif)

tl;dr this used to be a standalone repo.

demo

/projects/csp/bytelocker/
img/demo.gif

code

makefile

CC	= clang
CFLAGS	= -Wall 

.PHONY:	all
all:	bytelocker

bytelocker:		bytelocker.c bytelocker.h main.c
	$(CC) $(CFLAGS) main.c bytelocker.c -o bytelocker

.PHONY: clean
clean:
	-rm -f bytelocker

bytelocker.h

#ifndef _BYTELOCKER_H
#define _BYTELOCKER_H

#include <stdbool.h>

#define MAX_PATH_LEN 4096
#define MAX_LINE_LEN 2048
#define RAND_STR_LEN 16
#define CIPHER_BLOCK_SIZE 16


bool test_perms(char filename[MAX_PATH_LEN]);
void encrypt_f(char *f, char *pass);
void decrypt_f(char *f, char *pass);
char *shift_encrypt(char *plaintext, char *password);
char *shift_decrypt(char *ciphertext, char *password);
char *rand_str(int seed);

#endif // _BYTELOCKER_H

bytelocker.c

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <stdint.h>
#include <string.h>
#include <sys/stat.h>
#include <unistd.h>
#include <dirent.h>

#include "bytelocker.h"

const char *const MSG_ERROR_FILE_STAT  = "Could not stat file\n";
const char *const MSG_ERROR_DIRECTORY  =
    "bytelocker does not support encrypting directories.\n";
const char *const MSG_ERROR_READ       =
    "user does not have permission to read this file.\n";
const char *const MSG_ERROR_WRITE      =
    "user does not have permission to write here.\n";

void encrypt_f(char *f, char *pass) {
	char line[16];
	int r;
	FILE *f_r = fopen(f, "rb");
	FILE *tmp_w = fopen("tmp.txt", "wb");
	
	// writing a null character at start of file to indicate encrypted
	fwrite("\x00", 1, 1, tmp_w);

	// writing encrypted characters to tmp
	while ((r = fread(line, 1, sizeof line, f_r)) != 0) {
		if (r != 16) {
			for (int i = r; i < 16; i++) {
				line[i] = '\x00';
			}
		}
		fwrite(shift_encrypt(line, pass), 1, 16, tmp_w);
	}
	fclose(tmp_w);
	fclose(f_r);
	
	// writing encrypted characters to original file
	FILE *f_w = fopen(f, "wb");
	FILE *tmp_r = fopen("tmp.txt", "rb");
	int c;
	while ((c = fgetc(tmp_r)) != EOF) fputc(c, f_w);
	fclose(f_w);
	fclose(tmp_r);
	remove("tmp.txt");
}

void decrypt_f(char *f, char *pass) {
	char line[16];
	int r;
	FILE *f_r = fopen(f, "rb");
	FILE *tmp_w = fopen("tmp.txt", "wb");
	
	// gobbling first null character
	fseek(f_r, 1, SEEK_SET);

	// writing decrypted characters to tmp
	while ((r = fread(line, 1, sizeof line, f_r)) != 0) {
		if (r != 16) {
			for (int i = r; i < 16; i++) {
				line[i] = '\x00';
			}
		}
		fwrite(shift_decrypt(line, pass), 1, 16, tmp_w);
	}
	fclose(tmp_w);
	fclose(f_r);
	
	// writing decrypted characters to original file
	FILE *f_w = fopen(f, "wb");
	FILE *tmp_r = fopen("tmp.txt", "rb");
	int c;
	while ((c = fgetc(tmp_r)) != EOF && c != '\x00') fputc(c, f_w);
	fclose(f_w);
	fclose(tmp_r);
}


bool test_perms(char filename[MAX_PATH_LEN]) {
	// 4 cases:
	// - checks if file exists
	// - must be a regular file
	// - user must be able to read file
	// - user must have premissions to write
	
	struct stat s;
	// checking if a file exists
	if (stat(filename, &s) != 0) {
		printf(MSG_ERROR_FILE_STAT);
		return false;
	}
	// checking if file is a folder
	else if (s.st_mode & S_IFDIR) {
		printf(MSG_ERROR_DIRECTORY);
		return false;
	}
	// checking if user can read file
	else if (!(s.st_mode & S_IRUSR)) {
		printf(MSG_ERROR_READ);
		return false;
	}
	else if (!(s.st_mode & S_IWUSR)) {
		printf(MSG_ERROR_WRITE);
		return false;
	}
	else return true;
}


char *shift_encrypt(char *plaintext, char *password) {
	for (int i = 0; i < 16; i++) {
		uint8_t k = plaintext[i];
		for (int j = password[i]; j > 0; j--) {
			k = k << 1 | k >> (8 - 1);
		}
		plaintext[i] = k;
	}
	return plaintext;
}

char *shift_decrypt(char *ciphertext, char *password) {
	for (int i = 0; i < 16; i++) {
		uint8_t k = ciphertext[i];
		for (int j = password[i]; j > 0; j--) {
			k = k >> 1 | k << (8 - 1);
		}
		ciphertext[i] = k;
	}
	return ciphertext;
}

// and must be freed by the caller.
// generates random string of length 16
// same seed returns same string
// string composed of upper, lower and 0 - 9
char *rand_str(int seed) {
    if (seed != 0) {
        srand(seed);
    }

    char *alpha_num_str =
            "abcdefghijklmnopqrstuvwxyz"
            "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            "0123456789";

    char *random_str = malloc(RAND_STR_LEN);

    for (int i = 0; i < RAND_STR_LEN; i++) {
        random_str[i] = alpha_num_str[rand() % (strlen(alpha_num_str) - 1)];
    }

    return random_str;
}

main.c

#include <stdio.h>
#include <stdlib.h>

#include "bytelocker.h"

int main(int argc, char **argv) {
	// argv[1] = filename
	// argv[2] = password
	if (argc != 3) {
		fprintf(stderr, "Usage: %s <file to encrypt / decrypt> <password>\n", argv[0]);
		return EXIT_FAILURE;
	}
	if (!test_perms(argv[1])) {
		fprintf(stderr, "Do not have appropriate permissions on %s\n", argv[1]);
		return EXIT_FAILURE;
	}
	
	// checking first byte of file to see if it is ascii
	FILE *f = fopen(argv[1], "r");
	char check[1];
	fread(check, 1, sizeof(check), f);
	ungetc(check[0], f);
	fclose(f);
	
	// encrypts if ascii, otherwise decrypts
	if ( check[0] > 31 && check[0] < 127 ) {
		encrypt_f(argv[1], argv[2]);
	}
	else {
		decrypt_f(argv[1], argv[2]);
	}
}