Skip to content

Commit 229f699

Browse files
committed
initial commit
0 parents  commit 229f699

File tree

11 files changed

+1269
-0
lines changed

11 files changed

+1269
-0
lines changed

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2024
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

Makefile

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
CC = gcc
2+
CFLAGS = -Wall -Wextra -O2 -I/opt/homebrew/opt/openssl@3/include
3+
LDFLAGS = -lm -lz -L/opt/homebrew/opt/openssl@3/lib -lcrypto
4+
5+
SRCS = main.c stego.c image.c png.c
6+
OBJS = $(SRCS:.c=.o)
7+
TARGET = stego
8+
TEST = test_stego
9+
10+
all: $(TARGET)
11+
12+
$(TARGET): $(OBJS)
13+
$(CC) $(OBJS) -o $(TARGET) $(LDFLAGS)
14+
15+
$(TEST): test_stego.c stego.o image.o png.c
16+
$(CC) $(CFLAGS) $^ -o $@ $(LDFLAGS)
17+
18+
test: $(TARGET) $(TEST)
19+
./$(TEST)
20+
21+
%.o: %.c
22+
$(CC) $(CFLAGS) -c $< -o $@
23+
24+
clean:
25+
rm -f $(OBJS) $(TARGET) $(TEST) test.png output.png test_data.txt extracted.txt

README.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
# Stego
2+
3+
A simple steganography tool that hides data in PNG images using LSB (Least Significant Bit) technique with optional encryption.
4+
5+
## Features
6+
7+
- Hide files or text in PNG images
8+
- Extract hidden data from images
9+
- Optional password encryption using ChaCha20-Poly1305
10+
- Efficient bit usage (1 LSB per channel across 3 pixels)
11+
- No visible artifacts in the output image
12+
13+
## Building
14+
15+
```bash
16+
make
17+
```
18+
19+
## Usage
20+
21+
Hide a file:
22+
```bash
23+
stego hide input.txt output.png -p secret
24+
```
25+
26+
Hide text directly:
27+
```bash
28+
stego hide -t "Hello World" output.png -p secret
29+
```
30+
31+
Extract data:
32+
```bash
33+
stego extract output.png extracted.txt -p secret
34+
```
35+
36+
## How it Works
37+
38+
The tool uses the least significant bit (LSB) of each RGB channel to store data. For each byte of data:
39+
- Uses 1 LSB from each channel (R,G,B) across 3 pixels
40+
- This allows for 8 bits of data to be stored in 3 pixels
41+
- The pattern is designed to minimize visible artifacts
42+
- Optional encryption using ChaCha20-Poly1305 for security
43+
44+
## License
45+
46+
MIT License - see LICENSE file for details

image.c

Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include <zlib.h>
5+
#include "image.h"
6+
#include "png.h"
7+
8+
Image* image_create(uint32_t width, uint32_t height) {
9+
Image* img = malloc(sizeof(Image));
10+
if (!img) return NULL;
11+
12+
img->width = width;
13+
img->height = height;
14+
img->data = calloc(width * height * 3, 1);
15+
if (!img->data) {
16+
free(img);
17+
return NULL;
18+
}
19+
20+
return img;
21+
}
22+
23+
Image* image_load(const char* filename) {
24+
FILE* f = fopen(filename, "rb");
25+
if (!f) return NULL;
26+
27+
// Check PNG signature
28+
unsigned char signature[8];
29+
if (fread(signature, 1, 8, f) != 8 ||
30+
signature[0] != 0x89 || signature[1] != 0x50 || signature[2] != 0x4E || signature[3] != 0x47 ||
31+
signature[4] != 0x0D || signature[5] != 0x0A || signature[6] != 0x1A || signature[7] != 0x0A) {
32+
fclose(f);
33+
return NULL;
34+
}
35+
36+
// Read IHDR chunk
37+
uint32_t length, type;
38+
if (fread(&length, 4, 1, f) != 1) {
39+
fclose(f);
40+
return NULL;
41+
}
42+
length = __builtin_bswap32(length);
43+
if (length != 13) {
44+
fclose(f);
45+
return NULL;
46+
}
47+
48+
if (fread(&type, 4, 1, f) != 1) {
49+
fclose(f);
50+
return NULL;
51+
}
52+
type = __builtin_bswap32(type);
53+
if (type != 0x49484452) { // "IHDR"
54+
fclose(f);
55+
return NULL;
56+
}
57+
58+
uint32_t width, height;
59+
uint8_t bit_depth, color_type, compression, filter, interlace;
60+
if (fread(&width, 4, 1, f) != 1 || fread(&height, 4, 1, f) != 1 ||
61+
fread(&bit_depth, 1, 1, f) != 1 || fread(&color_type, 1, 1, f) != 1 ||
62+
fread(&compression, 1, 1, f) != 1 || fread(&filter, 1, 1, f) != 1 ||
63+
fread(&interlace, 1, 1, f) != 1) {
64+
fclose(f);
65+
return NULL;
66+
}
67+
68+
width = __builtin_bswap32(width);
69+
height = __builtin_bswap32(height);
70+
71+
// Skip IHDR CRC
72+
fseek(f, 4, SEEK_CUR);
73+
74+
// Read IDAT chunk
75+
if (fread(&length, 4, 1, f) != 1) {
76+
fclose(f);
77+
return NULL;
78+
}
79+
length = __builtin_bswap32(length);
80+
81+
if (fread(&type, 4, 1, f) != 1) {
82+
fclose(f);
83+
return NULL;
84+
}
85+
type = __builtin_bswap32(type);
86+
if (type != 0x49444154) { // "IDAT"
87+
fclose(f);
88+
return NULL;
89+
}
90+
91+
// Read compressed data
92+
unsigned char* compressed = malloc(length);
93+
if (!compressed) {
94+
fclose(f);
95+
return NULL;
96+
}
97+
98+
if (fread(compressed, 1, length, f) != length) {
99+
free(compressed);
100+
fclose(f);
101+
return NULL;
102+
}
103+
104+
// Calculate uncompressed size
105+
size_t row_size = width * 3;
106+
size_t image_size = height * (row_size + 1); // +1 for filter byte per row
107+
108+
// Decompress data using zlib
109+
z_stream strm;
110+
strm.zalloc = Z_NULL;
111+
strm.zfree = Z_NULL;
112+
strm.opaque = Z_NULL;
113+
strm.avail_in = length;
114+
strm.next_in = compressed;
115+
116+
if (inflateInit(&strm) != Z_OK) {
117+
free(compressed);
118+
fclose(f);
119+
return NULL;
120+
}
121+
122+
unsigned char* image_data = malloc(image_size);
123+
if (!image_data) {
124+
inflateEnd(&strm);
125+
free(compressed);
126+
fclose(f);
127+
return NULL;
128+
}
129+
130+
strm.avail_out = image_size;
131+
strm.next_out = image_data;
132+
133+
if (inflate(&strm, Z_FINISH) != Z_STREAM_END || strm.total_out != image_size) {
134+
inflateEnd(&strm);
135+
free(compressed);
136+
free(image_data);
137+
fclose(f);
138+
return NULL;
139+
}
140+
141+
inflateEnd(&strm);
142+
free(compressed);
143+
144+
// Create image structure
145+
Image* img = malloc(sizeof(Image));
146+
if (!img) {
147+
free(image_data);
148+
fclose(f);
149+
return NULL;
150+
}
151+
152+
img->width = width;
153+
img->height = height;
154+
img->data = malloc(width * height * 3);
155+
if (!img->data) {
156+
free(image_data);
157+
free(img);
158+
fclose(f);
159+
return NULL;
160+
}
161+
162+
// Copy pixel data, skipping filter bytes
163+
for (size_t y = 0; y < height; y++) {
164+
memcpy(img->data + y * row_size,
165+
image_data + y * (row_size + 1) + 1,
166+
row_size);
167+
}
168+
169+
free(image_data);
170+
fclose(f);
171+
return img;
172+
}
173+
174+
bool image_save(const Image* img, const char* filename) {
175+
if (!img || !filename) return false;
176+
return write_png(filename, (const unsigned char*)img->data, img->width, img->height);
177+
}
178+
179+
void image_free(Image* img) {
180+
if (!img) return;
181+
free(img->data);
182+
free(img);
183+
}

