]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright 2017-2018 The OpenSSL Project Authors. All Rights Reserved. | |
3 | * | |
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use | |
5 | * this file except in compliance with the License. You can obtain a copy | |
6 | * in the file LICENSE in the source distribution or at | |
7 | * https://www.openssl.org/source/license.html | |
8 | */ | |
9 | ||
10 | #include "../testutil.h" | |
11 | #include "output.h" | |
12 | #include "tu_local.h" | |
13 | ||
14 | #include <string.h> | |
15 | #include <ctype.h> | |
16 | #include "internal/nelem.h" | |
17 | ||
18 | /* The size of memory buffers to display on failure */ | |
19 | #define MEM_BUFFER_SIZE (2000) | |
20 | #define MAX_STRING_WIDTH (80) | |
21 | #define BN_OUTPUT_SIZE (8) | |
22 | ||
23 | /* Output a diff header */ | |
24 | static void test_diff_header(const char *left, const char *right) | |
25 | { | |
26 | test_printf_stderr("--- %s\n", left); | |
27 | test_printf_stderr("+++ %s\n", right); | |
28 | } | |
29 | ||
30 | /* Formatted string output routines */ | |
31 | static void test_string_null_empty(const char *m, char c) | |
32 | { | |
33 | if (m == NULL) | |
34 | test_printf_stderr("% 4s %c NULL\n", "", c); | |
35 | else | |
36 | test_printf_stderr("% 4u:%c ''\n", 0u, c); | |
37 | } | |
38 | ||
39 | static void test_fail_string_common(const char *prefix, const char *file, | |
40 | int line, const char *type, | |
41 | const char *left, const char *right, | |
42 | const char *op, const char *m1, size_t l1, | |
43 | const char *m2, size_t l2) | |
44 | { | |
45 | const size_t width = (MAX_STRING_WIDTH - subtest_level() - 12) / 16 * 16; | |
46 | char b1[MAX_STRING_WIDTH + 1], b2[MAX_STRING_WIDTH + 1]; | |
47 | char bdiff[MAX_STRING_WIDTH + 1]; | |
48 | size_t n1, n2, i; | |
49 | unsigned int cnt = 0, diff; | |
50 | ||
51 | test_fail_message_prefix(prefix, file, line, type, left, right, op); | |
52 | if (m1 == NULL) | |
53 | l1 = 0; | |
54 | if (m2 == NULL) | |
55 | l2 = 0; | |
56 | if (l1 == 0 && l2 == 0) { | |
57 | if ((m1 == NULL) == (m2 == NULL)) { | |
58 | test_string_null_empty(m1, ' '); | |
59 | } else { | |
60 | test_diff_header(left, right); | |
61 | test_string_null_empty(m1, '-'); | |
62 | test_string_null_empty(m2, '+'); | |
63 | } | |
64 | goto fin; | |
65 | } | |
66 | ||
67 | if (l1 != l2 || strcmp(m1, m2) != 0) | |
68 | test_diff_header(left, right); | |
69 | ||
70 | while (l1 > 0 || l2 > 0) { | |
71 | n1 = n2 = 0; | |
72 | if (l1 > 0) { | |
73 | b1[n1 = l1 > width ? width : l1] = 0; | |
74 | for (i = 0; i < n1; i++) | |
75 | b1[i] = isprint((unsigned char)m1[i]) ? m1[i] : '.'; | |
76 | } | |
77 | if (l2 > 0) { | |
78 | b2[n2 = l2 > width ? width : l2] = 0; | |
79 | for (i = 0; i < n2; i++) | |
80 | b2[i] = isprint((unsigned char)m2[i]) ? m2[i] : '.'; | |
81 | } | |
82 | diff = 0; | |
83 | i = 0; | |
84 | if (n1 > 0 && n2 > 0) { | |
85 | const size_t j = n1 < n2 ? n1 : n2; | |
86 | ||
87 | for (; i < j; i++) | |
88 | if (m1[i] == m2[i]) { | |
89 | bdiff[i] = ' '; | |
90 | } else { | |
91 | bdiff[i] = '^'; | |
92 | diff = 1; | |
93 | } | |
94 | bdiff[i] = '\0'; | |
95 | } | |
96 | if (n1 == n2 && !diff) { | |
97 | test_printf_stderr("% 4u: '%s'\n", cnt, n2 > n1 ? b2 : b1); | |
98 | } else { | |
99 | if (cnt == 0 && (m1 == NULL || *m1 == '\0')) | |
100 | test_string_null_empty(m1, '-'); | |
101 | else if (n1 > 0) | |
102 | test_printf_stderr("% 4u:- '%s'\n", cnt, b1); | |
103 | if (cnt == 0 && (m2 == NULL || *m2 == '\0')) | |
104 | test_string_null_empty(m2, '+'); | |
105 | else if (n2 > 0) | |
106 | test_printf_stderr("% 4u:+ '%s'\n", cnt, b2); | |
107 | if (diff && i > 0) | |
108 | test_printf_stderr("% 4s %s\n", "", bdiff); | |
109 | } | |
110 | m1 += n1; | |
111 | m2 += n2; | |
112 | l1 -= n1; | |
113 | l2 -= n2; | |
114 | cnt += width; | |
115 | } | |
116 | fin: | |
117 | test_flush_stderr(); | |
118 | } | |
119 | ||
120 | /* | |
121 | * Wrapper routines so that the underlying code can be shared. | |
122 | * The first is the call from inside the test utilities when a conditional | |
123 | * fails. The second is the user's call to dump a string. | |
124 | */ | |
125 | void test_fail_string_message(const char *prefix, const char *file, | |
126 | int line, const char *type, | |
127 | const char *left, const char *right, | |
128 | const char *op, const char *m1, size_t l1, | |
129 | const char *m2, size_t l2) | |
130 | { | |
131 | test_fail_string_common(prefix, file, line, type, left, right, op, | |
132 | m1, l1, m2, l2); | |
133 | test_printf_stderr("\n"); | |
134 | } | |
135 | ||
136 | void test_output_string(const char *name, const char *m, size_t l) | |
137 | { | |
138 | test_fail_string_common("string", NULL, 0, NULL, NULL, NULL, name, | |
139 | m, l, m, l); | |
140 | } | |
141 | ||
142 | /* BIGNUM formatted output routines */ | |
143 | ||
144 | /* | |
145 | * A basic memory byte to hex digit converter with allowance for spacing | |
146 | * every so often. | |
147 | */ | |
148 | static void hex_convert_memory(const unsigned char *m, size_t n, char *b, | |
149 | size_t width) | |
150 | { | |
151 | size_t i; | |
152 | ||
153 | for (i = 0; i < n; i++) { | |
154 | const unsigned char c = *m++; | |
155 | ||
156 | *b++ = "0123456789abcdef"[c >> 4]; | |
157 | *b++ = "0123456789abcdef"[c & 15]; | |
158 | if (i % width == width - 1 && i != n - 1) | |
159 | *b++ = ' '; | |
160 | } | |
161 | *b = '\0'; | |
162 | } | |
163 | ||
164 | /* | |
165 | * Constants to define the number of bytes to display per line and the number | |
166 | * of characters these take. | |
167 | */ | |
168 | static const int bn_bytes = (MAX_STRING_WIDTH - 9) / (BN_OUTPUT_SIZE * 2 + 1) | |
169 | * BN_OUTPUT_SIZE; | |
170 | static const int bn_chars = (MAX_STRING_WIDTH - 9) / (BN_OUTPUT_SIZE * 2 + 1) | |
171 | * (BN_OUTPUT_SIZE * 2 + 1) - 1; | |
172 | ||
173 | /* | |
174 | * Output the header line for the bignum | |
175 | */ | |
176 | static void test_bignum_header_line(void) | |
177 | { | |
178 | test_printf_stderr(" %*s\n", bn_chars + 6, "bit position"); | |
179 | } | |
180 | ||
181 | static const char *test_bignum_zero_null(const BIGNUM *bn) | |
182 | { | |
183 | if (bn != NULL) | |
184 | return BN_is_negative(bn) ? "-0" : "0"; | |
185 | return "NULL"; | |
186 | } | |
187 | ||
188 | /* | |
189 | * Print a bignum zero taking care to include the correct sign. | |
190 | * This routine correctly deals with a NULL bignum pointer as input. | |
191 | */ | |
192 | static void test_bignum_zero_print(const BIGNUM *bn, char sep) | |
193 | { | |
194 | const char *v = test_bignum_zero_null(bn); | |
195 | const char *suf = bn != NULL ? ": 0" : ""; | |
196 | ||
197 | test_printf_stderr("%c%*s%s\n", sep, bn_chars, v, suf); | |
198 | } | |
199 | ||
200 | /* | |
201 | * Convert a section of memory from inside a bignum into a displayable | |
202 | * string with appropriate visual aid spaces inserted. | |
203 | */ | |
204 | static int convert_bn_memory(const unsigned char *in, size_t bytes, | |
205 | char *out, int *lz, const BIGNUM *bn) | |
206 | { | |
207 | int n = bytes * 2, i; | |
208 | char *p = out, *q = NULL; | |
209 | ||
210 | if (bn != NULL && !BN_is_zero(bn)) { | |
211 | hex_convert_memory(in, bytes, out, BN_OUTPUT_SIZE); | |
212 | if (*lz) { | |
213 | for (; *p == '0' || *p == ' '; p++) | |
214 | if (*p == '0') { | |
215 | q = p; | |
216 | *p = ' '; | |
217 | n--; | |
218 | } | |
219 | if (*p == '\0') { | |
220 | /* | |
221 | * in[bytes] is defined because we're converting a non-zero | |
222 | * number and we've not seen a non-zero yet. | |
223 | */ | |
224 | if ((in[bytes] & 0xf0) != 0 && BN_is_negative(bn)) { | |
225 | *lz = 0; | |
226 | *q = '-'; | |
227 | n++; | |
228 | } | |
229 | } else { | |
230 | *lz = 0; | |
231 | if (BN_is_negative(bn)) { | |
232 | /* | |
233 | * This is valid because we always convert more digits than | |
234 | * the number holds. | |
235 | */ | |
236 | *q = '-'; | |
237 | n++; | |
238 | } | |
239 | } | |
240 | } | |
241 | return n; | |
242 | } | |
243 | ||
244 | for (i = 0; i < n; i++) { | |
245 | *p++ = ' '; | |
246 | if (i % (2 * BN_OUTPUT_SIZE) == 2 * BN_OUTPUT_SIZE - 1 && i != n - 1) | |
247 | *p++ = ' '; | |
248 | } | |
249 | *p = '\0'; | |
250 | if (bn == NULL) | |
251 | q = "NULL"; | |
252 | else | |
253 | q = BN_is_negative(bn) ? "-0" : "0"; | |
254 | strcpy(p - strlen(q), q); | |
255 | return 0; | |
256 | } | |
257 | ||
258 | /* | |
259 | * Common code to display either one or two bignums, including the diff | |
260 | * pointers for changes (only when there are two). | |
261 | */ | |
262 | static void test_fail_bignum_common(const char *prefix, const char *file, | |
263 | int line, const char *type, | |
264 | const char *left, const char *right, | |
265 | const char *op, | |
266 | const BIGNUM *bn1, const BIGNUM *bn2) | |
267 | { | |
268 | const size_t bytes = bn_bytes; | |
269 | char b1[MAX_STRING_WIDTH + 1], b2[MAX_STRING_WIDTH + 1]; | |
270 | char *p, bdiff[MAX_STRING_WIDTH + 1]; | |
271 | size_t l1, l2, n1, n2, i, len; | |
272 | unsigned int cnt, diff, real_diff; | |
273 | unsigned char *m1 = NULL, *m2 = NULL; | |
274 | int lz1 = 1, lz2 = 1; | |
275 | unsigned char buffer[MEM_BUFFER_SIZE * 2], *bufp = buffer; | |
276 | ||
277 | test_fail_message_prefix(prefix, file, line, type, left, right, op); | |
278 | l1 = bn1 == NULL ? 0 : (BN_num_bytes(bn1) + (BN_is_negative(bn1) ? 1 : 0)); | |
279 | l2 = bn2 == NULL ? 0 : (BN_num_bytes(bn2) + (BN_is_negative(bn2) ? 1 : 0)); | |
280 | if (l1 == 0 && l2 == 0) { | |
281 | if ((bn1 == NULL) == (bn2 == NULL)) { | |
282 | test_bignum_header_line(); | |
283 | test_bignum_zero_print(bn1, ' '); | |
284 | } else { | |
285 | test_diff_header(left, right); | |
286 | test_bignum_header_line(); | |
287 | test_bignum_zero_print(bn1, '-'); | |
288 | test_bignum_zero_print(bn2, '+'); | |
289 | } | |
290 | goto fin; | |
291 | } | |
292 | ||
293 | if (l1 != l2 || bn1 == NULL || bn2 == NULL || BN_cmp(bn1, bn2) != 0) | |
294 | test_diff_header(left, right); | |
295 | test_bignum_header_line(); | |
296 | ||
297 | len = ((l1 > l2 ? l1 : l2) + bytes - 1) / bytes * bytes; | |
298 | ||
299 | if (len > MEM_BUFFER_SIZE && (bufp = OPENSSL_malloc(len * 2)) == NULL) { | |
300 | bufp = buffer; | |
301 | len = MEM_BUFFER_SIZE; | |
302 | test_printf_stderr("WARNING: these BIGNUMs have been truncated\n"); | |
303 | } | |
304 | ||
305 | if (bn1 != NULL) { | |
306 | m1 = bufp; | |
307 | BN_bn2binpad(bn1, m1, len); | |
308 | } | |
309 | if (bn2 != NULL) { | |
310 | m2 = bufp + len; | |
311 | BN_bn2binpad(bn2, m2, len); | |
312 | } | |
313 | ||
314 | while (len > 0) { | |
315 | cnt = 8 * (len - bytes); | |
316 | n1 = convert_bn_memory(m1, bytes, b1, &lz1, bn1); | |
317 | n2 = convert_bn_memory(m2, bytes, b2, &lz2, bn2); | |
318 | ||
319 | diff = real_diff = 0; | |
320 | i = 0; | |
321 | p = bdiff; | |
322 | for (i=0; b1[i] != '\0'; i++) | |
323 | if (b1[i] == b2[i] || b1[i] == ' ' || b2[i] == ' ') { | |
324 | *p++ = ' '; | |
325 | diff |= b1[i] != b2[i]; | |
326 | } else { | |
327 | *p++ = '^'; | |
328 | real_diff = diff = 1; | |
329 | } | |
330 | *p++ = '\0'; | |
331 | if (!diff) { | |
332 | test_printf_stderr(" %s:% 5d\n", n2 > n1 ? b2 : b1, cnt); | |
333 | } else { | |
334 | if (cnt == 0 && bn1 == NULL) | |
335 | test_printf_stderr("-%s\n", b1); | |
336 | else if (cnt == 0 || n1 > 0) | |
337 | test_printf_stderr("-%s:% 5d\n", b1, cnt); | |
338 | if (cnt == 0 && bn2 == NULL) | |
339 | test_printf_stderr("+%s\n", b2); | |
340 | else if (cnt == 0 || n2 > 0) | |
341 | test_printf_stderr("+%s:% 5d\n", b2, cnt); | |
342 | if (real_diff && (cnt == 0 || (n1 > 0 && n2 > 0)) | |
343 | && bn1 != NULL && bn2 != NULL) | |
344 | test_printf_stderr(" %s\n", bdiff); | |
345 | } | |
346 | if (m1 != NULL) | |
347 | m1 += bytes; | |
348 | if (m2 != NULL) | |
349 | m2 += bytes; | |
350 | len -= bytes; | |
351 | } | |
352 | fin: | |
353 | test_flush_stderr(); | |
354 | if (bufp != buffer) | |
355 | OPENSSL_free(bufp); | |
356 | } | |
357 | ||
358 | /* | |
359 | * Wrapper routines so that the underlying code can be shared. | |
360 | * The first two are calls from inside the test utilities when a conditional | |
361 | * fails. The third is the user's call to dump a bignum. | |
362 | */ | |
363 | void test_fail_bignum_message(const char *prefix, const char *file, | |
364 | int line, const char *type, | |
365 | const char *left, const char *right, | |
366 | const char *op, | |
367 | const BIGNUM *bn1, const BIGNUM *bn2) | |
368 | { | |
369 | test_fail_bignum_common(prefix, file, line, type, left, right, op, bn1, bn2); | |
370 | test_printf_stderr("\n"); | |
371 | } | |
372 | ||
373 | void test_fail_bignum_mono_message(const char *prefix, const char *file, | |
374 | int line, const char *type, | |
375 | const char *left, const char *right, | |
376 | const char *op, const BIGNUM *bn) | |
377 | { | |
378 | test_fail_bignum_common(prefix, file, line, type, left, right, op, bn, bn); | |
379 | test_printf_stderr("\n"); | |
380 | } | |
381 | ||
382 | void test_output_bignum(const char *name, const BIGNUM *bn) | |
383 | { | |
384 | if (bn == NULL || BN_is_zero(bn)) { | |
385 | test_printf_stderr("bignum: '%s' = %s\n", name, | |
386 | test_bignum_zero_null(bn)); | |
387 | } else if (BN_num_bytes(bn) <= BN_OUTPUT_SIZE) { | |
388 | unsigned char buf[BN_OUTPUT_SIZE]; | |
389 | char out[2 * sizeof(buf) + 1]; | |
390 | char *p = out; | |
391 | int n = BN_bn2bin(bn, buf); | |
392 | ||
393 | hex_convert_memory(buf, n, p, BN_OUTPUT_SIZE); | |
394 | while (*p == '0' && *++p != '\0') | |
395 | ; | |
396 | test_printf_stderr("bignum: '%s' = %s0x%s\n", name, | |
397 | BN_is_negative(bn) ? "-" : "", p); | |
398 | } else { | |
399 | test_fail_bignum_common("bignum", NULL, 0, NULL, NULL, NULL, name, | |
400 | bn, bn); | |
401 | } | |
402 | } | |
403 | ||
404 | /* Memory output routines */ | |
405 | ||
406 | /* | |
407 | * Handle zero length blocks of memory or NULL pointers to memory | |
408 | */ | |
409 | static void test_memory_null_empty(const unsigned char *m, char c) | |
410 | { | |
411 | if (m == NULL) | |
412 | test_printf_stderr("% 4s %c%s\n", "", c, "NULL"); | |
413 | else | |
414 | test_printf_stderr("%04x %c%s\n", 0u, c, "empty"); | |
415 | } | |
416 | ||
417 | /* | |
418 | * Common code to display one or two blocks of memory. | |
419 | */ | |
420 | static void test_fail_memory_common(const char *prefix, const char *file, | |
421 | int line, const char *type, | |
422 | const char *left, const char *right, | |
423 | const char *op, | |
424 | const unsigned char *m1, size_t l1, | |
425 | const unsigned char *m2, size_t l2) | |
426 | { | |
427 | const size_t bytes = (MAX_STRING_WIDTH - 9) / 17 * 8; | |
428 | char b1[MAX_STRING_WIDTH + 1], b2[MAX_STRING_WIDTH + 1]; | |
429 | char *p, bdiff[MAX_STRING_WIDTH + 1]; | |
430 | size_t n1, n2, i; | |
431 | unsigned int cnt = 0, diff; | |
432 | ||
433 | test_fail_message_prefix(prefix, file, line, type, left, right, op); | |
434 | if (m1 == NULL) | |
435 | l1 = 0; | |
436 | if (m2 == NULL) | |
437 | l2 = 0; | |
438 | if (l1 == 0 && l2 == 0) { | |
439 | if ((m1 == NULL) == (m2 == NULL)) { | |
440 | test_memory_null_empty(m1, ' '); | |
441 | } else { | |
442 | test_diff_header(left, right); | |
443 | test_memory_null_empty(m1, '-'); | |
444 | test_memory_null_empty(m2, '+'); | |
445 | } | |
446 | goto fin; | |
447 | } | |
448 | ||
449 | if (l1 != l2 || (m1 != m2 && memcmp(m1, m2, l1) != 0)) | |
450 | test_diff_header(left, right); | |
451 | ||
452 | while (l1 > 0 || l2 > 0) { | |
453 | n1 = n2 = 0; | |
454 | if (l1 > 0) { | |
455 | n1 = l1 > bytes ? bytes : l1; | |
456 | hex_convert_memory(m1, n1, b1, 8); | |
457 | } | |
458 | if (l2 > 0) { | |
459 | n2 = l2 > bytes ? bytes : l2; | |
460 | hex_convert_memory(m2, n2, b2, 8); | |
461 | } | |
462 | ||
463 | diff = 0; | |
464 | i = 0; | |
465 | p = bdiff; | |
466 | if (n1 > 0 && n2 > 0) { | |
467 | const size_t j = n1 < n2 ? n1 : n2; | |
468 | ||
469 | for (; i < j; i++) { | |
470 | if (m1[i] == m2[i]) { | |
471 | *p++ = ' '; | |
472 | *p++ = ' '; | |
473 | } else { | |
474 | *p++ = '^'; | |
475 | *p++ = '^'; | |
476 | diff = 1; | |
477 | } | |
478 | if (i % 8 == 7 && i != j - 1) | |
479 | *p++ = ' '; | |
480 | } | |
481 | *p++ = '\0'; | |
482 | } | |
483 | ||
484 | if (n1 == n2 && !diff) { | |
485 | test_printf_stderr("%04x: %s\n", cnt, b1); | |
486 | } else { | |
487 | if (cnt == 0 && (m1 == NULL || l1 == 0)) | |
488 | test_memory_null_empty(m1, '-'); | |
489 | else if (n1 > 0) | |
490 | test_printf_stderr("%04x:-%s\n", cnt, b1); | |
491 | if (cnt == 0 && (m2 == NULL || l2 == 0)) | |
492 | test_memory_null_empty(m2, '+'); | |
493 | else if (n2 > 0) | |
494 | test_printf_stderr("%04x:+%s\n", cnt, b2); | |
495 | if (diff && i > 0) | |
496 | test_printf_stderr("% 4s %s\n", "", bdiff); | |
497 | } | |
498 | m1 += n1; | |
499 | m2 += n2; | |
500 | l1 -= n1; | |
501 | l2 -= n2; | |
502 | cnt += bytes; | |
503 | } | |
504 | fin: | |
505 | test_flush_stderr(); | |
506 | } | |
507 | ||
508 | /* | |
509 | * Wrapper routines so that the underlying code can be shared. | |
510 | * The first is the call from inside the test utilities when a conditional | |
511 | * fails. The second is the user's call to dump memory. | |
512 | */ | |
513 | void test_fail_memory_message(const char *prefix, const char *file, | |
514 | int line, const char *type, | |
515 | const char *left, const char *right, | |
516 | const char *op, | |
517 | const unsigned char *m1, size_t l1, | |
518 | const unsigned char *m2, size_t l2) | |
519 | { | |
520 | test_fail_memory_common(prefix, file, line, type, left, right, op, | |
521 | m1, l1, m2, l2); | |
522 | test_printf_stderr("\n"); | |
523 | } | |
524 | ||
525 | void test_output_memory(const char *name, const unsigned char *m, size_t l) | |
526 | { | |
527 | test_fail_memory_common("memory", NULL, 0, NULL, NULL, NULL, name, | |
528 | m, l, m, l); | |
529 | } |