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, bblack@barracuda.com, 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  */
barf(const char * pname,const char * err)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  */
parse_hex_salt(unsigned char * in,unsigned char * out)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  */
parse_openssl_header(FILE * in,unsigned char * out)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  */
dump_bytes(unsigned char * in,unsigned long len)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  */
pkcs7_pad(union paddable * buf,size_t nb,int block_length,int is_padding)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  */
do_crypt(FILE * infd,FILE * outfd,unsigned char * key,unsigned char * iv,int encrypt)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  */
main(int argc,char * argv[])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:         HEAD -> master, tag: v1.18.2 */
396 /* git commit:  7e7eb695d581782f04b24dc444cbfde86af59853 */
397 /* commit time: 2018-07-01 22:49:01 +0200 */
398