tomb

the crypto undertaker
git clone git://parazyd.org/tomb.git
Log | Files | Refs | README | LICENSE

pbkdf2.c (4265B)


      1 /*
      2 ** SYNOPSIS
      3 **   echo "passphrase" | pbkdf2 salt_hex count > 48_byte_hex_key_and_iv
      4 **
      5 ** DESCRIPTION
      6 **
      7 ** Make the "Password-Based Key Derivation Function v2" function found in
      8 ** the openssl library available to the command line, as it is not available
      9 ** for use from the "openssl" command.  At the time of writing the "openssl"
     10 ** command only encrypts using the older, 'fast' pbkdf1.5 method.
     11 **
     12 ** The 'salt_hex' is the salt to be used, as a hexadecimal string. Typically
     13 ** this is 8 bytes (64 bit), and is an assigned randomly during encryption.
     14 **
     15 ** The 'count' is iteration count used to make the calculation of the key
     16 ** from the passphrase longer so as to take 1/2 to 2 seconds to generate.
     17 ** This complexity prevents slows down brute force attacks enormously.
     18 **
     19 ** The output of the above is a 48 bytes in hexadeximal, which is typically
     20 ** used for 32 byte encryption key KEY and a 16 byte IV as needed by
     21 ** Crypt-AES-256 (or some other encryption method).
     22 **
     23 ** NOTE: While the "openssl" command can accept a hex encoded 'key' and 'iv'
     24 ** it only does so on the command line, which is insecure.  As such I
     25 ** recommend that the output only be used with API access to the "OpenSSL"
     26 ** cryptography libraries.
     27 **
     28 *************
     29 **
     30 ** Anthony Thyssen   4 November 2009      A.Thyssen@griffith.edu.au
     31 **
     32 ** Based on a test program "pkcs5.c" found on
     33 **   http://www.mail-archive.com/openssl-users@openssl.org
     34 ** which uses openssl to perform PBKDF2 (RFC2898) iteritive (slow) password
     35 ** hashing.
     36 **
     37 ** Build
     38 **    gcc -o pbkdf2 pbkdf2.c -lcrypto
     39 **
     40 */
     41 #include <stdio.h>
     42 #include <string.h>
     43 
     44 #include <gcrypt.h>
     45 
     46 /* TODO: move print_hex and hex_to_binary to utils.h, with separate compiling */
     47 void print_hex(unsigned char *buf, int len)
     48 {
     49 	int i;
     50 
     51 	for(i=0;i<len;i++)
     52 		printf("%02x", buf[i]);
     53 	printf("\n");
     54 }
     55 
     56 int hex_to_binary(unsigned char *buf, char *hex)
     57 {
     58 	int ret;
     59 	int count=0;
     60 	while(*hex) {
     61 		if( hex[1] ) {
     62 			ret = sscanf( hex, "%2x", (unsigned int*) buf++ );
     63 			hex += 2;
     64 		}
     65 		else {
     66 			ret = sscanf( hex++, "%1x", (unsigned int*)buf++ );
     67 		}
     68 		count++;
     69 		if( ret != 1)
     70 			return -1;
     71 	}
     72 	*buf = 0;  // null terminate -- precaution
     73 	return count;
     74 }
     75 
     76 int main(int argc, char *argv[])
     77 {
     78 	char *pass = NULL;
     79 	unsigned char *salt;
     80 	int salt_len;                  // salt length in bytes
     81 	int ic=0;                        // iterative count
     82 	int result_len;
     83 	unsigned char *result;       // result (binary - 32+16 chars)
     84 	int i;
     85 
     86 	if ( argc != 4 ) {
     87 		fprintf(stderr, "usage: %s salt count len <passwd >binary_key_iv\n", argv[0]);
     88 		exit(10);
     89 	}
     90 
     91 	//TODO: move to base64decode
     92 	salt=calloc(strlen(argv[1])/2+3, sizeof(char));
     93 	salt_len=hex_to_binary(salt, argv[1]);
     94 	if( salt_len <= 0 ) {
     95 		fprintf(stderr, "Error: %s is not a valid salt (it must be a hexadecimal string)\n", argv[1]);
     96 		exit(1);
     97 	}
     98 
     99 	if( sscanf(argv[2], "%d", &ic) == 0 || ic<=0) {
    100 		fprintf(stderr, "Error: count must be a positive integer\n");
    101 		exit(1);
    102 	}
    103 	if( sscanf(argv[3], "%d", &result_len) == 0 || result_len<=0) {
    104 		fprintf(stderr, "Error: result_len must be a positive integer\n");
    105 		exit(1);
    106 	}
    107 
    108 	fscanf(stdin, "%ms", &pass);
    109 	if ( pass[strlen(pass)-1] == '\n' )
    110 		pass[strlen(pass)-1] = '\0';
    111 
    112 	// PBKDF 2
    113 	result = calloc(result_len, sizeof(unsigned char*));
    114 	if (!gcry_check_version ("1.5.0")) {
    115 		fputs ("libgcrypt version mismatch\n", stderr);
    116 		exit (2);
    117 	}
    118 	/* Allocate a pool of 16k secure memory.  This make the secure memory
    119 	available and also drops privileges where needed.  */
    120 	gcry_control (GCRYCTL_INIT_SECMEM, 16384, 0);
    121 	/* It is now okay to let Libgcrypt complain when there was/is
    122 	a problem with the secure memory. */
    123 	gcry_control (GCRYCTL_RESUME_SECMEM_WARN);
    124 	/* Tell Libgcrypt that initialization has completed. */
    125 	gcry_control (GCRYCTL_INITIALIZATION_FINISHED, 0);
    126 
    127 	gcry_kdf_derive( pass, strlen(pass), GCRY_KDF_PBKDF2, GCRY_MD_SHA1, salt, salt_len, ic, result_len, result);
    128 	print_hex(result, result_len);            // Key + IV   (as hex string)
    129 
    130 	//clear and free everything
    131 	for(i=0; i<result_len;i++)
    132 		result[i]=0;
    133 	free(result);
    134 	for(i=0; i<strlen(pass); i++) //blank
    135 		pass[i]=0;
    136 	free(pass);
    137 	for(i=0; i<strlen(argv[1])/2+3; i++) //blank
    138 		salt[i]=0;
    139 	free(salt);
    140 
    141 	return(0);
    142 }
    143 
    144 /* vim: set noexpandtab ts=4 sw=4: */