Add OpenSSL-compatible PKCS#5v1 KDF, demo of OpenSSL-compatible aes-256-cbc command.
This commit is contained in:
		
							parent
							
								
									bbe54053ec
								
							
						
					
					
						commit
						c7d6c3ad28
					
				
							
								
								
									
										13
									
								
								crypt.tex
									
									
									
									
									
								
							
							
						
						
									
										13
									
								
								crypt.tex
									
									
									
									
									
								
							@ -5070,6 +5070,8 @@ In order to securely handle user passwords for the purposes of creating session
 | 
				
			|||||||
is made up of two algorithms, Algorithm One and Algorithm Two.  Algorithm One is the older fairly limited algorithm which has been implemented
 | 
					is made up of two algorithms, Algorithm One and Algorithm Two.  Algorithm One is the older fairly limited algorithm which has been implemented
 | 
				
			||||||
for completeness.  Algorithm Two is a bit more modern and more flexible to work with.
 | 
					for completeness.  Algorithm Two is a bit more modern and more flexible to work with.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The OpenSSL project implemented an extension to Algorithm One that allows for arbitrary keylengths; we have a compatible implementation described below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
\subsection{Algorithm One}
 | 
					\subsection{Algorithm One}
 | 
				
			||||||
Algorithm One accepts as input a password, an 8--byte salt, and an iteration counter.  The iteration counter is meant to act as delay for
 | 
					Algorithm One accepts as input a password, an 8--byte salt, and an iteration counter.  The iteration counter is meant to act as delay for
 | 
				
			||||||
people trying to brute force guess the password.  The higher the iteration counter the longer the delay.  This algorithm also requires a hash 
 | 
					people trying to brute force guess the password.  The higher the iteration counter the longer the delay.  This algorithm also requires a hash 
 | 
				
			||||||
@ -5092,6 +5094,17 @@ on the password.  The \textit{hash\_idx} is the index of the hash you wish to us
 | 
				
			|||||||
The output of length up to \textit{outlen} is stored in \textit{out}.  If \textit{outlen} is initially larger than the size of the hash functions output
 | 
					The output of length up to \textit{outlen} is stored in \textit{out}.  If \textit{outlen} is initially larger than the size of the hash functions output
 | 
				
			||||||
it is set to the number of bytes stored.  If it is smaller than not all of the hash output is stored in \textit{out}.
 | 
					it is set to the number of bytes stored.  If it is smaller than not all of the hash output is stored in \textit{out}.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					\index{pkcs\_5\_alg1\_openssl()}
 | 
				
			||||||
 | 
					\begin{alltt}
 | 
				
			||||||
 | 
					int pkcs_5_alg1_openssl(const unsigned char *password,
 | 
				
			||||||
 | 
					                      unsigned long  password_len,
 | 
				
			||||||
 | 
					                const unsigned char *salt,
 | 
				
			||||||
 | 
					                                int  iteration_count,
 | 
				
			||||||
 | 
					                                int  hash_idx,
 | 
				
			||||||
 | 
					                      unsigned char *out,
 | 
				
			||||||
 | 
					                      unsigned long *outlen)
 | 
				
			||||||
 | 
					\end{alltt}
 | 
				
			||||||
 | 
					As above, but we generate as many bytes as requested in outlen per the OpenSSL extension to Algorithm One.  If you are trying to be compatible with OpenSSL's EVP\_BytesToKey() or the "openssl enc" command line (or variants such as perl's Crypt::CBC), then use this function with MD5 as your hash (ick!) and iteration\_count=1 (double-ick!!).
 | 
				
			||||||
