123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397 |
- /* LibTomCrypt, modular cryptographic library -- Tom St Denis
- *
- * LibTomCrypt is a library that provides various cryptographic
- * algorithms in a highly modular and flexible manner.
- *
- * The library is free for all purposes without any express
- * guarantee it works.
- */
- /*
- * Demo to do the rough equivalent of:
- *
- * openssl enc -aes-256-cbc -pass pass:foobar -in infile -out outfile -p
- *
- * Compilation:
- *
- * $(CC) -I /path/to/headers -L .../libs \
- * -o openssl-enc \
- * openssl-enc.c -ltomcrypt
- *
- * Usage:
- *
- * ./openssl-enc <enc|dec> infile outfile "passphrase" [salt]
- *
- * If provided, the salt must be EXACTLY a 16-char hex string.
- *
- * Demo is an example of:
- *
- * - (When decrypting) yanking salt out of the OpenSSL "Salted__..." header
- * - OpenSSL-compatible key derivation (in OpenSSL's modified PKCS#5v1 approach)
- * - Grabbing an Initialization Vector from the key generator
- * - Performing simple block encryption using AES
- * - PKCS#7-type padding (which hopefully can get ripped out of this demo and
- * made a libtomcrypt thing someday).
- *
- * This program is free for all purposes without any express guarantee it
- * works. If you really want to see a license here, assume the WTFPL :-)
- *
- * BJ Black, bblack@barracuda.com, https://wjblack.com
- *
- * BUGS:
- * Passing a password on a command line is a HORRIBLE idea. Don't use
- * this program for serious work!
- */
- #include <tomcrypt.h>
- #ifndef LTC_RIJNDAEL
- #error Cannot compile this demo; Rijndael (AES) required
- #endif
- #ifndef LTC_CBC_MODE
- #error Cannot compile this demo; CBC mode required
- #endif
- #ifndef LTC_PKCS_5
- #error Cannot compile this demo; PKCS5 required
- #endif
- #ifndef LTC_RNG_GET_BYTES
- #error Cannot compile this demo; random generator required
- #endif
- #ifndef LTC_MD5
- #error Cannot compile this demo; MD5 required
- #endif
- /* OpenSSL by default only runs one hash round */
- #define OPENSSL_ITERATIONS 1
- /* Use aes-256-cbc, so 256 bits of key, 128 of IV */
- #define KEY_LENGTH (256>>3)
- #define IV_LENGTH (128>>3)
- /* PKCS#5v1 requires exactly an 8-byte salt */
- #define SALT_LENGTH 8
- /* The header OpenSSL puts on an encrypted file */
- static char salt_header[] = { 'S', 'a', 'l', 't', 'e', 'd', '_', '_' };
- #include <errno.h>
- #include <stdio.h>
- #include <string.h>
- /* A simple way to handle the possibility that a block may increase in size
- after padding. */
- union paddable {
- unsigned char unpad[1024];
- unsigned char pad[1024+MAXBLOCKSIZE];
- };
- /*
- * Print usage and exit with a bad status (and perror() if any errno).
- *
- * Input: argv[0] and the error string
- * Output: <no return>
- * Side Effects: print messages and barf (does exit(3))
- */
- void barf(const char *pname, const char *err)
- {
- printf("Usage: %s <enc|dec> infile outfile passphrase [salt]\n", pname);
- printf("\n");
- printf(" # encrypts infile->outfile, random salt\n");
- printf(" %s enc infile outfile \"passphrase\"\n", pname);
- printf("\n");
- printf(" # encrypts infile->outfile, salt from cmdline\n");
- printf(" %s enc infile outfile pass 0123456789abcdef\n", pname);
- printf("\n");
- printf(" # decrypts infile->outfile, pulls salt from infile\n");
- printf(" %s dec infile outfile pass\n", pname);
- printf("\n");
- printf(" # decrypts infile->outfile, salt specified\n");
- printf(" # (don't try to read the salt from infile)\n");
- printf(" %s dec infile outfile pass 0123456789abcdef"
- "\n", pname);
- printf("\n");
- printf("Application Error: %s\n", err);
- if(errno)
- perror(" System Error");
- exit(-1);
- }
- /*
- * Parse a salt value passed in on the cmdline.
- *
- * Input: string passed in and a buf to put it in (exactly 8 bytes!)
- * Output: CRYPT_OK if parsed OK, CRYPT_ERROR if not
- * Side Effects: none
- */
- int parse_hex_salt(unsigned char *in, unsigned char *out)
- {
- int idx;
- for(idx=0; idx<SALT_LENGTH; idx++)
- if(sscanf((char*)in+idx*2, "%02hhx", out+idx) != 1)
- return CRYPT_ERROR;
- return CRYPT_OK;
- }
- /*
- * Parse the Salted__[+8 bytes] from an OpenSSL-compatible file header.
- *
- * Input: file to read from and a to put the salt in (exactly 8 bytes!)
- * Output: CRYPT_OK if parsed OK, CRYPT_ERROR if not
- * Side Effects: infile's read pointer += 16
- */
- int parse_openssl_header(FILE *in, unsigned char *out)
- {
- unsigned char tmp[SALT_LENGTH];
- if(fread(tmp, 1, sizeof(tmp), in) != sizeof(tmp))
- return CRYPT_ERROR;
- if(memcmp(tmp, salt_header, sizeof(tmp)))
- return CRYPT_ERROR;
- if(fread(tmp, 1, sizeof(tmp), in) != sizeof(tmp))
- return CRYPT_ERROR;
- memcpy(out, tmp, sizeof(tmp));
- return CRYPT_OK;
- }
- /*
- * Dump a hexed stream of bytes (convenience func).
- *
- * Input: buf to read from, length
- * Output: none
- * Side Effects: bytes printed as a hex blob, no lf at the end
- */
- void dump_bytes(unsigned char *in, unsigned long len)
- {
- unsigned long idx;
- for(idx=0; idx<len; idx++)
- printf("%02hhX", *(in+idx));
- }
- /*
- * Pad or unpad a message using PKCS#7 padding.
- * Padding will add 1-(blocksize) bytes and unpadding will remove that amount.
- * Set is_padding to 1 to pad, 0 to unpad.
- *
- * Input: paddable buffer, size read, block length of cipher, mode
- * Output: number of bytes after padding resp. after unpadding
- * Side Effects: none
- */
- size_t pkcs7_pad(union paddable *buf, size_t nb, int block_length,
- int is_padding)
- {
- unsigned char padval;
- off_t idx;
- if(is_padding) {
- /* We are PADDING this block (and therefore adding bytes) */
- /* The pad value in PKCS#7 is the number of bytes remaining in
- the block, so for a 16-byte block and 3 bytes left, it's
- 0x030303. In the oddball case where nb is an exact multiple
- multiple of block_length, set the padval to blocksize (i.e.
- add one full block) */
- padval = (unsigned char) (block_length - (nb % block_length));
- padval = padval ? padval : block_length;
- memset(buf->pad+nb, padval, padval);
- return nb+padval;
- } else {
- /* We are UNPADDING this block (and removing bytes)
- We really just need to verify that the pad bytes are correct,
- so start at the end of the string and work backwards. */
- /* Figure out what the padlength should be by looking at the
- last byte */
- idx = nb-1;
- padval = buf->pad[idx];
- /* padval must be nonzero and <= block length */
- if(padval <= 0 || padval > block_length)
- return 0;
- /* First byte's accounted for; do the rest */
- idx--;
- while(idx >= (off_t)(nb-padval))
- if(buf->pad[idx] != padval)
- return 0;
- else
- idx--;
- /* If we got here, the pad checked out, so return a smaller
- number of bytes than nb (basically where we left off+1) */
- return idx+1;
- }
- }
- /*
- * Perform an encrypt/decrypt operation to/from files using AES+CBC+PKCS7 pad.
- * Set encrypt to 1 to encrypt, 0 to decrypt.
- *
- * Input: in/out files, key, iv, and mode
- * Output: CRYPT_OK if no error
- * Side Effects: bytes slurped from infile, pushed to outfile, fds updated.
- */
- int do_crypt(FILE *infd, FILE *outfd, unsigned char *key, unsigned char *iv,
- int encrypt)
- {
- union paddable inbuf, outbuf;
- int cipher, ret;
- symmetric_CBC cbc;
- size_t nb;
- /* Register your cipher! */
- cipher = register_cipher(&aes_desc);
- if(cipher == -1)
- return CRYPT_INVALID_CIPHER;
- /* Start a CBC session with cipher/key/val params */
- ret = cbc_start(cipher, iv, key, KEY_LENGTH, 0, &cbc);
- if( ret != CRYPT_OK )
- return -1;
- do {
- /* Get bytes from the source */
- nb = fread(inbuf.unpad, 1, sizeof(inbuf.unpad), infd);
- if(!nb)
- return encrypt ? CRYPT_OK : CRYPT_ERROR;
- /* Barf if we got a read error */
- if(ferror(infd))
- return CRYPT_ERROR;
- if(encrypt) {
- /* We're encrypting, so pad first (if at EOF) and then
- crypt */
- if(feof(infd))
- nb = pkcs7_pad(&inbuf, nb,
- aes_desc.block_length, 1);
- ret = cbc_encrypt(inbuf.pad, outbuf.pad, nb, &cbc);
- if(ret != CRYPT_OK)
- return ret;
- } else {
- /* We're decrypting, so decrypt and then unpad if at
- EOF */
- ret = cbc_decrypt(inbuf.unpad, outbuf.unpad, nb, &cbc);
- if( ret != CRYPT_OK )
- return ret;
- if( feof(infd) )
- nb = pkcs7_pad(&outbuf, nb,
- aes_desc.block_length, 0);
- if(nb == 0)
- /* The file didn't decrypt correctly */
- return CRYPT_ERROR;
- }
- /* Push bytes to outfile */
- if(fwrite(outbuf.unpad, 1, nb, outfd) != nb)
- return CRYPT_ERROR;
- } while(!feof(infd));
- /* Close up */
- cbc_done(&cbc);
- return CRYPT_OK;
- }
- /* Convenience macro for the various barfable places below */
- #define BARF(a) { \
- if(infd) fclose(infd); \
- if(outfd) { fclose(outfd); remove(argv[3]); } \
- barf(argv[0], a); \
- }
- /*
- * The main routine. Mostly validate cmdline params, open files, run the KDF,
- * and do the crypt.
- */
- int main(int argc, char *argv[]) {
- unsigned char salt[SALT_LENGTH];
- FILE *infd = NULL, *outfd = NULL;
- int encrypt = -1;
- int hash = -1;
- int ret;
- unsigned char keyiv[KEY_LENGTH + IV_LENGTH];
- unsigned long keyivlen = (KEY_LENGTH + IV_LENGTH);
- unsigned char *key, *iv;
- /* Check proper number of cmdline args */
- if(argc < 5 || argc > 6)
- BARF("Invalid number of arguments");
- /* Check proper mode of operation */
- if (!strncmp(argv[1], "enc", 3))
- encrypt = 1;
- else if(!strncmp(argv[1], "dec", 3))
- encrypt = 0;
- else
- BARF("Bad command name");
- /* Check we can open infile/outfile */
- infd = fopen(argv[2], "rb");
- if(infd == NULL)
- BARF("Could not open infile");
- outfd = fopen(argv[3], "wb");
- if(outfd == NULL)
- BARF("Could not open outfile");
- /* Get the salt from wherever */
- if(argc == 6) {
- /* User-provided */
- if(parse_hex_salt((unsigned char*) argv[5], salt) != CRYPT_OK)
- BARF("Bad user-specified salt");
- } else if(!strncmp(argv[1], "enc", 3)) {
- /* Encrypting; get from RNG */
- if(rng_get_bytes(salt, sizeof(salt), NULL) != sizeof(salt))
- BARF("Not enough random data");
- } else {
- /* Parse from infile (decrypt only) */
- if(parse_openssl_header(infd, salt) != CRYPT_OK)
- BARF("Invalid OpenSSL header in infile");
- }
- /* Fetch the MD5 hasher for PKCS#5 */
- hash = register_hash(&md5_desc);
- if(hash == -1)
- BARF("Could not register MD5 hash");
- /* Set things to a sane initial state */
- zeromem(keyiv, sizeof(keyiv));
- key = keyiv + 0; /* key comes first */
- iv = keyiv + KEY_LENGTH; /* iv comes next */
- /* Run the key derivation from the provided passphrase. This gets us
- the key and iv. */
- ret = pkcs_5_alg1_openssl((unsigned char*)argv[4], strlen(argv[4]), salt,
- OPENSSL_ITERATIONS, hash, keyiv, &keyivlen );
- if(ret != CRYPT_OK)
- BARF("Could not derive key/iv from passphrase");
- /* Display the salt/key/iv like OpenSSL cmdline does when -p */
- printf("salt="); dump_bytes(salt, sizeof(salt)); printf("\n");
- printf("key="); dump_bytes(key, KEY_LENGTH); printf("\n");
- printf("iv ="); dump_bytes(iv, IV_LENGTH ); printf("\n");
- /* If we're encrypting, write the salt header as OpenSSL does */
- if(!strncmp(argv[1], "enc", 3)) {
- if(fwrite(salt_header, 1, sizeof(salt_header), outfd) !=
- sizeof(salt_header) )
- BARF("Error writing salt header to outfile");
- if(fwrite(salt, 1, sizeof(salt), outfd) != sizeof(salt))
- BARF("Error writing salt to outfile");
- }
- /* At this point, the files are open, the salt has been figured out,
- and we're ready to pump data through crypt. */
- /* Do the crypt operation */
- if(do_crypt(infd, outfd, key, iv, encrypt) != CRYPT_OK)
- BARF("Error during crypt operation");
- /* Clean up */
- fclose(infd); fclose(outfd);
- return 0;
- }
- /* ref: $Format:%D$ */
- /* git commit: $Format:%H$ */
- /* commit time: $Format:%ai$ */
|