1 // SPDX-License-Identifier: BSD-2-Clause
2 /* LibTomCrypt, modular cryptographic library -- Tom St Denis
3  *
4  * LibTomCrypt is a library that provides various cryptographic
5  * algorithms in a highly modular and flexible manner.
6  *
7  * The library is free for all purposes without any express
8  * guarantee it works.
9  */
10 #include "tomcrypt_private.h"
11 
12 /**
13    @file pkcs_5_1.c
14    PKCS #5, Algorithm #1, Tom St Denis
15 */
16 #ifdef LTC_PKCS_5
17 /**
18    Execute PKCS #5 v1 in strict or OpenSSL EVP_BytesToKey()-compat mode.
19 
20    PKCS#5 v1 specifies that the output key length can be no larger than
21    the hash output length.  OpenSSL unilaterally extended that by repeating
22    the hash process on a block-by-block basis for as long as needed to make
23    bigger keys.  If you want to be compatible with KDF for e.g. "openssl enc",
24    you'll want that.
25 
26    If you want strict PKCS behavior, turn openssl_compat off.  Or (more
27    likely), use one of the convenience functions below.
28 
29    @param password         The password (or key)
30    @param password_len     The length of the password (octet)
31    @param salt             The salt (or nonce) which is 8 octets long
32    @param iteration_count  The PKCS #5 v1 iteration count
33    @param hash_idx         The index of the hash desired
34    @param out              [out] The destination for this algorithm
35    @param outlen           [in/out] The max size and resulting size of the algorithm output
36    @param openssl_compat   [in] Whether or not to grow the key to the buffer size ala OpenSSL
37    @return CRYPT_OK if successful
38 */
_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)39 static int _pkcs_5_alg1_common(const unsigned char *password,
40                        unsigned long password_len,
41                        const unsigned char *salt,
42                        int iteration_count,  int hash_idx,
43                        unsigned char *out,   unsigned long *outlen,
44                        int openssl_compat)
45 {
46    int err;
47    unsigned long x;
48    hash_state    *md;
49    unsigned char *buf;
50    /* Storage vars in case we need to support > hashsize (OpenSSL compat) */
51    unsigned long block = 0, iter;
52    /* How many bytes to put in the outbut buffer (convenience calc) */
53    unsigned long outidx = 0, nb = 0;
54 
55    LTC_ARGCHK(password != NULL);
56    LTC_ARGCHK(salt     != NULL);
57    LTC_ARGCHK(out      != NULL);
58    LTC_ARGCHK(outlen   != NULL);
59 
60    /* test hash IDX */
61    if ((err = hash_is_valid(hash_idx)) != CRYPT_OK) {
62       return err;
63    }
64 
65    /* allocate memory */
66    md  = XMALLOC(sizeof(hash_state));
67    buf = XMALLOC(MAXBLOCKSIZE);
68    if (md == NULL || buf == NULL) {
69       if (md != NULL) {
70          XFREE(md);
71       }
72       if (buf != NULL) {
73          XFREE(buf);
74       }
75       return CRYPT_MEM;
76    }
77 
78    while(block * hash_descriptor[hash_idx]->hashsize < *outlen) {
79 
80       /* hash initial (maybe previous hash) + password + salt */
81       if ((err = hash_descriptor[hash_idx]->init(md)) != CRYPT_OK) {
82           goto LBL_ERR;
83       }
84       /* in OpenSSL mode, we first hash the previous result for blocks 2-n */
85       if (openssl_compat && block) {
86           if ((err = hash_descriptor[hash_idx]->process(md, buf, hash_descriptor[hash_idx]->hashsize)) != CRYPT_OK) {
87               goto LBL_ERR;
88           }
89       }
90       if ((err = hash_descriptor[hash_idx]->process(md, password, password_len)) != CRYPT_OK) {
91           goto LBL_ERR;
92       }
93       if ((err = hash_descriptor[hash_idx]->process(md, salt, 8)) != CRYPT_OK) {
94           goto LBL_ERR;
95       }
96       if ((err = hash_descriptor[hash_idx]->done(md, buf)) != CRYPT_OK) {
97           goto LBL_ERR;
98       }
99 
100       iter = iteration_count;
101       while (--iter) {
102          /* code goes here. */
103          x = MAXBLOCKSIZE;
104          if ((err = hash_memory(hash_idx, buf, hash_descriptor[hash_idx]->hashsize, buf, &x)) != CRYPT_OK) {
105             goto LBL_ERR;
106          }
107       }
108 
109       /* limit the size of the copy to however many bytes we have left in
110          the output buffer (and how many bytes we have to copy) */
111       outidx = block*hash_descriptor[hash_idx]->hashsize;
112       nb = hash_descriptor[hash_idx]->hashsize;
113       if(outidx+nb > *outlen) {
114           nb = *outlen - outidx;
115       }
116       if(nb > 0) {
117           XMEMCPY(out+outidx, buf, nb);
118       }
119 
120       block++;
121       if (!openssl_compat) {
122           break;
123       }
124    }
125    /* In strict mode, we always return the hashsize, in compat we filled it
126       as much as was requested, so we leave it alone. */
127    if(!openssl_compat) {
128       *outlen = hash_descriptor[hash_idx]->hashsize;
129    }
130 
131    err = CRYPT_OK;
132 LBL_ERR:
133 #ifdef LTC_CLEAN_STACK
134    zeromem(buf, MAXBLOCKSIZE);
135    zeromem(md, sizeof(hash_state));
136 #endif
137 
138    XFREE(buf);
139    XFREE(md);
140 
141    return err;
142 }
143 
144 /**
145    Execute PKCS #5 v1 - Strict mode (no OpenSSL-compatible extension)
146    @param password         The password (or key)
147    @param password_len     The length of the password (octet)
148    @param salt             The salt (or nonce) which is 8 octets long
149    @param iteration_count  The PKCS #5 v1 iteration count
150    @param hash_idx         The index of the hash desired
151    @param out              [out] The destination for this algorithm
152    @param outlen           [in/out] The max size and resulting size of the algorithm output
153    @return CRYPT_OK if successful
154 */
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)155 int pkcs_5_alg1(const unsigned char *password, unsigned long password_len,
156                 const unsigned char *salt,
157                 int iteration_count,  int hash_idx,
158                 unsigned char *out,   unsigned long *outlen)
159 {
160    return _pkcs_5_alg1_common(password, password_len, salt, iteration_count,
161                              hash_idx, out, outlen, 0);
162 }
163 
164 /**
165    Execute PKCS #5 v1 - OpenSSL-extension-compatible mode
166 
167    Use this one if you need to derive keys as "openssl enc" does by default.
168    OpenSSL (for better or worse), uses MD5 as the hash and iteration_count=1.
169    @param password         The password (or key)
170    @param password_len     The length of the password (octet)
171    @param salt             The salt (or nonce) which is 8 octets long
172    @param iteration_count  The PKCS #5 v1 iteration count
173    @param hash_idx         The index of the hash desired
174    @param out              [out] The destination for this algorithm
175    @param outlen           [in/out] The max size and resulting size of the algorithm output
176    @return CRYPT_OK if successful
177 */
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)178 int pkcs_5_alg1_openssl(const unsigned char *password,
179                         unsigned long password_len,
180                         const unsigned char *salt,
181                         int iteration_count,  int hash_idx,
182                         unsigned char *out,   unsigned long *outlen)
183 {
184    return _pkcs_5_alg1_common(password, password_len, salt, iteration_count,
185                              hash_idx, out, outlen, 1);
186 }
187 
188 #endif
189 
190 /* ref:         $Format:%D$ */
191 /* git commit:  $Format:%H$ */
192 /* commit time: $Format:%ai$ */
193