Coverage Report

Created: 2026-05-16 06:51

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/dovecot/src/lib-auth/auth-scram.c
Line
Count
Source
1
/* Copyright (c) 2022-2023 Dovecot authors, see the included COPYING file */
2
3
#include "lib.h"
4
#include "safe-memset.h"
5
#include "base64.h"
6
#include "hmac.h"
7
#include "randgen.h"
8
#include "str.h"
9
10
#include "auth-scram.h"
11
12
void auth_scram_key_data_clear(struct auth_scram_key_data *data)
13
1.64k
{
14
1.64k
  if (data->hmethod != NULL) {
15
1.31k
    if (data->stored_key != NULL) {
16
1.31k
      safe_memset(data->stored_key, 0,
17
1.31k
            data->hmethod->digest_size);
18
1.31k
    }
19
1.31k
    if (data->server_key != NULL) {
20
1.31k
      safe_memset(data->server_key, 0,
21
1.31k
            data->hmethod->digest_size);
22
1.31k
    }
23
1.31k
  } else {
24
321
    i_assert(data->stored_key == NULL);
25
321
    i_assert(data->server_key == NULL);
26
321
  }
27
1.64k
}
28
29
void auth_scram_hi(const struct hash_method *hmethod,
30
       const unsigned char *str, size_t str_size,
31
       const unsigned char *salt, size_t salt_size, unsigned int i,
32
       unsigned char *result)
33
2.24k
{
34
2.24k
  struct hmac_context ctx;
35
2.24k
  unsigned char U[hmethod->digest_size];
36
2.24k
  unsigned int j, k;
37
38
  /* Hi(str, salt, i):
39
40
     U1   := HMAC(str, salt + INT(1))
41
     U2   := HMAC(str, U1)
42
     ...
43
     Ui-1 := HMAC(str, Ui-2)
44
     Ui   := HMAC(str, Ui-1)
45
46
     Hi := U1 XOR U2 XOR ... XOR Ui
47
48
      where "i" is the iteration count, "+" is the string concatenation
49
      operator, and INT(g) is a 4-octet encoding of the integer g, most
50
      significant octet first.
51
  */
52
53
  /* Calculate U1 */
54
2.24k
  hmac_init(&ctx, str, str_size, hmethod);
55
2.24k
  hmac_update(&ctx, salt, salt_size);
56
2.24k
  hmac_update(&ctx, "\0\0\0\1", 4);
57
2.24k
  hmac_final(&ctx, U);
58
59
2.24k
  memcpy(result, U, hmethod->digest_size);
60
61
  /* Calculate U2 to Ui and Hi */
62
9.12M
  for (j = 2; j <= i; j++) {
63
9.12M
    hmac_init(&ctx, str, str_size, hmethod);
64
9.12M
    hmac_update(&ctx, U, sizeof(U));
65
9.12M
    hmac_final(&ctx, U);
66
228M
    for (k = 0; k < hmethod->digest_size; k++)
67
219M
      result[k] ^= U[k];
68
9.12M
  }
69
2.24k
}
70
71
void auth_scram_generate_key_data(const struct hash_method *hmethod,
72
          const char *plaintext, unsigned int rounds,
73
          unsigned int *iter_count_r,
74
          const char **salt_r,
75
          unsigned char stored_key_r[],
76
          unsigned char server_key_r[])
