xref: /netbsd-src/external/bsd/openldap/dist/contrib/slapd-modules/passwd/pbkdf2/pw-pbkdf2.c (revision 549b59ed3ccf0d36d3097190a0db27b770f3a839)
1 /*	$NetBSD: pw-pbkdf2.c,v 1.3 2021/08/14 16:14:53 christos Exp $	*/
2 
3 /* $OpenLDAP$ */
4 /* This work is part of OpenLDAP Software <http://www.openldap.org/>.
5  *
6  * Copyright 2009-2021 The OpenLDAP Foundation.
7  * All rights reserved.
8  *
9  * Redistribution and use in source and binary forms, with or without
10  * modification, are permitted only as authorized by the OpenLDAP
11  * Public License.
12  *
13  * A copy of this license is available in the file LICENSE in the
14  * top-level directory of the distribution or, alternatively, at
15  * <http://www.OpenLDAP.org/license.html>.
16  */
17 /* ACKNOWLEDGEMENT:
18  * This work was initially developed by HAMANO Tsukasa <hamano@osstech.co.jp>
19  */
20 
21 #define _GNU_SOURCE
22 
23 #include <sys/cdefs.h>
24 __RCSID("$NetBSD: pw-pbkdf2.c,v 1.3 2021/08/14 16:14:53 christos Exp $");
25 
26 #include "portable.h"
27 #include <ac/string.h>
28 #include "lber_pvt.h"
29 #include "lutil.h"
30 #include <stdio.h>
31 #include <stdlib.h>
32 
33 #ifdef HAVE_OPENSSL
34 #include <openssl/evp.h>
35 #elif HAVE_GNUTLS
36 #include <nettle/pbkdf2.h>
37 #include <nettle/hmac.h>
38 typedef void (*pbkdf2_hmac_update)(void *, unsigned, const uint8_t *);
39 typedef void (*pbkdf2_hmac_digest)(void *, unsigned, uint8_t *);
40 #else
41 #error Unsupported crypto backend.
42 #endif
43 
44 #define PBKDF2_ITERATION 10000
45 #define PBKDF2_SALT_SIZE 16
46 #define PBKDF2_SHA1_DK_SIZE 20
47 #define PBKDF2_SHA256_DK_SIZE 32
48 #define PBKDF2_SHA512_DK_SIZE 64
49 #define PBKDF2_MAX_DK_SIZE 64
50 
51 const struct berval pbkdf2_scheme = BER_BVC("{PBKDF2}");
52 const struct berval pbkdf2_sha1_scheme = BER_BVC("{PBKDF2-SHA1}");
53 const struct berval pbkdf2_sha256_scheme = BER_BVC("{PBKDF2-SHA256}");
54 const struct berval pbkdf2_sha512_scheme = BER_BVC("{PBKDF2-SHA512}");
55 
56 /*
57  * Converting base64 string to adapted base64 string.
58  * Adapted base64 encode is identical to general base64 encode except
59  * that it uses '.' instead of '+', and omits trailing padding '=' and
60  * whitespace.
61  * see http://pythonhosted.org/passlib/lib/passlib.utils.html
62  * This is destructive function.
63  */
b64_to_ab64(char * str)64 static int b64_to_ab64(char *str)
65 {
66 	char *p = str;
67 	do {
68 		if(*p == '+'){
69 			*p = '.';
70 		}
71 		if(*p == '='){
72 			*p = '\0';
73 		}
74 	} while(*p++);
75 	return 0;
76 }
77 
78 /*
79  * Converting adapted base64 string to base64 string.
80  * dstsize will require src length + 2, due to output string have
81  * potential to append "=" or "==".
82  * return -1 if few output buffer.
83  */
ab64_to_b64(char * src,char * dst,size_t dstsize)84 static int ab64_to_b64(char *src, char *dst, size_t dstsize){
85 	int i;
86 	char *p = src;
87 	for(i=0; p[i] && p[i] != '$'; i++){
88 		if(i >= dstsize){
89 			dst[0] = '\0';
90 			return -1;
91 		}
92 		if(p[i] == '.'){
93 			dst[i] = '+';
94 		}else{
95 			dst[i] = p[i];
96 		}
97 	}
98 	for(;i%4;i++){
99 		if(i >= dstsize){
100 			dst[0] = '\0';
101 			return -1;
102 		}
103 		dst[i] = '=';
104 	}
105 	dst[i] = '\0';
106 	return 0;
107 }
108 
pbkdf2_format(const struct berval * sc,int iteration,const struct berval * salt,const struct berval * dk,struct berval * msg)109 static int pbkdf2_format(
110 	const struct berval *sc,
111 	int iteration,
112 	const struct berval *salt,
113 	const struct berval *dk,
114 	struct berval *msg)
115 {
116 
117 	int rc, msg_len;
118 	char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1];
119 	char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_MAX_DK_SIZE) + 1];
120 
121 	rc = lutil_b64_ntop((unsigned char *)salt->bv_val, salt->bv_len,
122 						salt_b64, sizeof(salt_b64));
123 	if(rc < 0){
124 		return LUTIL_PASSWD_ERR;
125 	}
126 	b64_to_ab64(salt_b64);
127 	rc = lutil_b64_ntop((unsigned char *)dk->bv_val, dk->bv_len,
128 						dk_b64, sizeof(dk_b64));
129 	if(rc < 0){
130 		return LUTIL_PASSWD_ERR;
131 	}
132 	b64_to_ab64(dk_b64);
133 	msg_len = asprintf(&msg->bv_val, "%s%d$%s$%s",
134 						   sc->bv_val, iteration,
135 						   salt_b64, dk_b64);
136 	if(msg_len < 0){
137 		msg->bv_len = 0;
138 		return LUTIL_PASSWD_ERR;
139 	}
140 
141 	msg->bv_len = msg_len;
142 	return LUTIL_PASSWD_OK;
143 }
144 
pbkdf2_encrypt(const struct berval * scheme,const struct berval * passwd,struct berval * msg,const char ** text)145 static int pbkdf2_encrypt(
146 	const struct berval *scheme,
147 	const struct berval *passwd,
148 	struct berval *msg,
149 	const char **text)
150 {
151 	unsigned char salt_value[PBKDF2_SALT_SIZE];
152 	struct berval salt;
153 	unsigned char dk_value[PBKDF2_MAX_DK_SIZE];
154 	struct berval dk;
155 	int iteration = PBKDF2_ITERATION;
156 	int rc;
157 #ifdef HAVE_OPENSSL
158 	const EVP_MD *md;
159 #elif HAVE_GNUTLS
160 	struct hmac_sha1_ctx sha1_ctx;
161 	struct hmac_sha256_ctx sha256_ctx;
162 	struct hmac_sha512_ctx sha512_ctx;
163 	void * current_ctx = NULL;
164 	pbkdf2_hmac_update current_hmac_update = NULL;
165 	pbkdf2_hmac_digest current_hmac_digest = NULL;
166 #endif
167 
168 	salt.bv_val = (char *)salt_value;
169 	salt.bv_len = sizeof(salt_value);
170 	dk.bv_val = (char *)dk_value;
171 
172 #ifdef HAVE_OPENSSL
173 	if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
174 		dk.bv_len = PBKDF2_SHA1_DK_SIZE;
175 		md = EVP_sha1();
176 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
177 		dk.bv_len = PBKDF2_SHA1_DK_SIZE;
178 		md = EVP_sha1();
179 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
180 		dk.bv_len = PBKDF2_SHA256_DK_SIZE;
181 		md = EVP_sha256();
182 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
183 		dk.bv_len = PBKDF2_SHA512_DK_SIZE;
184 		md = EVP_sha512();
185 	}else{
186 		return LUTIL_PASSWD_ERR;
187 	}
188 #elif HAVE_GNUTLS
189 	if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
190 		dk.bv_len = PBKDF2_SHA1_DK_SIZE;
191 		current_ctx = &sha1_ctx;
192 		current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
193 		current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
194 		hmac_sha1_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
195 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
196 		dk.bv_len = PBKDF2_SHA1_DK_SIZE;
197 		current_ctx = &sha1_ctx;
198 		current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
199 		current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
200 		hmac_sha1_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
201 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
202 		dk.bv_len = PBKDF2_SHA256_DK_SIZE;
203 		current_ctx = &sha256_ctx;
204 		current_hmac_update = (pbkdf2_hmac_update) &hmac_sha256_update;
205 		current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha256_digest;
206 		hmac_sha256_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
207 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
208 		dk.bv_len = PBKDF2_SHA512_DK_SIZE;
209 		current_ctx = &sha512_ctx;
210 		current_hmac_update = (pbkdf2_hmac_update) &hmac_sha512_update;
211 		current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha512_digest;
212 		hmac_sha512_set_key(current_ctx, passwd->bv_len, (const uint8_t *) passwd->bv_val);
213 	}else{
214 		return LUTIL_PASSWD_ERR;
215 	}
216 #endif
217 
218 	if(lutil_entropy((unsigned char *)salt.bv_val, salt.bv_len) < 0){
219 		return LUTIL_PASSWD_ERR;
220 	}
221 
222 #ifdef HAVE_OPENSSL
223 	if(!PKCS5_PBKDF2_HMAC(passwd->bv_val, passwd->bv_len,
224 						  (unsigned char *)salt.bv_val, salt.bv_len,
225 						  iteration, md, dk.bv_len, dk_value)){
226 		return LUTIL_PASSWD_ERR;
227 	}
228 #elif HAVE_GNUTLS
229 	PBKDF2(current_ctx, current_hmac_update, current_hmac_digest,
230 						  dk.bv_len, iteration,
231 						  salt.bv_len, (const uint8_t *) salt.bv_val,
232 						  dk.bv_len, dk_value);
233 #endif
234 
235 #ifdef SLAPD_PBKDF2_DEBUG
236 	printf("Encrypt for %s\n", scheme->bv_val);
237 	printf("  Password:\t%s\n", passwd->bv_val);
238 
239 	printf("  Salt:\t\t");
240 	int i;
241 	for(i=0; i<salt.bv_len; i++){
242 		printf("%02x", salt_value[i]);
243 	}
244 	printf("\n");
245 	printf("  Iteration:\t%d\n", iteration);
246 
247 	printf("  DK:\t\t");
248 	for(i=0; i<dk.bv_len; i++){
249 		printf("%02x", dk_value[i]);
250 	}
251 	printf("\n");
252 #endif
253 
254 	rc = pbkdf2_format(scheme, iteration, &salt, &dk, msg);
255 
256 #ifdef SLAPD_PBKDF2_DEBUG
257 	printf("  Output:\t%s\n", msg->bv_val);
258 #endif
259 
260 	return rc;
261 }
262 
pbkdf2_check(const struct berval * scheme,const struct berval * passwd,const struct berval * cred,const char ** text)263 static int pbkdf2_check(
264 	const struct berval *scheme,
265 	const struct berval *passwd,
266 	const struct berval *cred,
267 	const char **text)
268 {
269 	int rc;
270 	int iteration;
271 
272 	/* salt_value require PBKDF2_SALT_SIZE + 1 in lutil_b64_pton. */
273 	unsigned char salt_value[PBKDF2_SALT_SIZE + 1];
274 	char salt_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_SALT_SIZE) + 1];
275 	/* dk_value require PBKDF2_MAX_DK_SIZE + 1 in lutil_b64_pton. */
276 	unsigned char dk_value[PBKDF2_MAX_DK_SIZE + 1];
277 	char dk_b64[LUTIL_BASE64_ENCODE_LEN(PBKDF2_MAX_DK_SIZE) + 1];
278 	unsigned char input_dk_value[PBKDF2_MAX_DK_SIZE];
279 	size_t dk_len;
280 #ifdef HAVE_OPENSSL
281 	const EVP_MD *md;
282 #elif HAVE_GNUTLS
283 	struct hmac_sha1_ctx sha1_ctx;
284 	struct hmac_sha256_ctx sha256_ctx;
285 	struct hmac_sha512_ctx sha512_ctx;
286 	void * current_ctx = NULL;
287 	pbkdf2_hmac_update current_hmac_update = NULL;
288 	pbkdf2_hmac_digest current_hmac_digest = NULL;
289 #endif
290 
291 #ifdef SLAPD_PBKDF2_DEBUG
292 	printf("Checking for %s\n", scheme->bv_val);
293 	printf("  Stored Value:\t%s\n", passwd->bv_val);
294 	printf("  Input Cred:\t%s\n", cred->bv_val);
295 #endif
296 
297 #ifdef HAVE_OPENSSL
298 	if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
299 		dk_len = PBKDF2_SHA1_DK_SIZE;
300 		md = EVP_sha1();
301 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
302 		dk_len = PBKDF2_SHA1_DK_SIZE;
303 		md = EVP_sha1();
304 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
305 		dk_len = PBKDF2_SHA256_DK_SIZE;
306 		md = EVP_sha256();
307 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
308 		dk_len = PBKDF2_SHA512_DK_SIZE;
309 		md = EVP_sha512();
310 	}else{
311 		return LUTIL_PASSWD_ERR;
312 	}
313 #elif HAVE_GNUTLS
314 	if(!ber_bvcmp(scheme, &pbkdf2_scheme)){
315 		dk_len = PBKDF2_SHA1_DK_SIZE;
316 		current_ctx = &sha1_ctx;
317 		current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
318 		current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
319 		hmac_sha1_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
320 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha1_scheme)){
321 		dk_len = PBKDF2_SHA1_DK_SIZE;
322 		current_ctx = &sha1_ctx;
323 		current_hmac_update = (pbkdf2_hmac_update) &hmac_sha1_update;
324 		current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha1_digest;
325 		hmac_sha1_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
326 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha256_scheme)){
327 		dk_len = PBKDF2_SHA256_DK_SIZE;
328 		current_ctx = &sha256_ctx;
329 		current_hmac_update = (pbkdf2_hmac_update) &hmac_sha256_update;
330 		current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha256_digest;
331 		hmac_sha256_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
332 	}else if(!ber_bvcmp(scheme, &pbkdf2_sha512_scheme)){
333 		dk_len = PBKDF2_SHA512_DK_SIZE;
334 		current_ctx = &sha512_ctx;
335 		current_hmac_update = (pbkdf2_hmac_update) &hmac_sha512_update;
336 		current_hmac_digest = (pbkdf2_hmac_digest) &hmac_sha512_digest;
337 		hmac_sha512_set_key(current_ctx, cred->bv_len, (const uint8_t *) cred->bv_val);
338 	}else{
339 		return LUTIL_PASSWD_ERR;
340 	}
341 #endif
342 
343 	iteration = atoi(passwd->bv_val);
344 	if(iteration < 1){
345 		return LUTIL_PASSWD_ERR;
346 	}
347 
348 	char *ptr;
349 	ptr = strchr(passwd->bv_val, '$');
350 	if(!ptr){
351 		return LUTIL_PASSWD_ERR;
352 	}
353 	ptr++; /* skip '$' */
354 	rc = ab64_to_b64(ptr, salt_b64, sizeof(salt_b64));
355 	if(rc < 0){
356 		return LUTIL_PASSWD_ERR;
357 	}
358 
359 	ptr = strchr(ptr, '$');
360 	if(!ptr){
361 		return LUTIL_PASSWD_ERR;
362 	}
363 	ptr++; /* skip '$' */
364 	rc = ab64_to_b64(ptr, dk_b64, sizeof(dk_b64));
365 	if(rc < 0){
366 		return LUTIL_PASSWD_ERR;
367 	}
368 
369 	/* The targetsize require PBKDF2_SALT_SIZE + 1 in lutil_b64_pton. */
370 	rc = lutil_b64_pton(salt_b64, salt_value, PBKDF2_SALT_SIZE + 1);
371 	if(rc < 0){
372 		return LUTIL_PASSWD_ERR;
373 	}
374 
375 	/* consistency check */
376 	if(rc != PBKDF2_SALT_SIZE){
377 		return LUTIL_PASSWD_ERR;
378 	}
379 
380 	/* The targetsize require PBKDF2_MAX_DK_SIZE + 1 in lutil_b64_pton. */
381 	rc = lutil_b64_pton(dk_b64, dk_value, sizeof(dk_value));
382 	if(rc < 0){
383 		return LUTIL_PASSWD_ERR;
384 	}
385 
386 	/* consistency check */
387 	if(rc != dk_len){
388 		return LUTIL_PASSWD_ERR;
389 	}
390 
391 #ifdef HAVE_OPENSSL
392 	if(!PKCS5_PBKDF2_HMAC(cred->bv_val, cred->bv_len,
393 						  salt_value, PBKDF2_SALT_SIZE,
394 						  iteration, md, dk_len, input_dk_value)){
395 		return LUTIL_PASSWD_ERR;
396 	}
397 #elif HAVE_GNUTLS
398 	PBKDF2(current_ctx, current_hmac_update, current_hmac_digest,
399 						  dk_len, iteration,
400 						  PBKDF2_SALT_SIZE, salt_value,
401 						  dk_len, input_dk_value);
402 #endif
403 
404 	rc = memcmp(dk_value, input_dk_value, dk_len);
405 #ifdef SLAPD_PBKDF2_DEBUG
406 	printf("  Iteration:\t%d\n", iteration);
407 	printf("  Base64 Salt:\t%s\n", salt_b64);
408 	printf("  Base64 DK:\t%s\n", dk_b64);
409 	int i;
410 	printf("  Stored Salt:\t");
411 	for(i=0; i<PBKDF2_SALT_SIZE; i++){
412 		printf("%02x", salt_value[i]);
413 	}
414 	printf("\n");
415 
416 	printf("  Stored DK:\t");
417 	for(i=0; i<dk_len; i++){
418 		printf("%02x", dk_value[i]);
419 	}
420 	printf("\n");
421 
422 	printf("  Input DK:\t");
423 	for(i=0; i<dk_len; i++){
424 		printf("%02x", input_dk_value[i]);
425 	}
426 	printf("\n");
427 	printf("  Result:\t%d\n", rc);
428 #endif
429 	return rc?LUTIL_PASSWD_ERR:LUTIL_PASSWD_OK;
430 }
431 
init_module(int argc,char * argv[])432 int init_module(int argc, char *argv[]) {
433 	int rc;
434 	rc = lutil_passwd_add((struct berval *)&pbkdf2_scheme,
435 						  pbkdf2_check, pbkdf2_encrypt);
436 	if(rc) return rc;
437 	rc = lutil_passwd_add((struct berval *)&pbkdf2_sha1_scheme,
438 						  pbkdf2_check, pbkdf2_encrypt);
439 	if(rc) return rc;
440 
441 	rc = lutil_passwd_add((struct berval *)&pbkdf2_sha256_scheme,
442 						  pbkdf2_check, pbkdf2_encrypt);
443 	if(rc) return rc;
444 
445 	rc = lutil_passwd_add((struct berval *)&pbkdf2_sha512_scheme,
446 						  pbkdf2_check, pbkdf2_encrypt);
447 	return rc;
448 }
449 
450 /*
451  * Local variables:
452  * indent-tabs-mode: t
453  * tab-width: 4
454  * c-basic-offset: 4
455  * End:
456  */
457