xref: /freebsd/crypto/krb5/src/lib/crypto/builtin/pbkdf2.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/crypto/builtin/pbkdf2.c - Implementation of PBKDF2 from RFC 2898 */
3 /*
4  * Copyright 2002, 2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include <ctype.h>
28 #include "crypto_int.h"
29 
30 #ifdef K5_BUILTIN_PBKDF2
31 
32 /*
33  * RFC 2898 specifies PBKDF2 in terms of an underlying pseudo-random
34  * function with two arguments (password and salt||blockindex).  Right
35  * now we only use PBKDF2 with the hmac-sha1 PRF, also specified in
36  * RFC 2898, which invokes HMAC with the password as the key and the
37  * second argument as the text.  (HMAC accepts any key size up to the
38  * block size; the password is pre-hashed with unkeyed SHA1 if it is
39  * longer than the block size.)
40  *
41  * For efficiency, it is better to generate the key from the password
42  * once at the beginning, so we specify prf_fn in terms of a
43  * krb5_key first argument.  That might not be convenient for a PRF
44  * which uses the password in some other way, so this might need to be
45  * adjusted in the future.
46  */
47 
48 typedef krb5_error_code (*prf_fn)(krb5_key pass, krb5_data *salt,
49                                   krb5_data *out);
50 
51 static int debug_hmac = 0;
52 
printd(const char * descr,krb5_data * d)53 static void printd (const char *descr, krb5_data *d) {
54     unsigned int i, j;
55     const int r = 16;
56 
57     printf("%s:", descr);
58 
59     for (i = 0; i < d->length; i += r) {
60         printf("\n  %04x: ", i);
61         for (j = i; j < i + r && j < d->length; j++)
62             printf(" %02x", 0xff & d->data[j]);
63         for (; j < i + r; j++)
64             printf("   ");
65         printf("   ");
66         for (j = i; j < i + r && j < d->length; j++) {
67             int c = 0xff & d->data[j];
68             printf("%c", isprint(c) ? c : '.');
69         }
70     }
71     printf("\n");
72 }
73 
74 /*
75  * Implements the hmac-sha1 PRF.  pass has been pre-hashed (if
76  * necessary) and converted to a key already; salt has had the block
77  * index appended to the original salt.
78  *
79  * NetBSD 8 declares an hmac() function in stdlib.h, so avoid that name.
80  */
81 static krb5_error_code
k5_hmac(const struct krb5_hash_provider * hash,krb5_keyblock * pass,krb5_data * salt,krb5_data * out)82 k5_hmac(const struct krb5_hash_provider *hash, krb5_keyblock *pass,
83         krb5_data *salt, krb5_data *out)
84 {
85     krb5_error_code err;
86     krb5_crypto_iov iov;
87 
88     if (debug_hmac)
89         printd(" hmac input", salt);
90     iov.flags = KRB5_CRYPTO_TYPE_DATA;
91     iov.data = *salt;
92     err = krb5int_hmac_keyblock(hash, pass, &iov, 1, out);
93     if (err == 0 && debug_hmac)
94         printd(" hmac output", out);
95     return err;
96 }
97 
98 static krb5_error_code
F(char * output,char * u_tmp1,char * u_tmp2,const struct krb5_hash_provider * hash,size_t hlen,krb5_keyblock * pass,const krb5_data * salt,unsigned long count,int i)99 F(char *output, char *u_tmp1, char *u_tmp2,
100   const struct krb5_hash_provider *hash, size_t hlen, krb5_keyblock *pass,
101   const krb5_data *salt, unsigned long count, int i)
102 {
103     unsigned char ibytes[4];
104     unsigned int j, k;
105     krb5_data sdata;
106     krb5_data out;
107     krb5_error_code err;
108 
109     /* Compute U_1.  */
110     store_32_be(i, ibytes);
111 
112     memcpy(u_tmp2, salt->data, salt->length);
113     memcpy(u_tmp2 + salt->length, ibytes, 4);
114     sdata = make_data(u_tmp2, salt->length + 4);
115 
116     out = make_data(u_tmp1, hlen);
117 
118     err = k5_hmac(hash, pass, &sdata, &out);
119     if (err)
120         return err;
121 
122     memcpy(output, u_tmp1, hlen);
123 
124     /* Compute U_2, .. U_c.  */
125     sdata.length = hlen;
126     for (j = 2; j <= count; j++) {
127         memcpy(u_tmp2, u_tmp1, hlen);
128         err = k5_hmac(hash, pass, &sdata, &out);
129         if (err)
130             return err;
131 
132         /* And xor them together.  */
133         for (k = 0; k < hlen; k++)
134             output[k] ^= u_tmp1[k];
135     }
136     return 0;
137 }
138 
139 static krb5_error_code
pbkdf2(const struct krb5_hash_provider * hash,krb5_keyblock * pass,const krb5_data * salt,unsigned long count,const krb5_data * output)140 pbkdf2(const struct krb5_hash_provider *hash, krb5_keyblock *pass,
141        const krb5_data *salt, unsigned long count, const krb5_data *output)
142 {
143     size_t hlen = hash->hashsize;
144     int l, i;
145     char *utmp1, *utmp2;
146     char utmp3[128];             /* XXX length shouldn't be hardcoded! */
147 
148     if (output->length == 0 || hlen == 0)
149         abort();
150     /* Step 1 & 2.  */
151     if (output->length / hlen > 0xffffffff)
152         abort();
153     /* Step 2.  */
154     l = (output->length + hlen - 1) / hlen;
155 
156     utmp1 = /*output + dklen; */ malloc(hlen);
157     if (utmp1 == NULL)
158         return ENOMEM;
159     utmp2 = /*utmp1 + hlen; */ malloc(salt->length + 4 + hlen);
160     if (utmp2 == NULL) {
161         free(utmp1);
162         return ENOMEM;
163     }
164 
165     /* Step 3.  */
166     for (i = 1; i <= l; i++) {
167         krb5_error_code err;
168         char *out;
169 
170         if (i == l)
171             out = utmp3;
172         else
173             out = output->data + (i-1) * hlen;
174         err = F(out, utmp1, utmp2, hash, hlen, pass, salt, count, i);
175         if (err) {
176             free(utmp1);
177             free(utmp2);
178             return err;
179         }
180         if (i == l)
181             memcpy(output->data + (i-1) * hlen, utmp3,
182                    output->length - (i-1) * hlen);
183 
184     }
185     free(utmp1);
186     free(utmp2);
187     return 0;
188 }
189 
190 krb5_error_code
krb5int_pbkdf2_hmac(const struct krb5_hash_provider * hash,const krb5_data * out,unsigned long count,const krb5_data * pass,const krb5_data * salt)191 krb5int_pbkdf2_hmac(const struct krb5_hash_provider *hash,
192                     const krb5_data *out, unsigned long count,
193                     const krb5_data *pass, const krb5_data *salt)
194 {
195     krb5_keyblock keyblock;
196     char tmp[128];
197     krb5_data d;
198     krb5_crypto_iov iov;
199     krb5_error_code err;
200 
201     assert(hash->hashsize <= sizeof(tmp));
202     if (pass->length > hash->blocksize) {
203         d = make_data(tmp, hash->hashsize);
204         iov.flags = KRB5_CRYPTO_TYPE_DATA;
205         iov.data = *pass;
206         err = hash->hash(&iov, 1, &d);
207         if (err)
208             return err;
209         keyblock.length = d.length;
210         keyblock.contents = (krb5_octet *) d.data;
211     } else {
212         keyblock.length = pass->length;
213         keyblock.contents = (krb5_octet *) pass->data;
214     }
215     keyblock.enctype = ENCTYPE_NULL;
216 
217     err = pbkdf2(hash, &keyblock, salt, count, out);
218     return err;
219 }
220 
221 #endif /* K5_BUILTIN_PBKDF2 */
222