diff options
Diffstat (limited to 'libtomcrypt/src/misc/pkcs5/pkcs_5_1.c')
-rw-r--r-- | libtomcrypt/src/misc/pkcs5/pkcs_5_1.c | 161 |
1 files changed, 121 insertions, 40 deletions
diff --git a/libtomcrypt/src/misc/pkcs5/pkcs_5_1.c b/libtomcrypt/src/misc/pkcs5/pkcs_5_1.c index 519e7aa..10325de 100644 --- a/libtomcrypt/src/misc/pkcs5/pkcs_5_1.c +++ b/libtomcrypt/src/misc/pkcs5/pkcs_5_1.c @@ -5,36 +5,51 @@ * * The library is free for all purposes without any express * guarantee it works. - * - * Tom St Denis, tomstdenis@gmail.com, http://libtom.org */ -#include <tomcrypt.h> +#include "tomcrypt.h" -/** +/** @file pkcs_5_1.c - LTC_PKCS #5, Algorithm #1, Tom St Denis + PKCS #5, Algorithm #1, Tom St Denis */ #ifdef LTC_PKCS_5 /** - Execute LTC_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 - @param iteration_count The LTC_PKCS #5 v1 iteration count + @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 + @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); @@ -53,42 +68,64 @@ int pkcs_5_alg1(const unsigned char *password, unsigned long password_len, if (md != NULL) { XFREE(md); } - if (buf != NULL) { + if (buf != NULL) { XFREE(buf); } 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 (--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; + 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) { + 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; + } + + 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); - /* copy upto outlen bytes */ - for (x = 0; x < hash_descriptor[hash_idx].hashsize && x < *outlen; x++) { - out[x] = buf[x]; + 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 +#ifdef LTC_CLEAN_STACK zeromem(buf, MAXBLOCKSIZE); zeromem(md, sizeof(hash_state)); #endif @@ -99,8 +136,52 @@ 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$ */ -/* $Revision$ */ -/* $Date$ */ +/* ref: $Format:%D$ */ +/* git commit: $Format:%H$ */ +/* commit time: $Format:%ai$ */ |