Add OpenSSL-compatible PKCS#5v1 KDF, demo of OpenSSL-compatible aes-256-cbc command.

This commit is contained in:
BJ Black
2015-12-02 16:54:09 -08:00
committed by Karel Miko
parent bbe54053ec
commit c7d6c3ad28
4 changed files with 513 additions and 28 deletions
+111 -28
View File
@@ -16,7 +16,17 @@
*/
#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_len The length of the password (octet)
@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 out [out] The destination for this algorithm
@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
*/
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)
static int _pkcs_5_alg1_common(const unsigned char *password,
unsigned long password_len,
const unsigned char *salt,
int iteration_count, int hash_idx,
unsigned char *out, unsigned long *outlen,
int openssl_compat)
{
int err;
unsigned long x;
hash_state *md;
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(salt != NULL);
@@ -59,33 +76,55 @@ int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
return CRYPT_MEM;
}
/* hash initial password + salt */
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(block * hash_descriptor[hash_idx].hashsize < *outlen) {
while (--iteration_count) {
/* code goes here. */
x = MAXBLOCKSIZE;
if ((err = hash_memory(hash_idx, buf, hash_descriptor[hash_idx].hashsize, buf, &x)) != CRYPT_OK) {
goto LBL_ERR;
/* hash initial (maybe previous hash) + password + salt */
if ((err = hash_descriptor[hash_idx].init(md)) != 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 */
for (x = 0; x < hash_descriptor[hash_idx].hashsize && x < *outlen; x++) {
out[x] = buf[x];
iter = iteration_count;
while (--iter) {
/* 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;
LBL_ERR:
#ifdef LTC_CLEAN_STACK
@@ -99,6 +138,50 @@ LBL_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
/* $Source$ */