Coverage Report

Created: 2025-08-26 07:05

/src/proftpd/lib/openbsd-bcrypt.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Copyright (c) 2014 Ted Unangst <tedu@openbsd.org>
3
 * Copyright (c) 1997 Niels Provos <provos@umich.edu>
4
 *
5
 * Permission to use, copy, modify, and distribute this software for any
6
 * purpose with or without fee is hereby granted, provided that the above
7
 * copyright notice and this permission notice appear in all copies.
8
 *
9
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16
 */
17
/* This password hashing algorithm was designed by David Mazieres
18
 * <dm@lcs.mit.edu> and works as follows:
19
 *
20
 * 1. state := InitState ()
21
 * 2. state := ExpandKey (state, salt, password)
22
 * 3. REPEAT rounds:
23
 *      state := ExpandKey (state, 0, password)
24
 *  state := ExpandKey (state, 0, salt)
25
 * 4. ctext := "OrpheanBeholderScryDoubt"
26
 * 5. REPEAT 64:
27
 *  ctext := Encrypt_ECB (state, ctext);
28
 * 6. RETURN Concatenate (salt, ctext);
29
 *
30
 */
31
32
#include "config.h"
33
34
#include <sys/types.h>
35
#include "openbsd-blowfish.h"
36
#include <ctype.h>
37
#include <errno.h>
38
#include <stdio.h>
39
#include <stdlib.h>
40
#include <string.h>
41
42
/* This implementation is adaptable to current computing power.
43
 * You can have up to 2^31 rounds which should be enough for some
44
 * time to come.
45
 */
46
47
0
#define BCRYPT_VERSION '2'
48
0
#define BCRYPT_MAXSALT 16  /* Precomputation is just so nice */
49
0
#define BCRYPT_WORDS 6    /* Ciphertext words */
50
0
#define BCRYPT_MINLOGROUNDS 4  /* we have log2(rounds) in salt */
51
52
#define BCRYPT_SALTSPACE  (7 + (BCRYPT_MAXSALT * 4 + 2) / 3 + 1)
53
0
#define BCRYPT_HASHSPACE  61
54
55
static int encode_base64(char *, const u_int8_t *, size_t);
56
static int decode_base64(u_int8_t *, size_t, const char *);
57
58
/*
59
 * the core bcrypt function
60
 */
61
int
62
bcrypt_hashpass(const char *key, const char *salt, char *encrypted,
63
    size_t encryptedlen)