image.h

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
#ifndef IMAGE_H
2+
#define IMAGE_H
3+
4+
#include <stdint.h>
5+
#include <stdbool.h>
6+
7+
typedef struct {
8+
uint32_t width;
9+
uint32_t height;
10+
unsigned char* data; // RGB format, 3 bytes per pixel
11+
} Image;
12+
13+
Image* image_load(const char* filename);
14+
bool image_save(const Image* img, const char* filename);
15+
void image_free(Image* img);
16+
Image* image_create(uint32_t width, uint32_t height);
17+
18+
#endif

main.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#include <stdio.h>
2+
#include <stdlib.h>
3+
#include <string.h>
4+
#include "stego.h"
5+
6+
void print_usage() {
7+
printf("Usage:\n");
8+
printf(" Hide file: stego hide <input_file> <output.png> [-p password]\n");
9+
printf(" Hide text: stego hide -t <text> <output.png> [-p password]\n");
10+
printf(" Extract: stego extract <image.png> <output_file> [-p password]\n");
11+
}
12+
13+
int main(int argc, char** argv) {
14+
if (argc < 4) {
15+
print_usage();
16+
return 1;
17+
}
18+
19+
const char* command = argv[1];
20+
const char* password = NULL;
21+
const char* input = NULL;
22+
bool is_text = false;
23+
24+
// Check for text flag
25+
if (strcmp(command, "hide") == 0 && strcmp(argv[2], "-t") == 0) {
26+
is_text = true;
27+
input = argv[3];
28+
} else {
29+
input = argv[2];
30+
}
31+
32+
// Check for password
33+
int start = is_text ? 4 : 4;
34+
for (int i = start; i < argc - 1; i++) {
35+
if (strcmp(argv[i], "-p") == 0) {
36+
password = argv[i + 1];
37+
break;
38+
}
39+
}
40+
41+
if (strcmp(command, "hide") == 0) {
42+
if (is_text) {
43+
// Create temporary file for text
44+
FILE* f = fopen("temp.txt", "w");
45+
if (!f) {
46+
perror("Failed to create temp file");
47+
return 1;
48+
}
49+
fprintf(f, "%s", input);
50+
fclose(f);
51+
52+
// Hide the temp file
53+
bool success = hide("temp.txt", argv[4], password);
54+
remove("temp.txt");
55+
return success ? 0 : 1;
56+
} else {
57+
return hide(input, argv[3], password) ? 0 : 1;
58+
}
59+
} else if (strcmp(command, "extract") == 0) {
60+
return extract(argv[2], argv[3], password) ? 0 : 1;
61+
} else {
62+
print_usage();
63+
return 1;
64+
}
65+
}

0 commit comments

Comments
 (0)