voxabdo.c
1#include <getopt.h>
2#include <stdarg.h>
3#include <stdlib.h>
4#include <stdio.h>
5#include <string.h>
6#include <time.h>
7
8/* Brad Contes implementation of sha256 */
9#include "sha256.h"
10
11#define SEED_LENGTH 8
12#define RAND_K1 1103515245
13#define RAND_K2 12345
14/* just a simpler way of writing putc(c, stdout) */
15#define PUT(x) (putc((x), stdout))
16
17void die(const char*, ...); /* print message and exit */
18int idx(const BYTE[], const BYTE); /* first index of byte in byte array */
19int uniq(const BYTE*); /* checks if bytes are unique in byte array */
20int msrand(int n); /* implementation of rand with support for multiple seeds */
21void seed_init(const BYTE*); /* initializes 8 seeds using a sha256 hash */
22void seed_password(const char*); /* converts a password into seeds */
23void generate_key(BYTE**); /* generates a 16 byte random key (based on seeds) */
24void encode(BYTE*[]); /* encodes input */
25void decode(BYTE*[]); /* decodes input */
26
27static const char *usage = "usage: voxabdo [-dh] [-p password] [-x key -y key]";
28static int seed[SEED_LENGTH] = { 0 };
29
30void
31die(const char *msg, ...)
32{
33 va_list arguments;
34
35 va_start(arguments, msg);
36 if (msg)
37 vfprintf(stderr, msg, arguments);
38 va_end(arguments);
39
40 exit(1);
41}
42
43int
44idx(const BYTE key[], const BYTE c)
45{
46 int i;
47
48 for (i = 0; i < 16; i++) {
49 if (key[i] == c)
50 return i;
51 }
52
53 die("index out of bounds\n");
54 return -1; /* will never be reached */
55}
56
57int
58uniq(const BYTE *str)
59{
60 int i, j;
61
62 for (i = 0; i < 16; i++) {
63 for (j = 0; j < 16; j++) {
64 if (i == j)
65 continue;
66 if (str[i] == str[j])
67 return 0;
68 }
69 }
70 return 1;
71}
72
73int
74msrand(int n)
75{
76 unsigned int next = seed[n];
77 int result;
78
79 next *= RAND_K1;
80 next += RAND_K2;
81 result = (unsigned int) (next / 65536) % 2048;
82
83 next *= RAND_K1;
84 next += RAND_K2;
85 result <<= 10;
86 result ^= (unsigned int) (next / 65536) % 1024;
87
88 next *= RAND_K1;
89 next += RAND_K2;
90 result <<= 10;
91 result ^= (unsigned int) (next / 65536) % 1024;
92
93 seed[n] = next;
94 return result;
95}
96
97void
98seed_init(const BYTE *hash)
99{
100 int i;
101
102 for (i = (SHA256_BLOCK_SIZE - 1) * 8; i >= 0; i--)
103 seed[i / 32] |= (1U & (hash[i / 8] >> (i % 8))) << (i % 32);
104}
105
106void
107seed_password(const char *password)
108{
109 BYTE buffer[SHA256_BLOCK_SIZE];
110 SHA256_CTX ctx;
111
112 sha256_init(&ctx);
113 sha256_update(&ctx, (BYTE *)password, strlen(password));
114 sha256_final(&ctx, buffer);
115 seed_init(buffer);
116}
117
118void
119generate_key(BYTE **key)
120{
121 int i, j, s;
122 BYTE c;
123
124 (*key) = (BYTE*)malloc(16 * sizeof(char));
125
126 for (i = 0, s = 0; i < 16; i++) {
127 (*key)[i] = (c = (BYTE)(msrand(s++) % 256));
128
129 /* circulate seed counter */
130 if (s == SEED_LENGTH)
131 s = 0;
132
133 /* if first byte, don't check uniqueness */
134 if (i == 0)
135 continue;
136
137 /* check if byte allready exist, and if so redo generation (i--) */
138 for (j = 0; j < i; j++) {
139 if ((*key)[j] == c) {
140 i--;
141 continue;
142 }
143 }
144 }
145}
146
147void
148encode(BYTE *key[])
149{
150 int i;
151 BYTE c, x, y;
152 BYTE *ikey[2] = {NULL};
153
154 /* randomize seed */
155 srand(time(NULL));
156 for (i = 0; i < SEED_LENGTH; i++)
157 seed[i] = rand();
158
159 /* create inner keys */
160 generate_key(&(ikey[0]));
161 generate_key(&(ikey[1]));
162
163 /* output the inner keys encoded by the outer keys */
164 for (i = 0; i < 32; i++) {
165 c = ikey[(i >= 16)][i % 16];
166 PUT(key[0][c / 16]);
167 PUT(key[1][c % 16]);
168 }
169
170 /* output input encoded first by the inner keys, then by the outer keys */
171 for (;;) {
172 c = getc(stdin);
173 if (feof(stdin))
174 break;
175 x = ikey[0][c / 16];
176 PUT(key[0][x / 16]);
177 PUT(key[1][x % 16]);
178
179 y = ikey[1][c % 16];
180 PUT(key[0][y / 16]);
181 PUT(key[1][y % 16]);
182 }
183 free(ikey[0]);
184 free(ikey[1]);
185}
186
187void
188decode(BYTE *key[])
189{
190 int i;
191 BYTE c, x, y;
192 BYTE ikey[2][16] = {0};
193
194 for (i = 0;; i++) {
195 c = getc(stdin);
196 if (feof(stdin))
197 break;
198 if (i < 64) { /* extract inner keys */
199 ikey[i >= 32][(i / 2) % 16] +=
200 (i % 2 == 0)
201 ? idx(key[0], c) * 16
202 : idx(key[1], c);
203 continue;
204 }
205 if (i == 64 && (!uniq(ikey[0]) || !uniq(ikey[1])))
206 die("keys are not unique\n");
207 switch (i % 4) { /* extract all parts of input and decode it */
208 case 0:
209 x = idx(key[0], c) * 16;
210 break;
211 case 1:
212 x += idx(key[1], c);
213 break;
214 case 2:
215 y = idx(key[0], c) * 16;
216 break;
217 case 3:
218 y += idx(key[1], c);
219 PUT(idx(ikey[0], x) * 16 + idx(ikey[1], y));
220 break;
221 }
222 }
223}
224
225int
226main(int argc, char *argv[])
227{
228 char c;
229 char *password = NULL;
230 BYTE *key[2] = {NULL};
231 int doencode = 1;
232
233 while ((c = getopt(argc, argv, "dhp:x:y:")) != -1) {
234 switch (c) {
235 case 'd':
236 doencode = 0;
237 break;
238 case 'p':
239 password = (char*)malloc((strlen(optarg) + 1) * sizeof(char));
240 memmove(password, optarg, strlen(optarg));
241 break;
242 case 'x':
243 case 'y':
244 if (strlen(optarg) != 16)
245 die("The '%c' key must contain exactly 16 bytes\n", c);
246 key[c == 'y'] = (BYTE*)malloc(16 * sizeof(char));
247 memmove(key[c == 'y'], optarg, 16);
248 if (!uniq(key[c == 'y']))
249 die("All bytes in the '%c' key must be unique\n", c);
250 break;
251 case 'h':
252 fprintf(stderr, "%s\n", usage);
253 fprintf(stderr, " -d decode input\n");
254 fprintf(stderr, " -h print this help text and exit\n");
255 fprintf(stderr, " -p PASSWORD specify a PASSWORD\n");
256 fprintf(stderr, " -x KEY specify 16 unique bytes as key\n");
257 fprintf(stderr, " -y KEY specify 16 unique bytes as key\n");
258 exit(0);
259 break;
260 default:
261 fprintf(stderr, "%s\n", usage);
262 exit(1);
263 break;
264 }
265 }
266
267 /* validate input */
268 if (password && (key[0] || key[1]))
269 die("You cannot specify both password and keys\n");
270 if (!password && !(key[0] || key[1]))
271 die("You must specify either a password or keys\n");
272 if (!password && (!key[0] || !key[1]))
273 die("You must specify both 'x' and 'y' keys\n");
274
275 if (password) {
276 seed_password(password);
277 generate_key(&(key[0]));
278 generate_key(&(key[1]));
279 free(password);
280 }
281
282 if (doencode)
283 encode(key);
284 else
285 decode(key);
286
287 free(key[0]);
288 free(key[1]);
289 return 0;
290}