64
0
{
65
0
  blf_ctx state;
66
0
  u_int32_t rounds, i, k;
67
0
  u_int16_t j;
68
0
  size_t key_len;
69
0
  u_int8_t salt_len, logr, minor;
70
0
  u_int8_t ciphertext[4 * BCRYPT_WORDS] = "OrpheanBeholderScryDoubt";
71
0
  u_int8_t csalt[BCRYPT_MAXSALT];
72
0
  u_int32_t cdata[BCRYPT_WORDS];
73
74
0
  if (encryptedlen < BCRYPT_HASHSPACE)
75
0
    goto inval;
76
77
  /* Check and discard "$" identifier */
78
0
  if (salt[0] != '$')
79
0
    goto inval;
80
0
  salt += 1;
81
82
0
  if (salt[0] != BCRYPT_VERSION)
83
0
    goto inval;
84
85
  /* Check for minor versions */
86
0
  switch ((minor = salt[1])) {
87
0
  case 'a':
88
0
    key_len = (u_int8_t)(strlen(key) + 1);
89
0
    break;
90
0
  case 'b':
91
    /* strlen() returns a size_t, but the function calls
92
     * below result in implicit casts to a narrower integer
93
     * type, so cap key_len at the actual maximum supported
94
     * length here to avoid integer wraparound */
95
0
    key_len = strlen(key);
96
0
    if (key_len > 72)
97
0
      key_len = 72;
98
0
    key_len++; /* include the NUL */
99
0
    break;
100
0
  case 'y':
101
    /* PHP-specific version; see:
102
     *  https://www.php.net/manual/en/function.password-hash.php
103
     */
104
0
    key_len = (u_int8_t)(strlen(key) + 1);
105
0
    break;
106
0
  default:
107
0
     goto inval;
108
0
  }
109
0
  if (salt[2] != '$')
110
0
    goto inval;
111
  /* Discard version + "$" identifier */
112
0
  salt += 3;
113
114
  /* Check and parse num rounds */
115
0
  if (!isdigit((unsigned char)salt[0]) ||
116
0
      !isdigit((unsigned char)salt[1]) || salt[2] != '$')
117
0
    goto inval;
118
0
  logr = (salt[1] - '0') + ((salt[0] - '0') * 10);
119
0
  if (logr < BCRYPT_MINLOGROUNDS || logr > 31)
120
0
    goto inval;
121
  /* Computer power doesn't increase linearly, 2^x should be fine */
122
0
  rounds = 1U << logr;
123
124
  /* Discard num rounds + "$" identifier */
125
0
  salt += 3;
126
127
0
  if (strlen(salt) * 3 / 4 < BCRYPT_MAXSALT)
128
0
    goto inval;
129
130
  /* We dont want the base64 salt but the raw data */
131
0
  if (decode_base64(csalt, BCRYPT_MAXSALT, salt))
132
0
    goto inval;
133
0
  salt_len = BCRYPT_MAXSALT;
134
135
  /* Setting up S-Boxes and Subkeys */
136
0
  Blowfish_initstate(&state);
137
0
  Blowfish_expandstate(&state, csalt, salt_len,
138
0
      (u_int8_t *) key, key_len);
139
0
  for (k = 0; k < rounds; k++) {
140
0
    Blowfish_expand0state(&state, (u_int8_t *) key, key_len);
141
0
    Blowfish_expand0state(&state, csalt, salt_len);
142
0
  }
143
144
  /* This can be precomputed later */
145
0
  j = 0;
146
0
  for (i = 0; i < BCRYPT_WORDS; i++)
147
0
    cdata[i] = Blowfish_stream2word(ciphertext, 4 * BCRYPT_WORDS, &j);
148
149
  /* Now do the encryption */
150
0
  for (k = 0; k < 64; k++)
151
0
    blf_enc(&state, cdata, BCRYPT_WORDS / 2);
152
153
0
  for (i = 0; i < BCRYPT_WORDS; i++) {
154
0
    ciphertext[4 * i + 3] = cdata[i] & 0xff;
155
0
    cdata[i] = cdata[i] >> 8;
156
0
    ciphertext[4 * i + 2] = cdata[i] & 0xff;
157
0
    cdata[i] = cdata[i] >> 8;
158
0
    ciphertext[4 * i + 1] = cdata[i] & 0xff;
159
0
    cdata[i] = cdata[i] >> 8;
160
0
    ciphertext[4 * i + 0] = cdata[i] & 0xff;
161
0
  }
162
163
164
0
  snprintf(encrypted, 8, "$2%c$%2.2u$", minor, logr);
165
0
  encode_base64(encrypted + 7, csalt, BCRYPT_MAXSALT);
166
0
  encode_base64(encrypted + 7 + 22, ciphertext, 4 * BCRYPT_WORDS - 1);
167
0
#if defined(HAVE_EXPLICIT_BZERO)
168
0
  explicit_bzero(&state, sizeof(state));
169
0
  explicit_bzero(ciphertext, sizeof(ciphertext));
170
0
  explicit_bzero(csalt, sizeof(csalt));
171
0
  explicit_bzero(cdata, sizeof(cdata));
172
#elif defined(HAVE_MEMSET_S)
173
  (void) memset_s(&state, sizeof(state), '\0', sizeof(state));
174
  (void) memset_s(ciphertext, sizeof(ciphertext), '\0', sizeof(ciphertext));
175
  (void) memset_s(csalt, sizeof(csalt), '\0', sizeof(csalt));
176
  (void) memset_s(cdata, sizeof(cdata), '\0', sizeof(cdata));
177
#else
178
  memset(&state, '\0', sizeof(state));
179
  memset(ciphertext, '\0', sizeof(ciphertext));
180
  memset(csalt, '\0', sizeof(csalt));
181
  memset(cdata, '\0', sizeof(cdata));
182
#endif
183
0
  return 0;
184
185
0
inval:
186
0
  errno = EINVAL;
187
0
  return -1;
188
0
}
189
190
/*
191
 * internal utilities
192
 */
