1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
|
/* Copyright (c) 2015-2018 Dovecot authors, see the included COPYING file */
#include "lib.h"
#include "buffer.h"
#include "str.h"
#include "password-scheme.h"
#include "hex-binary.h"
#include "hash-method.h"
#include "pkcs5.h"
#define PBKDF2_KEY_SIZE_SHA1 20
#define PBKDF2_GENERATE_SALT_LEN 16
#define PBKDF2_ROUNDS_DEFAULT 5000
static void
pbkdf_run(const char *plaintext, const char *salt,
unsigned int rounds, unsigned char key_r[PBKDF2_KEY_SIZE_SHA1])
{
memset(key_r, 0, PBKDF2_KEY_SIZE_SHA1);
buffer_t buf;
buffer_create_from_data(&buf, key_r, PBKDF2_KEY_SIZE_SHA1);
pkcs5_pbkdf(PKCS5_PBKDF2, hash_method_lookup("sha1"),
(const unsigned char *)plaintext, strlen(plaintext),
(const unsigned char *)salt, strlen(salt),
rounds, PBKDF2_KEY_SIZE_SHA1, &buf);
}
void pbkdf2_generate(const char *plaintext, const struct password_generate_params *params ATTR_UNUSED,
const unsigned char **raw_password_r, size_t *size_r)
{
unsigned char key[PBKDF2_KEY_SIZE_SHA1];
const char *salt;
string_t *str = t_str_new(64);
unsigned int rounds = params->rounds;
if (rounds == 0)
rounds = PBKDF2_ROUNDS_DEFAULT;
salt = password_generate_salt(PBKDF2_GENERATE_SALT_LEN);
pbkdf_run(plaintext, salt, rounds, key);
str_printfa(str, "$1$%s$%u$", salt, rounds);
binary_to_hex_append(str, key, sizeof(key));
*raw_password_r = str_data(str);
*size_r = str_len(str);
}
int pbkdf2_verify(const char *plaintext, const struct password_generate_params *params ATTR_UNUSED,
const unsigned char *raw_password, size_t size,
const char **error_r)
{
const char *const *fields;
const char *salt;
unsigned int rounds;
unsigned char key1[PBKDF2_KEY_SIZE_SHA1], key2[PBKDF2_KEY_SIZE_SHA1];
buffer_t buf;
/* $1$salt$rounds$hash */
if (size < 3 || memcmp(raw_password, "$1$", 3) != 0) {
*error_r = "Invalid PBKDF2 passdb entry prefix";
return -1;
}
fields = t_strsplit(t_strndup(raw_password + 3, size - 3), "$");
salt = fields[0];
if (str_array_length(fields) != 3 ||
str_to_uint(fields[1], &rounds) < 0) {
*error_r = "Invalid PBKDF2 passdb entry format";
return -1;
}
buffer_create_from_data(&buf, key1, sizeof(key1));
if (strlen(fields[2]) != sizeof(key1)*2 ||
hex_to_binary(fields[2], &buf) < 0) {
*error_r = "PBKDF2 hash not 160bit hex-encoded";
return -1;
}
pbkdf_run(plaintext, salt, rounds, key2);
return mem_equals_timing_safe(key1, key2, sizeof(key1)) ? 1 : 0;
}
|