Mercurial > dropbear
comparison libtomcrypt/demos/openssl-enc.c @ 1471:6dba84798cd5
Update to libtomcrypt 1.18.1, merged with Dropbear changes
author | Matt Johnston <matt@ucc.asn.au> |
---|---|
date | Fri, 09 Feb 2018 21:44:05 +0800 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
1470:8bba51a55704 | 1471:6dba84798cd5 |
---|---|
1 /* LibTomCrypt, modular cryptographic library -- Tom St Denis | |
2 * | |
3 * LibTomCrypt is a library that provides various cryptographic | |
4 * algorithms in a highly modular and flexible manner. | |
5 * | |
6 * The library is free for all purposes without any express | |
7 * guarantee it works. | |
8 */ | |
9 | |
10 /* | |
11 * Demo to do the rough equivalent of: | |
12 * | |
13 * openssl enc -aes-256-cbc -pass pass:foobar -in infile -out outfile -p | |
14 * | |
15 * Compilation: | |
16 * | |
17 * $(CC) -I /path/to/headers -L .../libs \ | |
18 * -o openssl-enc \ | |
19 * openssl-enc.c -ltomcrypt | |
20 * | |
21 * Usage: | |
22 * | |
23 * ./openssl-enc <enc|dec> infile outfile "passphrase" [salt] | |
24 * | |
25 * If provided, the salt must be EXACTLY a 16-char hex string. | |
26 * | |
27 * Demo is an example of: | |
28 * | |
29 * - (When decrypting) yanking salt out of the OpenSSL "Salted__..." header | |
30 * - OpenSSL-compatible key derivation (in OpenSSL's modified PKCS#5v1 approach) | |
31 * - Grabbing an Initialization Vector from the key generator | |
32 * - Performing simple block encryption using AES | |
33 * - PKCS#7-type padding (which hopefully can get ripped out of this demo and | |
34 * made a libtomcrypt thing someday). | |
35 * | |
36 * This program is free for all purposes without any express guarantee it | |
37 * works. If you really want to see a license here, assume the WTFPL :-) | |
38 * | |
39 * BJ Black, [email protected], https://wjblack.com | |
40 * | |
41 * BUGS: | |
42 * Passing a password on a command line is a HORRIBLE idea. Don't use | |
43 * this program for serious work! | |
44 */ | |
45 | |
46 #include <tomcrypt.h> | |
47 | |
48 #ifndef LTC_RIJNDAEL | |
49 #error Cannot compile this demo; Rijndael (AES) required | |
50 #endif | |
51 #ifndef LTC_CBC_MODE | |
52 #error Cannot compile this demo; CBC mode required | |
53 #endif | |
54 #ifndef LTC_PKCS_5 | |
55 #error Cannot compile this demo; PKCS5 required | |
56 #endif | |
57 #ifndef LTC_RNG_GET_BYTES | |
58 #error Cannot compile this demo; random generator required | |
59 #endif | |
60 #ifndef LTC_MD5 | |
61 #error Cannot compile this demo; MD5 required | |
62 #endif | |
63 | |
64 /* OpenSSL by default only runs one hash round */ | |
65 #define OPENSSL_ITERATIONS 1 | |
66 /* Use aes-256-cbc, so 256 bits of key, 128 of IV */ | |
67 #define KEY_LENGTH (256>>3) | |
68 #define IV_LENGTH (128>>3) | |
69 /* PKCS#5v1 requires exactly an 8-byte salt */ | |
70 #define SALT_LENGTH 8 | |
71 /* The header OpenSSL puts on an encrypted file */ | |
72 static char salt_header[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' }; | |
73 | |
74 #include <errno.h> | |
75 #include <stdio.h> | |
76 #include <string.h> | |
77 | |
78 /* A simple way to handle the possibility that a block may increase in size | |
79 after padding. */ | |
80 union paddable { | |
81 unsigned char unpad[1024]; | |
82 unsigned char pad[1024+MAXBLOCKSIZE]; | |
83 }; | |
84 | |
85 /* | |
86 * Print usage and exit with a bad status (and perror() if any errno). | |
87 * | |
88 * Input: argv[0] and the error string | |
89 * Output: <no return> | |
90 * Side Effects: print messages and barf (does exit(3)) | |
91 */ | |
92 void barf(const char *pname, const char *err) | |
93 { | |
94 printf("Usage: %s <enc|dec> infile outfile passphrase [salt]\n", pname); | |
95 printf("\n"); | |
96 printf(" # encrypts infile->outfile, random salt\n"); | |
97 printf(" %s enc infile outfile \"passphrase\"\n", pname); | |
98 printf("\n"); | |
99 printf(" # encrypts infile->outfile, salt from cmdline\n"); | |
100 printf(" %s enc infile outfile pass 0123456789abcdef\n", pname); | |
101 printf("\n"); | |
102 printf(" # decrypts infile->outfile, pulls salt from infile\n"); | |
103 printf(" %s dec infile outfile pass\n", pname); | |
104 printf("\n"); | |
105 printf(" # decrypts infile->outfile, salt specified\n"); | |
106 printf(" # (don't try to read the salt from infile)\n"); | |
107 printf(" %s dec infile outfile pass 0123456789abcdef" | |
108 "\n", pname); | |
109 printf("\n"); | |
110 printf("Application Error: %s\n", err); | |
111 if(errno) | |
112 perror(" System Error"); | |
113 exit(-1); | |
114 } | |
115 | |
116 /* | |
117 * Parse a salt value passed in on the cmdline. | |
118 * | |
119 * Input: string passed in and a buf to put it in (exactly 8 bytes!) | |
120 * Output: CRYPT_OK if parsed OK, CRYPT_ERROR if not | |
121 * Side Effects: none | |
122 */ | |
123 int parse_hex_salt(unsigned char *in, unsigned char *out) | |
124 { | |
125 int idx; | |
126 for(idx=0; idx<SALT_LENGTH; idx++) | |
127 if(sscanf((char*)in+idx*2, "%02hhx", out+idx) != 1) | |
128 return CRYPT_ERROR; | |
129 return CRYPT_OK; | |
130 } | |
131 | |
132 /* | |
133 * Parse the Salted__[+8 bytes] from an OpenSSL-compatible file header. | |
134 * | |
135 * Input: file to read from and a to put the salt in (exactly 8 bytes!) | |
136 * Output: CRYPT_OK if parsed OK, CRYPT_ERROR if not | |
137 * Side Effects: infile's read pointer += 16 | |
138 */ | |
139 int parse_openssl_header(FILE *in, unsigned char *out) | |
140 { | |
141 unsigned char tmp[SALT_LENGTH]; | |
142 if(fread(tmp, 1, sizeof(tmp), in) != sizeof(tmp)) | |
143 return CRYPT_ERROR; | |
144 if(memcmp(tmp, salt_header, sizeof(tmp))) | |
145 return CRYPT_ERROR; | |
146 if(fread(tmp, 1, sizeof(tmp), in) != sizeof(tmp)) | |
147 return CRYPT_ERROR; | |
148 memcpy(out, tmp, sizeof(tmp)); | |
149 return CRYPT_OK; | |
150 } | |
151 | |
152 /* | |
153 * Dump a hexed stream of bytes (convenience func). | |
154 * | |
155 * Input: buf to read from, length | |
156 * Output: none | |
157 * Side Effects: bytes printed as a hex blob, no lf at the end | |
158 */ | |
159 void dump_bytes(unsigned char *in, unsigned long len) | |
160 { | |
161 unsigned long idx; | |
162 for(idx=0; idx<len; idx++) | |
163 printf("%02hhX", *(in+idx)); | |
164 } | |
165 | |
166 /* | |
167 * Pad or unpad a message using PKCS#7 padding. | |
168 * Padding will add 1-(blocksize) bytes and unpadding will remove that amount. | |
169 * Set is_padding to 1 to pad, 0 to unpad. | |
170 * | |
171 * Input: paddable buffer, size read, block length of cipher, mode | |
172 * Output: number of bytes after padding resp. after unpadding | |
173 * Side Effects: none | |
174 */ | |
175 size_t pkcs7_pad(union paddable *buf, size_t nb, int block_length, | |
176 int is_padding) | |
177 { | |
178 unsigned char padval; | |
179 off_t idx; | |
180 | |
181 if(is_padding) { | |
182 /* We are PADDING this block (and therefore adding bytes) */ | |
183 /* The pad value in PKCS#7 is the number of bytes remaining in | |
184 the block, so for a 16-byte block and 3 bytes left, it's | |
185 0x030303. In the oddball case where nb is an exact multiple | |
186 multiple of block_length, set the padval to blocksize (i.e. | |
187 add one full block) */ | |
188 padval = (unsigned char) (block_length - (nb % block_length)); | |
189 padval = padval ? padval : block_length; | |
190 | |
191 memset(buf->pad+nb, padval, padval); | |
192 return nb+padval; | |
193 } else { | |
194 /* We are UNPADDING this block (and removing bytes) | |
195 We really just need to verify that the pad bytes are correct, | |
196 so start at the end of the string and work backwards. */ | |
197 | |
198 /* Figure out what the padlength should be by looking at the | |
199 last byte */ | |
200 idx = nb-1; | |
201 padval = buf->pad[idx]; | |
202 | |
203 /* padval must be nonzero and <= block length */ | |
204 if(padval <= 0 || padval > block_length) | |
205 return 0; | |
206 | |
207 /* First byte's accounted for; do the rest */ | |
208 idx--; | |
209 | |
210 while(idx >= (off_t)(nb-padval)) | |
211 if(buf->pad[idx] != padval) | |
212 return 0; | |
213 else | |
214 idx--; | |
215 | |
216 /* If we got here, the pad checked out, so return a smaller | |
217 number of bytes than nb (basically where we left off+1) */ | |
218 return idx+1; | |
219 } | |
220 } | |
221 | |
222 /* | |
223 * Perform an encrypt/decrypt operation to/from files using AES+CBC+PKCS7 pad. | |
224 * Set encrypt to 1 to encrypt, 0 to decrypt. | |
225 * | |
226 * Input: in/out files, key, iv, and mode | |
227 * Output: CRYPT_OK if no error | |
228 * Side Effects: bytes slurped from infile, pushed to outfile, fds updated. | |
229 */ | |
230 int do_crypt(FILE *infd, FILE *outfd, unsigned char *key, unsigned char *iv, | |
231 int encrypt) | |
232 { | |
233 union paddable inbuf, outbuf; | |
234 int cipher, ret; | |
235 symmetric_CBC cbc; | |
236 size_t nb; | |
237 | |
238 /* Register your cipher! */ | |
239 cipher = register_cipher(&aes_desc); | |
240 if(cipher == -1) | |
241 return CRYPT_INVALID_CIPHER; | |
242 | |
243 /* Start a CBC session with cipher/key/val params */ | |
244 ret = cbc_start(cipher, iv, key, KEY_LENGTH, 0, &cbc); | |
245 if( ret != CRYPT_OK ) | |
246 return -1; | |
247 | |
248 do { | |
249 /* Get bytes from the source */ | |
250 nb = fread(inbuf.unpad, 1, sizeof(inbuf.unpad), infd); | |
251 if(!nb) | |
252 return encrypt ? CRYPT_OK : CRYPT_ERROR; | |
253 | |
254 /* Barf if we got a read error */ | |
255 if(ferror(infd)) | |
256 return CRYPT_ERROR; | |
257 | |
258 if(encrypt) { | |
259 /* We're encrypting, so pad first (if at EOF) and then | |
260 crypt */ | |
261 if(feof(infd)) | |
262 nb = pkcs7_pad(&inbuf, nb, | |
263 aes_desc.block_length, 1); | |
264 | |
265 ret = cbc_encrypt(inbuf.pad, outbuf.pad, nb, &cbc); | |
266 if(ret != CRYPT_OK) | |
267 return ret; | |
268 | |
269 } else { | |
270 /* We're decrypting, so decrypt and then unpad if at | |
271 EOF */ | |
272 ret = cbc_decrypt(inbuf.unpad, outbuf.unpad, nb, &cbc); | |
273 if( ret != CRYPT_OK ) | |
274 return ret; | |
275 | |
276 if( feof(infd) ) | |
277 nb = pkcs7_pad(&outbuf, nb, | |
278 aes_desc.block_length, 0); | |
279 if(nb == 0) | |
280 /* The file didn't decrypt correctly */ | |
281 return CRYPT_ERROR; | |
282 | |
283 } | |
284 | |
285 /* Push bytes to outfile */ | |
286 if(fwrite(outbuf.unpad, 1, nb, outfd) != nb) | |
287 return CRYPT_ERROR; | |
288 | |
289 } while(!feof(infd)); | |
290 | |
291 /* Close up */ | |
292 cbc_done(&cbc); | |
293 | |
294 return CRYPT_OK; | |
295 } | |
296 | |
297 /* Convenience macro for the various barfable places below */ | |
298 #define BARF(a) { \ | |
299 if(infd) fclose(infd); \ | |
300 if(outfd) { fclose(outfd); remove(argv[3]); } \ | |
301 barf(argv[0], a); \ | |
302 } | |
303 /* | |
304 * The main routine. Mostly validate cmdline params, open files, run the KDF, | |
305 * and do the crypt. | |
306 */ | |
307 int main(int argc, char *argv[]) { | |
308 unsigned char salt[SALT_LENGTH]; | |
309 FILE *infd = NULL, *outfd = NULL; | |
310 int encrypt = -1; | |
311 int hash = -1; | |
312 int ret; | |
313 unsigned char keyiv[KEY_LENGTH + IV_LENGTH]; | |
314 unsigned long keyivlen = (KEY_LENGTH + IV_LENGTH); | |
315 unsigned char *key, *iv; | |
316 | |
317 /* Check proper number of cmdline args */ | |
318 if(argc < 5 || argc > 6) | |
319 BARF("Invalid number of arguments"); | |
320 | |
321 /* Check proper mode of operation */ | |
322 if (!strncmp(argv[1], "enc", 3)) | |
323 encrypt = 1; | |
324 else if(!strncmp(argv[1], "dec", 3)) | |
325 encrypt = 0; | |
326 else | |
327 BARF("Bad command name"); | |
328 | |
329 /* Check we can open infile/outfile */ | |
330 infd = fopen(argv[2], "rb"); | |
331 if(infd == NULL) | |
332 BARF("Could not open infile"); | |
333 outfd = fopen(argv[3], "wb"); | |
334 if(outfd == NULL) | |
335 BARF("Could not open outfile"); | |
336 | |
337 /* Get the salt from wherever */ | |
338 if(argc == 6) { | |
339 /* User-provided */ | |
340 if(parse_hex_salt((unsigned char*) argv[5], salt) != CRYPT_OK) | |
341 BARF("Bad user-specified salt"); | |
342 } else if(!strncmp(argv[1], "enc", 3)) { | |
343 /* Encrypting; get from RNG */ | |
344 if(rng_get_bytes(salt, sizeof(salt), NULL) != sizeof(salt)) | |
345 BARF("Not enough random data"); | |
346 } else { | |
347 /* Parse from infile (decrypt only) */ | |
348 if(parse_openssl_header(infd, salt) != CRYPT_OK) | |
349 BARF("Invalid OpenSSL header in infile"); | |
350 } | |
351 | |
352 /* Fetch the MD5 hasher for PKCS#5 */ | |
353 hash = register_hash(&md5_desc); | |
354 if(hash == -1) | |
355 BARF("Could not register MD5 hash"); | |
356 | |
357 /* Set things to a sane initial state */ | |
358 zeromem(keyiv, sizeof(keyiv)); | |
359 key = keyiv + 0; /* key comes first */ | |
360 iv = keyiv + KEY_LENGTH; /* iv comes next */ | |
361 | |
362 /* Run the key derivation from the provided passphrase. This gets us | |
363 the key and iv. */ | |
364 ret = pkcs_5_alg1_openssl((unsigned char*)argv[4], strlen(argv[4]), salt, | |
365 OPENSSL_ITERATIONS, hash, keyiv, &keyivlen ); | |
366 if(ret != CRYPT_OK) | |
367 BARF("Could not derive key/iv from passphrase"); | |
368 | |
369 /* Display the salt/key/iv like OpenSSL cmdline does when -p */ | |
370 printf("salt="); dump_bytes(salt, sizeof(salt)); printf("\n"); | |
371 printf("key="); dump_bytes(key, KEY_LENGTH); printf("\n"); | |
372 printf("iv ="); dump_bytes(iv, IV_LENGTH ); printf("\n"); | |
373 | |
374 /* If we're encrypting, write the salt header as OpenSSL does */ | |
375 if(!strncmp(argv[1], "enc", 3)) { | |
376 if(fwrite(salt_header, 1, sizeof(salt_header), outfd) != | |
377 sizeof(salt_header) ) | |
378 BARF("Error writing salt header to outfile"); | |
379 if(fwrite(salt, 1, sizeof(salt), outfd) != sizeof(salt)) | |
380 BARF("Error writing salt to outfile"); | |
381 } | |
382 | |
383 /* At this point, the files are open, the salt has been figured out, | |
384 and we're ready to pump data through crypt. */ | |
385 | |
386 /* Do the crypt operation */ | |
387 if(do_crypt(infd, outfd, key, iv, encrypt) != CRYPT_OK) | |
388 BARF("Error during crypt operation"); | |
389 | |
390 /* Clean up */ | |
391 fclose(infd); fclose(outfd); | |
392 return 0; | |
393 } | |
394 | |
395 /* ref: $Format:%D$ */ | |
396 /* git commit: $Format:%H$ */ | |
397 /* commit time: $Format:%ai$ */ |