193
static const u_int8_t Base64Code[] =
194
"./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
195
196
static const u_int8_t index_64[128] = {
197
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
198
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
199
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
200
  255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
201
  255, 255, 255, 255, 255, 255, 0, 1, 54, 55,
202
  56, 57, 58, 59, 60, 61, 62, 63, 255, 255,
203
  255, 255, 255, 255, 255, 2, 3, 4, 5, 6,
204
  7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
205
  17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
206
  255, 255, 255, 255, 255, 255, 28, 29, 30,
207
  31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
208
  41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
209
  51, 52, 53, 255, 255, 255, 255, 255
210
};
211
0
#define CHAR64(c)  ( (c) > 127 ? 255 : index_64[(c)])
212
213
/*
214
 * read buflen (after decoding) bytes of data from b64data
215
 */
216
static int
217
decode_base64(u_int8_t *buffer, size_t len, const char *b64data)
218
0
{
219
0
  u_int8_t *bp = buffer;
220
0
  const u_int8_t *p = (const unsigned char *) b64data;
221
0
  u_int8_t c1, c2, c3, c4;
222
223
0
  while (bp < buffer + len) {
224
0
    c1 = CHAR64(*p);
225
    /* Invalid data */
226
0
    if (c1 == 255)
227
0
      return -1;
228
229
0
    c2 = CHAR64(*(p + 1));
230
0
    if (c2 == 255)
231
0
      return -1;
232
233
0
    *bp++ = (c1 << 2) | ((c2 & 0x30) >> 4);
234
0
    if (bp >= buffer + len)
235
0
      break;
236
237
0
    c3 = CHAR64(*(p + 2));
238
0
    if (c3 == 255)
239
0
      return -1;
240
241
0
    *bp++ = ((c2 & 0x0f) << 4) | ((c3 & 0x3c) >> 2);
242
0
    if (bp >= buffer + len)
243
0
      break;
244
245
0
    c4 = CHAR64(*(p + 3));
246
0
    if (c4 == 255)
247
0
      return -1;
248
0
    *bp++ = ((c3 & 0x03) << 6) | c4;
249
250
0
    p += 4;
251
0
  }
252
0
  return 0;
253
0
}
254
255
/*
256
 * Turn len bytes of data into base64 encoded data.
257
 * This works without = padding.
258
 */
259
int
260
encode_base64(char *b64buffer, const u_int8_t *data, size_t len)
261
0
{
262
0
  u_int8_t *bp = (unsigned char *) b64buffer;
263
0
  const u_int8_t *p = data;
264
0
  u_int8_t c1, c2;
265
266
0
  while (p < data + len) {
267
0
    c1 = *p++;
268
0
    *bp++ = Base64Code[(c1 >> 2)];
269
0
    c1 = (c1 & 0x03) << 4;
270
0
    if (p >= data + len) {
271
0
      *bp++ = Base64Code[c1];
272
0
      break;
273
0
    }
274
0
    c2 = *p++;
275
0
    c1 |= (c2 >> 4) & 0x0f;
276
0
    *bp++ = Base64Code[c1];
277
0
    c1 = (c2 & 0x0f) << 2;
278
0
    if (p >= data + len) {
279
0
      *bp++ = Base64Code[c1];
280
0
      break;
281
0
    }
282
0
    c2 = *p++;
283
0
    c1 |= (c2 >> 6) & 0x03;
284
0
    *bp++ = Base64Code[c1];
285
0
    *bp++ = Base64Code[c2 & 0x3f];
286
0
  }
287
0
  *bp = '\0';
288
0
  return 0;
289
0
}