\subsection{Algorithm Two}
 | 
					\subsection{Algorithm Two}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Algorithm Two is the recommended algorithm for this task.  It allows variable length salts, and can produce outputs larger than the 
 | 
					Algorithm Two is the recommended algorithm for this task.  It allows variable length salts, and can produce outputs larger than the 
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										381
									
								
								demos/openssl-enc.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										381
									
								
								demos/openssl-enc.c
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,381 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * 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
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 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 {
 | 
				
			||||||
 | 
					   char unpad[1024];
 | 
				
			||||||
 | 
					   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(char *pname, char *err)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   printf("Usage: %s <enc|dec> infile outfile [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(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:       none
 | 
				
			||||||
 | 
					 * Side Effects: bytes printed as a hex blob, no lf at the end
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					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;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      XMEMSET(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 -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* First byte's accounted for; do the rest */
 | 
				
			||||||
 | 
					      idx--;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      while(idx >= nb-padval)
 | 
				
			||||||
 | 
					         if(buf->pad[idx] != padval)
 | 
				
			||||||
 | 
					            return -1;
 | 
				
			||||||
 | 
					         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", sizeof("enc")))
 | 
				
			||||||
 | 
					      encrypt = 1;
 | 
				
			||||||
 | 
					   else if(!strncmp(argv[1], "dec", sizeof("dec")))
 | 
				
			||||||
 | 
					      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", sizeof("enc"))) {
 | 
				
			||||||
 | 
					      /* 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(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", sizeof("enc"))) {
 | 
				
			||||||
 | 
					      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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -76,6 +76,14 @@ int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
 | 
				
			|||||||
                int iteration_count,  int hash_idx,
 | 
					                int iteration_count,  int hash_idx,
 | 
				
			||||||
                unsigned char *out,   unsigned long *outlen);
 | 
					                unsigned char *out,   unsigned long *outlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Algorithm #1 - OpenSSL-compatible variant for arbitrarily-long keys.
 | 
				
			||||||
 | 
					   Compatible with EVP_BytesToKey() */
 | 
				
			||||||
 | 
					int pkcs_5_alg1_openssl(const unsigned char *password,
 | 
				
			||||||
 | 
					                        unsigned long password_len,
 | 
				
			||||||
 | 
					                        const unsigned char *salt,
 | 
				
			||||||
 | 
					                        int iteration_count,  int hash_idx,
 | 
				
			||||||
 | 
					                        unsigned char *out,   unsigned long *outlen);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Algorithm #2 (new) */
 | 
					/* Algorithm #2 (new) */
 | 
				
			||||||
int pkcs_5_alg2(const unsigned char *password, unsigned long password_len,
 | 
					int pkcs_5_alg2(const unsigned char *password, unsigned long password_len,
 | 
				
			||||||
                const unsigned char *salt,     unsigned long salt_len,
 | 
					                const unsigned char *salt,     unsigned long salt_len,
 | 
				
			||||||
 | 
				
			|||||||
@ -16,7 +16,17 @@
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
#ifdef LTC_PKCS_5
 | 
					#ifdef LTC_PKCS_5
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
   Execute PKCS #5 v1
 | 
					   Execute PKCS #5 v1 in strict or OpenSSL EVP_BytesToKey()-compat mode.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   PKCS#5 v1 specifies that the output key length can be no larger than
 | 
				
			||||||
 | 
					   the hash output length.  OpenSSL unilaterally extended that by repeating
 | 
				
			||||||
 | 
					   the hash process on a block-by-block basis for as long as needed to make
 | 
				
			||||||
 | 
					   bigger keys.  If you want to be compatible with KDF for e.g. "openssl enc",
 | 
				
			||||||
 | 
					   you'll want that.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   If you want strict PKCS behavior, turn openssl_compat off.  Or (more
 | 
				
			||||||
 | 
					   likely), use one of the convenience functions below.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   @param password         The password (or key)
 | 
					   @param password         The password (or key)
 | 
				
			||||||
   @param password_len     The length of the password (octet)
 | 
					   @param password_len     The length of the password (octet)
 | 
				
			||||||
   @param salt             The salt (or nonce) which is 8 octets long
 | 
					   @param salt             The salt (or nonce) which is 8 octets long
 | 
				
			||||||
@ -24,17 +34,24 @@
 | 
				
			|||||||
   @param hash_idx         The index of the hash desired
 | 
					   @param hash_idx         The index of the hash desired
 | 
				
			||||||
   @param out              [out] The destination for this algorithm
 | 
					   @param out              [out] The destination for this algorithm
 | 
				
			||||||
   @param outlen           [in/out] The max size and resulting size of the algorithm output
 | 
					   @param outlen           [in/out] The max size and resulting size of the algorithm output
 | 
				
			||||||
 | 
					   @param openssl_compat   [in] Whether or not to grow the key to the buffer size ala OpenSSL
 | 
				
			||||||
   @return CRYPT_OK if successful
 | 
					   @return CRYPT_OK if successful
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
 | 
					static int _pkcs_5_alg1_common(const unsigned char *password,
 | 
				
			||||||
                const unsigned char *salt,
 | 
					                       unsigned long password_len,
 | 
				
			||||||
                int iteration_count,  int hash_idx,
 | 
					                       const unsigned char *salt,
 | 
				
			||||||
                unsigned char *out,   unsigned long *outlen)
 | 
					                       int iteration_count,  int hash_idx,
 | 
				
			||||||
 | 
					                       unsigned char *out,   unsigned long *outlen,
 | 
				
			||||||
 | 
					                       int openssl_compat)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
   int err;
 | 
					   int err;
 | 
				
			||||||
   unsigned long x;
 | 
					   unsigned long x;
 | 
				
			||||||
   hash_state    *md;
 | 
					   hash_state    *md;
 | 
				
			||||||
   unsigned char *buf;
 | 
					   unsigned char *buf;
 | 
				
			||||||
 | 
					   /* Storage vars in case we need to support > hashsize (OpenSSL compat) */
 | 
				
			||||||
 | 
					   unsigned long block = 0, iter;
 | 
				
			||||||
 | 
					   /* How many bytes to put in the outbut buffer (convenience calc) */
 | 
				
			||||||
 | 
					   unsigned long outidx = 0, nb = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   LTC_ARGCHK(password != NULL);
 | 
					   LTC_ARGCHK(password != NULL);
 | 
				
			||||||
   LTC_ARGCHK(salt     != NULL);
 | 
					   LTC_ARGCHK(salt     != NULL);
 | 
				
			||||||
@ -59,33 +76,55 @@ int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
 | 
				
			|||||||
      return CRYPT_MEM;
 | 
					      return CRYPT_MEM;
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* hash initial password + salt */
 | 
					   while(block * hash_descriptor[hash_idx].hashsize < *outlen) {
 | 
				
			||||||
   if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) {
 | 
					 | 
				
			||||||
       goto LBL_ERR;
 | 
					 | 
				
			||||||
   }
 | 
					 | 
				
			||||||
   if ((err = hash_descriptor[hash_idx].process(md, password, password_len)) != CRYPT_OK) {
 | 
					 | 
				
			||||||
       goto LBL_ERR;
 | 
					 | 
				
			||||||
   }
 | 
					 | 
				
			||||||
   if ((err = hash_descriptor[hash_idx].process(md, salt, 8)) != CRYPT_OK) {
 | 
					 | 
				
			||||||
       goto LBL_ERR;
 | 
					 | 
				
			||||||
   }
 | 
					 | 
				
			||||||
   if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) {
 | 
					 | 
				
			||||||
       goto LBL_ERR;
 | 
					 | 
				
			||||||
   }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
   while (--iteration_count) {
 | 
					      /* hash initial (maybe previous hash) + password + salt */
 | 
				
			||||||
      /* code goes here. */
 | 
					      if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) {
 | 
				
			||||||
      x = MAXBLOCKSIZE;
 | 
					          goto LBL_ERR;
 | 
				
			||||||
      if ((err = hash_memory(hash_idx, buf, hash_descriptor[hash_idx].hashsize, buf, &x)) != CRYPT_OK) {
 | 
					      }
 | 
				
			||||||
         goto LBL_ERR;
 | 
					      /* in OpenSSL mode, we first hash the previous result for blocks 2-n */
 | 
				
			||||||
 | 
					      if (openssl_compat && block) {
 | 
				
			||||||
 | 
					          if ((err = hash_descriptor[hash_idx].process(md, buf, hash_descriptor[hash_idx].hashsize)) != CRYPT_OK) {
 | 
				
			||||||
 | 
					              goto LBL_ERR;
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if ((err = hash_descriptor[hash_idx].process(md, password, password_len)) != CRYPT_OK) {
 | 
				
			||||||
 | 
					          goto LBL_ERR;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if ((err = hash_descriptor[hash_idx].process(md, salt, 8)) != CRYPT_OK) {
 | 
				
			||||||
 | 
					          goto LBL_ERR;
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      if ((err = hash_descriptor[hash_idx].done(md, buf)) != CRYPT_OK) {
 | 
				
			||||||
 | 
					          goto LBL_ERR;
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
   }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
   /* copy upto outlen bytes */
 | 
					      iter = iteration_count;
 | 
				
			||||||
   for (x = 0; x < hash_descriptor[hash_idx].hashsize && x < *outlen; x++) {
 | 
					      while (--iter) {
 | 
				
			||||||
       out[x] = buf[x];
 | 
					         /* code goes here. */
 | 
				
			||||||
 | 
					         x = MAXBLOCKSIZE;
 | 
				
			||||||
 | 
					         if ((err = hash_memory(hash_idx, buf, hash_descriptor[hash_idx].hashsize, buf, &x)) != CRYPT_OK) {
 | 
				
			||||||
 | 
					            goto LBL_ERR;
 | 
				
			||||||
 | 
					         }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      /* limit the size of the copy to however many bytes we have left in
 | 
				
			||||||
 | 
					         the output buffer (and how many bytes we have to copy) */
 | 
				
			||||||
 | 
					      outidx = block*hash_descriptor[hash_idx].hashsize;
 | 
				
			||||||
 | 
					      nb = hash_descriptor[hash_idx].hashsize;
 | 
				
			||||||
 | 
					      if(outidx+nb > *outlen)
 | 
				
			||||||
 | 
					          nb = *outlen - outidx;
 | 
				
			||||||
 | 
					      if(nb > 0)
 | 
				
			||||||
 | 
					          XMEMCPY(out+outidx, buf, nb);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      block++;
 | 
				
			||||||
 | 
					      if (!openssl_compat)
 | 
				
			||||||
 | 
					          break;
 | 
				
			||||||
   }
 | 
					   }
 | 
				
			||||||
   *outlen = x;
 | 
					   /* In strict mode, we always return the hashsize, in compat we filled it
 | 
				
			||||||
 | 
					      as much as was requested, so we leave it alone. */
 | 
				
			||||||
 | 
					   if(!openssl_compat)
 | 
				
			||||||
 | 
					      *outlen = hash_descriptor[hash_idx].hashsize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
   err = CRYPT_OK;
 | 
					   err = CRYPT_OK;
 | 
				
			||||||
LBL_ERR:
 | 
					LBL_ERR:
 | 
				
			||||||
#ifdef LTC_CLEAN_STACK
 | 
					#ifdef LTC_CLEAN_STACK
 | 
				
			||||||
@ -99,6 +138,50 @@ LBL_ERR:
 | 
				
			|||||||
   return err;
 | 
					   return err;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					   Execute PKCS #5 v1 - Strict mode (no OpenSSL-compatible extension)
 | 
				
			||||||
 | 
					   @param password         The password (or key)
 | 
				
			||||||
 | 
					   @param password_len     The length of the password (octet)
 | 
				
			||||||
 | 
					   @param salt             The salt (or nonce) which is 8 octets long
 | 
				
			||||||
 | 
					   @param iteration_count  The PKCS #5 v1 iteration count
 | 
				
			||||||
 | 
					   @param hash_idx         The index of the hash desired
 | 
				
			||||||
 | 
					   @param out              [out] The destination for this algorithm
 | 
				
			||||||
 | 
					   @param outlen           [in/out] The max size and resulting size of the algorithm output
 | 
				
			||||||
 | 
					   @return CRYPT_OK if successful
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
 | 
				
			||||||
 | 
					                const unsigned char *salt,
 | 
				
			||||||
 | 
					                int iteration_count,  int hash_idx,
 | 
				
			||||||
 | 
					                unsigned char *out,   unsigned long *outlen)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   return _pkcs_5_alg1_common(password, password_len, salt, iteration_count,
 | 
				
			||||||
 | 
					                             hash_idx, out, outlen, 0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					   Execute PKCS #5 v1 - OpenSSL-extension-compatible mode
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   Use this one if you need to derive keys as "openssl enc" does by default.
 | 
				
			||||||
 | 
					   OpenSSL (for better or worse), uses MD5 as the hash and iteration_count=1.
 | 
				
			||||||
 | 
					   @param password         The password (or key)
 | 
				
			||||||
 | 
					   @param password_len     The length of the password (octet)
 | 
				
			||||||
 | 
					   @param salt             The salt (or nonce) which is 8 octets long
 | 
				
			||||||
 | 
					   @param iteration_count  The PKCS #5 v1 iteration count
 | 
				
			||||||
 | 
					   @param hash_idx         The index of the hash desired
 | 
				
			||||||
 | 
					   @param out              [out] The destination for this algorithm
 | 
				
			||||||
 | 
					   @param outlen           [in/out] The max size and resulting size of the algorithm output
 | 
				
			||||||
 | 
					   @return CRYPT_OK if successful
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					int pkcs_5_alg1_openssl(const unsigned char *password,
 | 
				
			||||||
 | 
					                        unsigned long password_len,
 | 
				
			||||||
 | 
					                        const unsigned char *salt,
 | 
				
			||||||
 | 
					                        int iteration_count,  int hash_idx,
 | 
				
			||||||
 | 
					                        unsigned char *out,   unsigned long *outlen)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   return _pkcs_5_alg1_common(password, password_len, salt, iteration_count,
 | 
				
			||||||
 | 
					                             hash_idx, out, outlen, 1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* $Source$ */
 | 
					/* $Source$ */
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user