Project 6: File Encryption Tool
Project 6: File Encryption Tool¶
Learning Objectives¶
What you will learn through this project: - Bit operations (AND, OR, XOR, NOT, shift) - Byte-level file processing - Command-line argument handling (argc, argv) - Basic encryption principles
XOR Encryption Principle¶
XOR (Exclusive OR) Operation¶
A XOR B = C
C XOR B = A <- XOR again with same key restores original!
Example:
01100001 (a = 97)
^ 00110000 (key = 48)
-----------
01010001 (Q = 81) Encrypted
01010001 (Q = 81)
^ 00110000 (key = 48)
-----------
01100001 (a = 97) Decrypted!
Properties¶
A ^ A = 0(XOR with self = 0)A ^ 0 = A(XOR with 0 = self)(A ^ B) ^ B = A(XOR twice = original)
Step 1: Understanding Bit Operations¶
C Bit Operators¶
#include <stdio.h>
int main(void) {
unsigned char a = 0b11001010; // 202
unsigned char b = 0b10110100; // 180
printf("a = %d (0b", a);
for (int i = 7; i >= 0; i--) printf("%d", (a >> i) & 1);
printf(")\n");
printf("b = %d (0b", b);
for (int i = 7; i >= 0; i--) printf("%d", (b >> i) & 1);
printf(")\n\n");
// AND: 1 if both are 1
printf("a & b = %d\n", a & b); // 128
// OR: 1 if either is 1
printf("a | b = %d\n", a | b); // 254
// XOR: 1 if different
printf("a ^ b = %d\n", a ^ b); // 126
// NOT: bit inversion
printf("~a = %d\n", (unsigned char)~a); // 53
// Left shift: multiply by 2
printf("a << 1 = %d\n", a << 1); // 148 (overflow)
// Right shift: divide by 2
printf("a >> 1 = %d\n", a >> 1); // 101
return 0;
}
Bit Operation Truth Table¶
| A | B | AND | OR | XOR |
|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 0 |
| 0 | 1 | 0 | 1 | 1 |
| 1 | 0 | 0 | 1 | 1 |
| 1 | 1 | 1 | 1 | 0 |
Step 2: Simple XOR Encryption¶
// simple_xor.c
#include <stdio.h>
#include <string.h>
void xor_encrypt(char *data, int len, char key) {
for (int i = 0; i < len; i++) {
data[i] ^= key;
}
}
int main(void) {
char message[] = "Hello, World!";
char key = 'K'; // Simple single character key
printf("Original: %s\n", message);
// Encrypt
xor_encrypt(message, strlen(message), key);
printf("Encrypted: ");
for (int i = 0; message[i]; i++) {
printf("%02X ", (unsigned char)message[i]);
}
printf("\n");
// Decrypt (XOR again with same key)
xor_encrypt(message, strlen(message), key);
printf("Decrypted: %s\n", message);
return 0;
}
Example Output¶
Original: Hello, World!
Encrypted: 03 2E 27 27 24 67 52 18 24 31 27 2F 48
Decrypted: Hello, World!
Step 3: File Encryption Tool¶
Core Syntax: Byte-Level File Processing¶
// Byte-level read/write
FILE *fp = fopen("file.bin", "rb");
int byte;
while ((byte = fgetc(fp)) != EOF) {
// process byte
}
fclose(fp);
// Byte write
FILE *fp = fopen("file.bin", "wb");
fputc(encrypted_byte, fp);
fclose(fp);
Core Syntax: Command-Line Arguments¶
// ./program arg1 arg2
// argc = 3
// argv[0] = "./program"
// argv[1] = "arg1"
// argv[2] = "arg2"
int main(int argc, char *argv[]) {
if (argc < 2) {
printf("Usage: %s <argument>\n", argv[0]);
return 1;
}
printf("First argument: %s\n", argv[1]);
return 0;
}
File Encryption Program¶
// file_encrypt.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 4096
// Function declarations
void print_usage(const char *program_name);
int encrypt_file(const char *input_file, const char *output_file, const char *key);
int decrypt_file(const char *input_file, const char *output_file, const char *key);
void xor_buffer(unsigned char *buffer, int len, const char *key, int key_len);
int main(int argc, char *argv[]) {
if (argc < 5) {
print_usage(argv[0]);
return 1;
}
const char *mode = argv[1];
const char *input_file = argv[2];
const char *output_file = argv[3];
const char *key = argv[4];
if (strlen(key) == 0) {
fprintf(stderr, "Error: Key cannot be empty\n");
return 1;
}
int result;
if (strcmp(mode, "-e") == 0 || strcmp(mode, "--encrypt") == 0) {
result = encrypt_file(input_file, output_file, key);
if (result == 0) {
printf("Encryption successful: %s -> %s\n", input_file, output_file);
}
} else if (strcmp(mode, "-d") == 0 || strcmp(mode, "--decrypt") == 0) {
result = decrypt_file(input_file, output_file, key);
if (result == 0) {
printf("Decryption successful: %s -> %s\n", input_file, output_file);
}
} else {
fprintf(stderr, "Error: Unknown mode '%s'\n", mode);
print_usage(argv[0]);
return 1;
}
return result;
}
void print_usage(const char *program_name) {
printf("File Encryption Tool (XOR)\n\n");
printf("Usage:\n");
printf(" %s -e <input> <output> <key> Encrypt file\n", program_name);
printf(" %s -d <input> <output> <key> Decrypt file\n", program_name);
printf("\nOptions:\n");
printf(" -e, --encrypt Encrypt mode\n");
printf(" -d, --decrypt Decrypt mode\n");
printf("\nExample:\n");
printf(" %s -e secret.txt secret.enc mypassword\n", program_name);
printf(" %s -d secret.enc secret.txt mypassword\n", program_name);
}
void xor_buffer(unsigned char *buffer, int len, const char *key, int key_len) {
for (int i = 0; i < len; i++) {
buffer[i] ^= key[i % key_len];
}
}
int encrypt_file(const char *input_file, const char *output_file, const char *key) {
FILE *fin = fopen(input_file, "rb");
if (fin == NULL) {
perror("Error opening input file");
return 1;
}
FILE *fout = fopen(output_file, "wb");
if (fout == NULL) {
perror("Error opening output file");
fclose(fin);
return 1;
}
unsigned char buffer[BUFFER_SIZE];
int key_len = strlen(key);
size_t bytes_read;
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, fin)) > 0) {
xor_buffer(buffer, bytes_read, key, key_len);
fwrite(buffer, 1, bytes_read, fout);
}
fclose(fin);
fclose(fout);
return 0;
}
int decrypt_file(const char *input_file, const char *output_file, const char *key) {
// XOR encryption and decryption are identical
return encrypt_file(input_file, output_file, key);
}
Step 4: Improved Version (With Header)¶
Encrypted File Format¶
+-----------------------------------------+
| File Header |
+-----------------------------------------+
| Magic Number (4 bytes): "XENC" |
| Version (1 byte): 1 |
| Key Hash (4 bytes): for verification |
| Original Size (8 bytes): original size |
+-----------------------------------------+
| Encrypted Data |
+-----------------------------------------+
Improved Code¶
// file_encrypt_v2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#define MAGIC "XENC"
#define VERSION 1
#define BUFFER_SIZE 4096
#define HEADER_SIZE 17
// File header struct
typedef struct {
char magic[4];
uint8_t version;
uint32_t key_hash;
uint64_t original_size;
} FileHeader;
// Simple hash function (djb2)
uint32_t hash_key(const char *key) {
uint32_t hash = 5381;
int c;
while ((c = *key++)) {
hash = ((hash << 5) + hash) + c;
}
return hash;
}
void print_usage(const char *name) {
printf("Enhanced File Encryption Tool v2\n\n");
printf("Usage:\n");
printf(" %s encrypt <input> <output> <password>\n", name);
printf(" %s decrypt <input> <output> <password>\n", name);
printf(" %s info <encrypted_file>\n", name);
}
void xor_buffer(unsigned char *buf, size_t len, const char *key, size_t key_len, size_t *pos) {
for (size_t i = 0; i < len; i++) {
buf[i] ^= key[*pos % key_len];
(*pos)++;
}
}
int encrypt_file(const char *input, const char *output, const char *key) {
FILE *fin = fopen(input, "rb");
if (!fin) {
perror("Error opening input file");
return 1;
}
// Get original file size
fseek(fin, 0, SEEK_END);
uint64_t file_size = ftell(fin);
fseek(fin, 0, SEEK_SET);
FILE *fout = fopen(output, "wb");
if (!fout) {
perror("Error opening output file");
fclose(fin);
return 1;
}
// Write header
FileHeader header;
memcpy(header.magic, MAGIC, 4);
header.version = VERSION;
header.key_hash = hash_key(key);
header.original_size = file_size;
fwrite(&header, sizeof(FileHeader), 1, fout);
// Encrypt data
unsigned char buffer[BUFFER_SIZE];
size_t bytes_read;
size_t key_len = strlen(key);
size_t key_pos = 0;
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, fin)) > 0) {
xor_buffer(buffer, bytes_read, key, key_len, &key_pos);
fwrite(buffer, 1, bytes_read, fout);
}
fclose(fin);
fclose(fout);
printf("Encrypted: %s -> %s\n", input, output);
printf("Original size: %llu bytes\n", (unsigned long long)file_size);
return 0;
}
int decrypt_file(const char *input, const char *output, const char *key) {
FILE *fin = fopen(input, "rb");
if (!fin) {
perror("Error opening input file");
return 1;
}
// Read header
FileHeader header;
if (fread(&header, sizeof(FileHeader), 1, fin) != 1) {
fprintf(stderr, "Error: Invalid encrypted file\n");
fclose(fin);
return 1;
}
// Verify magic number
if (memcmp(header.magic, MAGIC, 4) != 0) {
fprintf(stderr, "Error: Not a valid encrypted file\n");
fclose(fin);
return 1;
}
// Verify key
if (header.key_hash != hash_key(key)) {
fprintf(stderr, "Error: Wrong password\n");
fclose(fin);
return 1;
}
FILE *fout = fopen(output, "wb");
if (!fout) {
perror("Error opening output file");
fclose(fin);
return 1;
}
// Decrypt data
unsigned char buffer[BUFFER_SIZE];
size_t bytes_read;
size_t key_len = strlen(key);
size_t key_pos = 0;
while ((bytes_read = fread(buffer, 1, BUFFER_SIZE, fin)) > 0) {
xor_buffer(buffer, bytes_read, key, key_len, &key_pos);
fwrite(buffer, 1, bytes_read, fout);
}
fclose(fin);
fclose(fout);
printf("Decrypted: %s -> %s\n", input, output);
printf("Original size: %llu bytes\n", (unsigned long long)header.original_size);
return 0;
}
int show_info(const char *filename) {
FILE *fp = fopen(filename, "rb");
if (!fp) {
perror("Error opening file");
return 1;
}
FileHeader header;
if (fread(&header, sizeof(FileHeader), 1, fp) != 1) {
fprintf(stderr, "Error: Cannot read header\n");
fclose(fp);
return 1;
}
fclose(fp);
if (memcmp(header.magic, MAGIC, 4) != 0) {
printf("Not an encrypted file (no XENC magic)\n");
return 1;
}
printf("=== Encrypted File Info ===\n");
printf("Magic: %.4s\n", header.magic);
printf("Version: %d\n", header.version);
printf("Key Hash: 0x%08X\n", header.key_hash);
printf("Original Size: %llu bytes\n", (unsigned long long)header.original_size);
return 0;
}
int main(int argc, char *argv[]) {
if (argc < 2) {
print_usage(argv[0]);
return 1;
}
if (strcmp(argv[1], "encrypt") == 0) {
if (argc < 5) {
print_usage(argv[0]);
return 1;
}
return encrypt_file(argv[2], argv[3], argv[4]);
}
else if (strcmp(argv[1], "decrypt") == 0) {
if (argc < 5) {
print_usage(argv[0]);
return 1;
}
return decrypt_file(argv[2], argv[3], argv[4]);
}
else if (strcmp(argv[1], "info") == 0) {
if (argc < 3) {
print_usage(argv[0]);
return 1;
}
return show_info(argv[2]);
}
else {
print_usage(argv[0]);
return 1;
}
return 0;
}
Compile and Run¶
# Compile
gcc -Wall -Wextra -std=c11 file_encrypt_v2.c -o encrypt
# Create test file
echo "This is a secret message!" > secret.txt
# Encrypt
./encrypt encrypt secret.txt secret.enc mypassword
# View file info
./encrypt info secret.enc
# Decrypt
./encrypt decrypt secret.enc decrypted.txt mypassword
# Verify
cat decrypted.txt
# Try with wrong password
./encrypt decrypt secret.enc fail.txt wrongpassword
# Error: Wrong password
Example Output¶
$ ./encrypt encrypt secret.txt secret.enc mypassword
Encrypted: secret.txt -> secret.enc
Original size: 27 bytes
$ ./encrypt info secret.enc
=== Encrypted File Info ===
Magic: XENC
Version: 1
Key Hash: 0x7C9E6D5A
Original Size: 27 bytes
$ ./encrypt decrypt secret.enc decrypted.txt mypassword
Decrypted: secret.enc -> decrypted.txt
Original size: 27 bytes
$ cat decrypted.txt
This is a secret message!
Summary¶
| Concept | Description |
|---|---|
^ (XOR) |
Bit XOR operation |
& (AND) |
Bit AND operation |
\| (OR) |
Bit OR operation |
~ (NOT) |
Bit inversion |
<<, >> |
Bit shift |
fgetc, fputc |
Byte-level file I/O |
argc, argv |
Command-line arguments |
Warning¶
Security Warning: Use XOR encryption for learning purposes only! - Pattern exposure when same key is reused - Vulnerable to known plaintext attacks - Use AES, RSA, etc. for real security
Exercises¶
-
Progress display: Show progress bar for large file processing
-
Compress then encrypt: Compress with zlib before encrypting
-
Directory processing: Batch encrypt all files in a folder
-
Encryption algorithm selection: Add other simple encryption options besides XOR
Learning Complete!¶
You have completed the project-based C language learning.
Projects Summary¶
| Project | Core Concepts |
|---|---|
| Calculator | Functions, switch, input handling |
| Number Guessing | Loops, random numbers, game logic |
| Address Book | Structs, file I/O |
| Dynamic Array | malloc, realloc, free |
| Linked List | Pointers, data structures |
| File Encryption | Bit operations, byte processing |
Next Steps¶
- Advanced Data Structures: Trees, hash tables, graphs
- Algorithms: Sorting, searching, recursion
- Systems Programming: Processes, threads, sockets
- Embedded C: Microcontroller programming