GCC Code Coverage Report


Directory: src/gate/
File: src/gate/encode/passwords.c
Date: 2026-02-03 22:06:38
Exec Total Coverage
Lines: 97 110 88.2%
Functions: 9 9 100.0%
Branches: 33 54 61.1%

Line Branch Exec Source
1 /* GATE PROJECT LICENSE:
2 +----------------------------------------------------------------------------+
3 | Copyright (c) 2018-2026, Stefan Meislinger <sm@opengate.at> |
4 | All rights reserved. |
5 | |
6 | Redistribution and use in source and binary forms, with or without |
7 | modification, are permitted provided that the following conditions are met:|
8 | |
9 | 1. Redistributions of source code must retain the above copyright notice, |
10 | this list of conditions and the following disclaimer. |
11 | 2. Redistributions in binary form must reproduce the above copyright |
12 | notice, this list of conditions and the following disclaimer in the |
13 | documentation and/or other materials provided with the distribution. |
14 | |
15 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"|
16 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
17 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
18 | ARE DISCLAIMED.IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
19 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
20 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
21 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
22 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
23 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
24 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF |
25 | THE POSSIBILITY OF SUCH DAMAGE. |
26 +----------------------------------------------------------------------------+
27 */
28
29 #include "gate/encode/passwords.h"
30 #include "gate/results.h"
31 #include "gate/strings.h"
32 #include "gate/randomgen.h"
33 #include "gate/encode/sha256hash.h"
34 #include "gate/encode/base64.h"
35
36 #if defined(GATE_SYS_POSIX)
37 # define GATE_ENCODE_CRYPT_IMPL 1
38 #endif
39
40
41 2 static void generate_salt(char salt[16])
42 {
43 gate_randomsession_t rand_session;
44 gate_result_t result;
45 static gate_string_t const valid_chars = GATE_STRING_INIT_STATIC("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890");
46 2 char const* valid_chars_ptr = gate_string_ptr(&valid_chars, 0);
47 2 gate_size_t const valid_chars_len = gate_string_length(&valid_chars);
48
49 2 result = gate_randomgenerator_create(&rand_session);
50
1/2
✓ Branch 0 taken 2 times.
✗ Branch 1 not taken.
2 if (GATE_SUCCEEDED(result))
51 {
52 2 gate_uint64_t num = 0;
53 size_t n;
54
2/2
✓ Branch 0 taken 32 times.
✓ Branch 1 taken 2 times.
34 for(n = 0; n != 16; ++n)
55 {
56 gate_size_t pos;
57 32 gate_randomgenerator_get_num(&rand_session, &num);
58 32 pos = num % valid_chars_len;
59 32 salt[n] = valid_chars_ptr[pos];
60 }
61 2 gate_randomgenerator_destroy(&rand_session);
62 }
63 2 }
64
65 #if defined(GATE_ENCODE_CRYPT_IMPL)
66
67 #include <unistd.h>
68 #include "gate/libraries.h"
69 #include "gate/platforms.h"
70
71 typedef char*(*crypt_func_t)(char const* key, char const* salt);
72
73 2 static crypt_func_t load_libcrypt()
74 {
75 static crypt_func_t crypt_func = NULL;
76
77
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if(crypt_func != NULL)
78 {
79 1 return crypt_func;
80 }
81 else
82 {
83 static gate_library_t lib_libcrypt = NULL;
84 gate_result_t result;
85
86
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if (lib_libcrypt == NULL)
87 {
88 static gate_string_t const libcrypt_name = GATE_STRING_INIT_STATIC("libcrypt.so");
89 1 result = gate_library_open(&libcrypt_name, &lib_libcrypt, GATE_LIBRARY_FLAG_DEFAULT);
90
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(GATE_FAILED(result))
91 {
92 return false;
93 }
94 }
95 1 result = gate_library_get_function_name(lib_libcrypt, "crypt", &crypt_func);
96
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 if(GATE_SUCCEEDED(result))
97 {
98 1 return crypt_func;
99 }
100 return NULL;
101 }
102 }
103
104 1 gate_result_t cryptlib_hash(char const* passwd, gate_size_t passwd_length, gate_uint8_t* passhash, gate_size_t* ptr_passhash_len)
105 {
106 gate_result_t ret;
107
108 do
109 {
110 char input[GATE_PASSWORD_MAX_LENGTH];
111 char setting[64];
112 char salt_buffer[16];
113 char const* ptr_setting;
114 char* ptr_hash;
115 gate_size_t hash_len;
116 1 char* ptr_output = (char*)passhash;
117
1/2
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
1 gate_size_t out_len = ptr_passhash_len ? *ptr_passhash_len : 0;
118 gate_size_t out_used;
119 gate_strbuilder_t builder;
120 1 crypt_func_t crypt_func = load_libcrypt();
121
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(crypt_func == NULL)
123 {
124 ret = GATE_RESULT_NOTAVAILABLE;
125 break;
126 }
127
128
4/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 if (!passwd || !passwd_length || !passhash || !ptr_passhash_len)
129 {
130 ret = GATE_RESULT_INVALIDARG;
131 break;
132 }
133 1 gate_str_print_text(input, sizeof(input), passwd, passwd_length);
134
135 1 generate_salt(salt_buffer);
136
137 1 gate_strbuilder_create_static(&builder, setting, sizeof(setting), 0);
138
139 1 gate_strbuilder_append_cstr(&builder, "$5$");
140 1 gate_strbuilder_append_text(&builder, salt_buffer, sizeof(salt_buffer));
141 1 ptr_setting = gate_strbuilder_ptr(&builder, 0);
142
143 /* NOTICE: crypt() uses a global buffer -> global lock */
144 {
145 1 ret = gate_platform_lock();
146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
147
148 1 ptr_hash = crypt_func(input, ptr_setting);
149 1 hash_len = gate_str_length(ptr_hash);
150 1 *ptr_passhash_len = gate_str_print_text(ptr_output, out_len, ptr_hash, hash_len);
151
152 1 gate_platform_unlock();
153 }
154
155 1 ret = GATE_RESULT_OK;
156 } while (0);
157
158 1 return ret;
159 }
160
161 1 gate_result_t cryptlib_check(char const* passwd, gate_size_t passwd_length, gate_uint8_t const* passhash, gate_size_t passhash_len)
162 {
163 gate_result_t ret;
164
165 do
166 {
167 char input[GATE_PASSWORD_MAX_LENGTH];
168 char setting[GATE_PASSWORD_MAX_LENGTH];
169 gate_size_t setting_length;
170 char output[GATE_PASSWORD_MAX_LENGTH];
171 gate_size_t output_used;
172 char const* ptr_hash;
173 gate_size_t hash_len;
174 1 crypt_func_t crypt_func = load_libcrypt();
175
176
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 if(crypt_func == NULL)
177 {
178 ret = GATE_RESULT_NOTAVAILABLE;
179 break;
180 }
181
182
4/8
✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 1 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 1 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 1 times.
1 if (!passwd || !passwd_length || !passhash || !passhash_len)
183 {
184 ret = GATE_RESULT_INVALIDARG;
185 break;
186 }
187 1 gate_str_print_text(input, sizeof(input), passwd, passwd_length);
188 1 setting_length = gate_str_print_text(setting, sizeof(setting), (char const*)passhash, passhash_len);
189
190 /* NOTICE: crypt() uses a global buffer -> global lock */
191 {
192 1 ret = gate_platform_lock();
193
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
1 GATE_BREAK_IF_FAILED(ret);
194
195 1 ptr_hash = crypt_func(input, setting);
196 1 hash_len = gate_str_length(ptr_hash);
197 1 output_used = gate_str_print_text(output, sizeof(output), ptr_hash, hash_len);
198
199 1 gate_platform_unlock();
200 }
201
202
1/2
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
1 if(gate_str_compare(setting, setting_length, output, output_used) == 0)
203 {
204 1 ret = GATE_RESULT_OK;
205 }
206 else
207 {
208 ret = GATE_RESULT_NOMATCH;
209 }
210 } while (0);
211
212 1 return ret;
213 }
214
215 #endif /* GATE_ENCODE_CRYPT_IMPL */
216
217 3 static void pass_hash_sha256_salt(
218 char const* passwd, gate_size_t passwd_length,
219 char const* salt, gate_size_t salt_length,
220 gate_uint8_t* passhash, gate_size_t* ptr_passhash_len)
221 {
222 char buffer[GATE_PASSWORD_MAX_LENGTH + 128];
223 static gate_size_t const buffer_capacity = sizeof(buffer);
224 static gate_size_t const rounds = 5000;
225 gate_size_t ndx;
226
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 gate_size_t hash_capacity = ptr_passhash_len ? *ptr_passhash_len : 0;
227
228 gate_strbuilder_t builder;
229 gate_string_t binstr;
230 gate_sha256_t sha;
231 gate_sha256_result_t sharesult;
232
233 3 gate_sha256_init(&sha);
234
235
2/2
✓ Branch 0 taken 15000 times.
✓ Branch 1 taken 3 times.
15003 for(ndx = 0; ndx != rounds; ++ndx)
236 {
237 15000 gate_sha256_update(&sha, passwd, passwd_length);
238 15000 gate_sha256_update(&sha, salt, salt_length);
239 15000 gate_sha256_update(&sha, passwd, passwd_length);
240 15000 gate_sha256_finish(&sha, &sharesult);
241
242 15000 gate_sha256_init(&sha);
243 15000 gate_sha256_update(&sha, sharesult.hash, sizeof(sharesult.hash));
244 }
245
246 3 gate_strbuilder_create_static(&builder, (char*)passhash, hash_capacity, 0);
247 3 gate_strbuilder_append_cstr(&builder, "$5$");
248 3 gate_strbuilder_append_text(&builder, salt, salt_length);
249 3 gate_strbuilder_append_cstr(&builder, "$");
250
251 3 gate_string_create_static_len(&binstr, (char const*)&sharesult.hash[0], sizeof(sharesult.hash));
252
253 3 gate_base64_encode(&binstr, &builder);
254 3 *ptr_passhash_len = gate_strbuilder_length(&builder);
255 3 }
256
257 1 static gate_result_t pass_hash_sha256(char const* passwd, gate_size_t passwd_length, gate_uint8_t* passhash, gate_size_t* ptr_passhash_len)
258 {
259 char salt[16];
260 1 generate_salt(salt);
261 1 pass_hash_sha256_salt(passwd, passwd_length, salt, sizeof(salt), passhash, ptr_passhash_len);
262 1 return GATE_RESULT_OK;
263 }
264
265 2 gate_result_t pass_check_sha256(char const* passwd, gate_size_t passwd_length, gate_uint8_t const* passhash, gate_size_t passhash_len)
266 {
267 gate_size_t pos;
268 char const* ptr_salt;
269 gate_size_t salt_len;
270 gate_uint8_t test_hash[GATE_PASSWORD_MAX_HASH_LENGTH];
271 2 gate_size_t test_hash_len = sizeof(test_hash);
272
273
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 2 times.
2 if (!gate_str_starts_with((char const*)passhash, passhash_len, "$5$", 3))
274 {
275 return GATE_RESULT_NOMATCH;
276 }
277 2 pos = gate_str_char_pos((char const*)passhash, passhash_len, '$', 3);
278
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2 times.
2 if (GATE_STR_NPOS == pos)
279 {
280 return GATE_RESULT_NOMATCH;
281 }
282 2 ptr_salt = (char const*)(&passhash[3]);
283 // "$5$salt$hash"
284 2 salt_len = pos - 3;
285
286 2 pass_hash_sha256_salt(passwd, passwd_length, ptr_salt, salt_len, test_hash, &test_hash_len);
287
2/2
✓ Branch 1 taken 1 times.
✓ Branch 2 taken 1 times.
2 if (gate_str_compare((char const*)passhash, passhash_len, (char const*)test_hash, test_hash_len) != 0)
288 {
289 1 return GATE_RESULT_NOMATCH;
290 }
291 1 return GATE_RESULT_OK;
292 }
293
294
295
296 2 gate_result_t gate_password_hash(
297 char const* passwd, gate_size_t passwd_length,
298 gate_uint8_t* passhash, gate_size_t* ptr_passhash_len,
299 gate_enumint_t flags)
300 {
301 #if defined(GATE_ENCODE_CRYPT_IMPL)
302
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
2 if (GATE_FLAG_ENABLED(flags, GATE_PASSWORD_FLAG_SYSTEM))
303 {
304 1 return cryptlib_hash(passwd, passwd_length, passhash, ptr_passhash_len);
305 }
306 #endif /* GATE_ENCODE_CRYPT_IMPL */
307
308 1 return pass_hash_sha256(passwd, passwd_length, passhash, ptr_passhash_len);
309 }
310
311
312
313 3 gate_result_t gate_password_check(
314 char const* passwd, gate_size_t passwd_length,
315 gate_uint8_t const* passhash, gate_size_t passhash_len,
316 gate_enumint_t flags)
317 {
318 #if defined(GATE_ENCODE_CRYPT_IMPL)
319
2/2
✓ Branch 0 taken 1 times.
✓ Branch 1 taken 2 times.
3 if (GATE_FLAG_ENABLED(flags, GATE_PASSWORD_FLAG_SYSTEM))
320 {
321 1 return cryptlib_check(passwd, passwd_length, passhash, passhash_len);
322 }
323 #endif /* GATE_ENCODE_CRYPT_IMPL */
324
325 2 return pass_check_sha256(passwd, passwd_length, passhash, passhash_len);
326 }
327