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