/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 | } |