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,
|
||||||
|
unsigned long password_len,
|
||||||
const unsigned char *salt,
|
const unsigned char *salt,
|
||||||
int iteration_count, int hash_idx,
|
int iteration_count, int hash_idx,
|
||||||
unsigned char *out, unsigned long *outlen)
|
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,10 +76,18 @@ 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) {
|
||||||
|
|
||||||
|
/* hash initial (maybe previous hash) + password + salt */
|
||||||
if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) {
|
if ((err = hash_descriptor[hash_idx].init(md)) != CRYPT_OK) {
|
||||||
goto LBL_ERR;
|
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) {
|
if ((err = hash_descriptor[hash_idx].process(md, password, password_len)) != CRYPT_OK) {
|
||||||
goto LBL_ERR;
|
goto LBL_ERR;
|
||||||
}
|
}
|
||||||
@ -73,7 +98,8 @@ int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
|
|||||||
goto LBL_ERR;
|
goto LBL_ERR;
|
||||||
}
|
}
|
||||||
|
|
||||||
while (--iteration_count) {
|
iter = iteration_count;
|
||||||
|
while (--iter) {
|
||||||
/* code goes here. */
|
/* code goes here. */
|
||||||
x = MAXBLOCKSIZE;
|
x = MAXBLOCKSIZE;
|
||||||
if ((err = hash_memory(hash_idx, buf, hash_descriptor[hash_idx].hashsize, buf, &x)) != CRYPT_OK) {
|
if ((err = hash_memory(hash_idx, buf, hash_descriptor[hash_idx].hashsize, buf, &x)) != CRYPT_OK) {
|
||||||
@ -81,11 +107,24 @@ int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* copy upto outlen bytes */
|
/* limit the size of the copy to however many bytes we have left in
|
||||||
for (x = 0; x < hash_descriptor[hash_idx].hashsize && x < *outlen; x++) {
|
the output buffer (and how many bytes we have to copy) */
|
||||||
out[x] = buf[x];
|
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…
Reference in New Issue
Block a user