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