Bytelocker in C
I loved locking up folders as a kid on the Windows 7 computers using batch scripts I didn’t understand.
# Welcome to the bytelocker!
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

tl;dr this used to be a standalone repo.
demo

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]);
}
}
Backlinks (3)
1. LAN Messenger /wiki/csp/lan-messenger/
“tessenger” is my unsw networks assignment: a multi-client chat server in raw c — server.c (583 lines) and client.c (253), no libraries beyond pthreads and the bsd socket api (Kernighan, Brian W. and Ritchie, Dennis M., 1988). it does authentication with timed lockout, private and group messaging, presence, audit logs, and a udp side channel for peer-to-peer file transfer. this page walks the architecture, weaves in the socket theory the assignment was designed to teach, and then does something the assignment never required: an honest concurrency audit of my own submission, with the bugs demonstrated live.
𐃏
2. Wiki /wiki/
Knowledge is a paradox. The more one understand, the more one realises the vastness of his ignorance.