| // SPDX-License-Identifier: BSD-2-Clause |
| /* LibTomCrypt, modular cryptographic library -- Tom St Denis |
| * |
| * LibTomCrypt is a library that provides various cryptographic |
| * algorithms in a highly modular and flexible manner. |
| * |
| * The library is free for all purposes without any express |
| * guarantee it works. |
| */ |
| #include "tomcrypt_private.h" |
| |
| /** |
| @file pkcs_5_1.c |
| PKCS #5, Algorithm #1, Tom St Denis |
| */ |
| #ifdef LTC_PKCS_5 |
| /** |
| 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 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 |
| */ |
| 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); |
| LTC_ARGCHK(out != NULL); |
| LTC_ARGCHK(outlen != NULL); |
| |
| /* test hash IDX */ |
| if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) { |
| return err; |
| } |
| |
| /* allocate memory */ |
| md = XMALLOC(sizeof(hash_state)); |
| buf = XMALLOC(MAXBLOCKSIZE); |
| if (md == NULL || buf == NULL) { |
| if (md != NULL) { |
| XFREE(md); |
| } |
| if (buf != NULL) { |
| XFREE(buf); |
| } |
| return CRYPT_MEM; |
| } |
| |
| 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); |
| } |
| |
| block++; |
| if (!openssl_compat) { |
| break; |
| } |
| } |
| /* 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 |
| zeromem(buf, MAXBLOCKSIZE); |
| zeromem(md, sizeof(hash_state)); |
| #endif |
| |
| XFREE(buf); |
| XFREE(md); |
| |
| 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 |
| |
| /* ref: $Format:%D$ */ |
| /* git commit: $Format:%H$ */ |
| /* commit time: $Format:%ai$ */ |