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$ */