1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
|
/* Test that the prefix argument to crypt_gensalt affects only the
choice of hashing method, not any of the parameters or the salt.
Written by Zack Weinberg <zackw at panix.com> in 2018.
To the extent possible under law, Zack Weinberg has waived all
copyright and related or neighboring rights to this work.
See https://creativecommons.org/publicdomain/zero/1.0/ for further
details. */
#include "crypt-port.h"
#include <errno.h>
#include <stdio.h>
/* Random bytes used when calling crypt_gensalt; for determinism, these
are fixed from run to run. */
#define N_RBYTES 64ul
static const char rbytes1[] =
"90idUkI2+mu2E/tMTViD418j2sPdEYq9LYq0yRW7RYhr4RqQ+oVzIIEcfJBqpf/D";
static const char rbytes2[] =
"sEwXQxrjBTEADauxCpvOQqq7iU9oq6uJ+Iux/fbhtLRj1MWgBFyo/t+nh/nzm0Kn";
static_assert(sizeof rbytes1 == N_RBYTES + 1, "rbytes1 is wrong length");
static_assert(sizeof rbytes2 == N_RBYTES + 1, "rbytes2 is wrong length");
struct testcase
{
const char *prefix;
unsigned long count1;
unsigned long count2;
};
/* This list should include one entry for each potentially-supported
hash prefix. If the hash method has tunable cost, set count1 and
count2 to two different nonzero values, within the supported cost
range. Neither value should equal the default cost. If the hash
method does not have tunable cost, set count1 and count2 to zero. */
static const struct testcase testcases[] =
{
#if INCLUDE_descrypt || INCLUDE_bigcrypt
{ "", 0, 0 },
#endif
#if INCLUDE_bsdicrypt
{ "_", 7019, 1120211 },
#endif
#if INCLUDE_nt
{ "$3$", 0, 0 },
#endif
#if INCLUDE_md5crypt
{ "$1$", 0, 0 },
#endif
#if INCLUDE_sunmd5
{ "$md5", 7019, 1120211 },
#endif
#if INCLUDE_sha1crypt
{ "$sha1", 7019, 1120211 },
#endif
#if INCLUDE_sha256crypt
{ "$5$", 7019, 1120211 },
#endif
#if INCLUDE_sha512crypt
{ "$6$", 7019, 1120211 },
#endif
#if INCLUDE_bcrypt
{ "$2b$", 7, 11 },
#endif
#if INCLUDE_bcrypt_y
{ "$2y$", 7, 11 },
#endif
#if INCLUDE_bcrypt_a
{ "$2a$", 7, 11 },
#endif
#if INCLUDE_scrypt
{ "$7$", 7, 11, },
#endif
#if INCLUDE_yescrypt
{ "$y$", 7, 11, },
#endif
#if INCLUDE_gost_yescrypt
{ "$gy$", 7, 11, },
#endif
{ 0, 0, 0, }
};
static int
do_crypt_gensalt(const char *prefix,
const char rbytes[MIN_SIZE(N_RBYTES)],
unsigned long count,
char outbuf[MIN_SIZE(CRYPT_GENSALT_OUTPUT_SIZE)])
{
/* Detect failure to NUL-terminate the output properly. */
static int ncalls = 0;
memset(outbuf, '!' + (ncalls % ('~' - '!' + 1)),
CRYPT_GENSALT_OUTPUT_SIZE - 1);
outbuf[CRYPT_GENSALT_OUTPUT_SIZE - 1] = 0;
ncalls++;
char *rv = crypt_gensalt_rn(prefix, count, rbytes, N_RBYTES,
outbuf, CRYPT_GENSALT_OUTPUT_SIZE);
if (rv == 0)
{
printf("ERROR: gensalt(%s, %lu, %c%c..., %lu, outbuf, %lu) = 0/%s\n",
prefix, count, rbytes[0], rbytes[1],
N_RBYTES, (unsigned long)CRYPT_GENSALT_OUTPUT_SIZE,
strerror(errno));
outbuf[0] = '*';
memset (outbuf+1, 0, CRYPT_GENSALT_OUTPUT_SIZE-1);
return 1;
}
else if (rv[0] == '*')
{
printf("ERROR: gensalt(%s, %lu, %c%c..., %lu, outbuf, %lu) = %s/%s\n",
prefix, count, rbytes[0], rbytes[1],
N_RBYTES, (unsigned long)CRYPT_GENSALT_OUTPUT_SIZE,
outbuf, strerror(errno));
outbuf[0] = '*';
memset (outbuf+1, 0, CRYPT_GENSALT_OUTPUT_SIZE-1);
return 1;
}
else
return 0;
}
static int
do_check_equal(const char *stst, const char *sref,
const char *prefix, const char rbytes[N_RBYTES],
unsigned long count, const char *setting)
{
if (!strcmp(stst, sref))
return 0;
printf("FAIL: expected %s\n"
" got %s\n"
" from %s, %lu, %c%c...\n"
" and %s\n",
sref, stst, prefix, count, rbytes[0], rbytes[1], setting);
return 1;
}
int
main(void)
{
int status = 0;
char sref[6][CRYPT_GENSALT_OUTPUT_SIZE];
char stst[CRYPT_GENSALT_OUTPUT_SIZE];
for (size_t i = 0; testcases[i].prefix; i++)
{
const char *prefix = testcases[i].prefix;
unsigned long count1 = testcases[i].count1;
unsigned long count2 = testcases[i].count2;
int ncases;
memset(sref, 0, sizeof sref);
/* If count1 and count2 are both nonzero, then they should also
be unequal, and we have six reference cases:
(0, count1, count2) x (rbytes1, rbytes2).
If count1 and count2 are both zero, then we only have two
reference cases: 0 x (rbytes1, rbytes2) (this happens when the
hash method doesn't have tunable cost).
It is incorrect for only one of count1 and count2 to be zero,
or for them to be equal but nonzero. */
if (count1 == 0 && count2 == 0)
{
ncases = 2;
status |= do_crypt_gensalt(prefix, rbytes1, 0, sref[0]);
status |= do_crypt_gensalt(prefix, rbytes2, 0, sref[1]);
}
else if (count1 != 0 && count2 != 0 && count1 != count2)
{
ncases = 6;
status |= do_crypt_gensalt(prefix, rbytes1, 0, sref[0]);
status |= do_crypt_gensalt(prefix, rbytes2, 0, sref[1]);
status |= do_crypt_gensalt(prefix, rbytes1, count1, sref[2]);
status |= do_crypt_gensalt(prefix, rbytes2, count1, sref[3]);
status |= do_crypt_gensalt(prefix, rbytes1, count2, sref[4]);
status |= do_crypt_gensalt(prefix, rbytes2, count2, sref[5]);
}
else
{
printf ("ERROR: %zu/%s: inappropriate count1=%lu count2=%lu\n",
i, prefix, count1, count2);
status = 1;
continue;
}
/* At this point, sref[0..ncases] are filled with setting
strings corresponding to different combinations of salt and
cost. If we reuse those strings as prefixes for crypt_gensalt,
none of the additional information should affect the output. */
for (int j = 0; j < ncases; j++)
{
if (sref[j][0] == '*')
continue; /* initial crypt_gensalt call failed */
if (count1 == 0 && count2 == 0)
{
status |= do_crypt_gensalt(sref[j], rbytes1, 0, stst);
status |= do_check_equal(stst, sref[0],
prefix, rbytes1, 0, sref[j]);
status |= do_crypt_gensalt(sref[j], rbytes2, 0, stst);
status |= do_check_equal(stst, sref[1],
prefix, rbytes2, 0, sref[j]);
}
else
{
status |= do_crypt_gensalt(sref[j], rbytes1, 0, stst);
status |= do_check_equal(stst, sref[0],
prefix, rbytes1, 0, sref[j]);
status |= do_crypt_gensalt(sref[j], rbytes2, 0, stst);
status |= do_check_equal(stst, sref[1],
prefix, rbytes2, 0, sref[j]);
status |= do_crypt_gensalt(sref[j], rbytes1, count1, stst);
status |= do_check_equal(stst, sref[2],
prefix, rbytes1, count1, sref[j]);
status |= do_crypt_gensalt(sref[j], rbytes2, count1, stst);
status |= do_check_equal(stst, sref[3],
prefix, rbytes2, count1, sref[j]);
status |= do_crypt_gensalt(sref[j], rbytes1, count2, stst);
status |= do_check_equal(stst, sref[4],
prefix, rbytes1, count2, sref[j]);
status |= do_crypt_gensalt(sref[j], rbytes2, count2, stst);
status |= do_check_equal(stst, sref[5],
prefix, rbytes2, count2, sref[j]);
}
}
}
return status;
}
|