77
1.31k
{
78
1.31k
  struct hmac_context ctx;
79
1.31k
  unsigned char salt[16];
80
1.31k
  unsigned char salted_password[hmethod->digest_size];
81
1.31k
  unsigned char client_key[hmethod->digest_size];
82
83
1.31k
  if (rounds == 0)
84
1.31k
    rounds = AUTH_SCRAM_DEFAULT_ITERATE_COUNT;
85
0
  else {
86
0
    rounds = I_MAX(I_MIN(AUTH_SCRAM_MAX_ITERATE_COUNT, rounds),
87
0
             AUTH_SCRAM_MIN_ITERATE_COUNT);
88
0
  }
89
1.31k
  *iter_count_r = rounds;
90
91
1.31k
  random_fill(salt, sizeof(salt));
92
1.31k
  *salt_r = str_c(t_base64_encode(0, 0, salt, sizeof(salt)));
93
94
  /* FIXME: credentials should be SASLprepped UTF8 data here */
95
1.31k
  auth_scram_hi(hmethod,
96
1.31k
          (const unsigned char *)plaintext, strlen(plaintext),
97
1.31k
          salt, sizeof(salt), rounds, salted_password);
98
99
  /* Calculate ClientKey */
100
1.31k
  hmac_init(&ctx, salted_password, sizeof(salted_password), hmethod);
101
1.31k
  hmac_update(&ctx, "Client Key", 10);
102
1.31k
  hmac_final(&ctx, client_key);
103
104
  /* Calculate StoredKey */
105
1.31k
  hash_method_get_digest(hmethod, client_key, sizeof(client_key),
106
1.31k
             stored_key_r);
107
108
  /* Calculate ServerKey */
109
1.31k
  hmac_init(&ctx, salted_password, sizeof(salted_password), hmethod);
110
1.31k
  hmac_update(&ctx, "Server Key", 10);
111
1.31k
  hmac_final(&ctx, server_key_r);
112
113
1.31k
  safe_memset(salted_password, 0, sizeof(salted_password));
114
1.31k
  safe_memset(client_key, 0, sizeof(client_key));
115
1.31k
}
116
117
int auth_scram_credentials_parse(const struct hash_method *hmethod,
118
         const char *name,
119
         const unsigned char *credentials, size_t size,
120
         unsigned int *iter_count_r,
121
         const char **salt_r,
122
         unsigned char stored_key_r[],
123
         unsigned char server_key_r[],
124
         const char **error_r)
125
1.31k
{
126
1.31k
  const char *const *fields;
127
1.31k
  buffer_t *buf;
128
129
  /* password string format: iter,salt,stored_key,server_key */
130
1.31k
  fields = t_strsplit(t_strndup(credentials, size), ",");
131
132
1.31k
  if (str_array_length(fields) != 4) {
133
0
    *error_r = t_strdup_printf(
134
0
      "Invalid %s passdb entry format", name);
135
0
    return -1;
136
0
  }
137
1.31k
  if (str_to_uint(fields[0], iter_count_r) < 0 ||
138
1.31k
      *iter_count_r < AUTH_SCRAM_MIN_ITERATE_COUNT ||
139
1.31k
      *iter_count_r > AUTH_SCRAM_MAX_ITERATE_COUNT) {
140
0
    *error_r = t_strdup_printf(
141
0
      "Invalid %s iteration count in passdb", name);
142
0
    return -1;
143
0
  }
144
1.31k
  *salt_r = fields[1];
145
146
1.31k
  buf = t_buffer_create(hmethod->digest_size);
147
1.31k
  if (base64_decode(fields[2], strlen(fields[2]), buf) < 0 ||
148
1.31k
      buf->used != hmethod->digest_size) {
149
0
    *error_r = t_strdup_printf(
150
0
      "Invalid %s StoredKey in passdb", name);
151
0
    return -1;
152
0
  }
153
1.31k
  memcpy(stored_key_r, buf->data, hmethod->digest_size);
154
155
1.31k
  buffer_set_used_size(buf, 0);
156
1.31k
  if (base64_decode(fields[3], strlen(fields[3]), buf) < 0 ||
157
1.31k
      buf->used != hmethod->digest_size) {
158
0
    *error_r = t_strdup_printf(
159
0
      "Invalid %s ServerKey in passdb", name);
160
0
    return -1;
161
0
  }
162
1.31k
  memcpy(server_key_r, buf->data, hmethod->digest_size);
163
1.31k
  return 0;
164
1.31k
}