1 /* 2 * Copyright 2017-2018 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 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 const char *r; 210 211 if (bn != NULL && !BN_is_zero(bn)) { 212 hex_convert_memory(in, bytes, out, BN_OUTPUT_SIZE); 213 if (*lz) { 214 for (; *p == '0' || *p == ' '; p++) 215 if (*p == '0') { 216 q = p; 217 *p = ' '; 218 n--; 219 } 220 if (*p == '\0') { 221 /* 222 * in[bytes] is defined because we're converting a non-zero 223 * number and we've not seen a non-zero yet. 224 */ 225 if ((in[bytes] & 0xf0) != 0 && BN_is_negative(bn)) { 226 *lz = 0; 227 *q = '-'; 228 n++; 229 } 230 } else { 231 *lz = 0; 232 if (BN_is_negative(bn)) { 233 /* 234 * This is valid because we always convert more digits than 235 * the number holds. 236 */ 237 *q = '-'; 238 n++; 239 } 240 } 241 } 242 return n; 243 } 244 245 for (i = 0; i < n; i++) { 246 *p++ = ' '; 247 if (i % (2 * BN_OUTPUT_SIZE) == 2 * BN_OUTPUT_SIZE - 1 && i != n - 1) 248 *p++ = ' '; 249 } 250 *p = '\0'; 251 if (bn == NULL) 252 r = "NULL"; 253 else 254 r = BN_is_negative(bn) ? "-0" : "0"; 255 strcpy(p - strlen(r), r); 256 return 0; 257 } 258 259 /* 260 * Common code to display either one or two bignums, including the diff 261 * pointers for changes (only when there are two). 262 */ 263 static void test_fail_bignum_common(const char *prefix, const char *file, 264 int line, const char *type, 265 const char *left, const char *right, 266 const char *op, 267 const BIGNUM *bn1, const BIGNUM *bn2) 268 { 269 const size_t bytes = bn_bytes; 270 char b1[MAX_STRING_WIDTH + 1], b2[MAX_STRING_WIDTH + 1]; 271 char *p, bdiff[MAX_STRING_WIDTH + 1]; 272 size_t l1, l2, n1, n2, i, len; 273 unsigned int cnt, diff, real_diff; 274 unsigned char *m1 = NULL, *m2 = NULL; 275 int lz1 = 1, lz2 = 1; 276 unsigned char buffer[MEM_BUFFER_SIZE * 2], *bufp = buffer; 277 278 test_fail_message_prefix(prefix, file, line, type, left, right, op); 279 l1 = bn1 == NULL ? 0 : (BN_num_bytes(bn1) + (BN_is_negative(bn1) ? 1 : 0)); 280 l2 = bn2 == NULL ? 0 : (BN_num_bytes(bn2) + (BN_is_negative(bn2) ? 1 : 0)); 281 if (l1 == 0 && l2 == 0) { 282 if ((bn1 == NULL) == (bn2 == NULL)) { 283 test_bignum_header_line(); 284 test_bignum_zero_print(bn1, ' '); 285 } else { 286 test_diff_header(left, right); 287 test_bignum_header_line(); 288 test_bignum_zero_print(bn1, '-'); 289 test_bignum_zero_print(bn2, '+'); 290 } 291 goto fin; 292 } 293 294 if (l1 != l2 || bn1 == NULL || bn2 == NULL || BN_cmp(bn1, bn2) != 0) 295 test_diff_header(left, right); 296 test_bignum_header_line(); 297 298 len = ((l1 > l2 ? l1 : l2) + bytes - 1) / bytes * bytes; 299 300 if (len > MEM_BUFFER_SIZE && (bufp = OPENSSL_malloc(len * 2)) == NULL) { 301 bufp = buffer; 302 len = MEM_BUFFER_SIZE; 303 test_printf_stderr("WARNING: these BIGNUMs have been truncated\n"); 304 } 305 306 if (bn1 != NULL) { 307 m1 = bufp; 308 BN_bn2binpad(bn1, m1, len); 309 } 310 if (bn2 != NULL) { 311 m2 = bufp + len; 312 BN_bn2binpad(bn2, m2, len); 313 } 314 315 while (len > 0) { 316 cnt = 8 * (len - bytes); 317 n1 = convert_bn_memory(m1, bytes, b1, &lz1, bn1); 318 n2 = convert_bn_memory(m2, bytes, b2, &lz2, bn2); 319 320 diff = real_diff = 0; 321 i = 0; 322 p = bdiff; 323 for (i=0; b1[i] != '\0'; i++) 324 if (b1[i] == b2[i] || b1[i] == ' ' || b2[i] == ' ') { 325 *p++ = ' '; 326 diff |= b1[i] != b2[i]; 327 } else { 328 *p++ = '^'; 329 real_diff = diff = 1; 330 } 331 *p++ = '\0'; 332 if (!diff) { 333 test_printf_stderr(" %s:% 5d\n", n2 > n1 ? b2 : b1, cnt); 334 } else { 335 if (cnt == 0 && bn1 == NULL) 336 test_printf_stderr("-%s\n", b1); 337 else if (cnt == 0 || n1 > 0) 338 test_printf_stderr("-%s:% 5d\n", b1, cnt); 339 if (cnt == 0 && bn2 == NULL) 340 test_printf_stderr("+%s\n", b2); 341 else if (cnt == 0 || n2 > 0) 342 test_printf_stderr("+%s:% 5d\n", b2, cnt); 343 if (real_diff && (cnt == 0 || (n1 > 0 && n2 > 0)) 344 && bn1 != NULL && bn2 != NULL) 345 test_printf_stderr(" %s\n", bdiff); 346 } 347 if (m1 != NULL) 348 m1 += bytes; 349 if (m2 != NULL) 350 m2 += bytes; 351 len -= bytes; 352 } 353 fin: 354 test_flush_stderr(); 355 if (bufp != buffer) 356 OPENSSL_free(bufp); 357 } 358 359 /* 360 * Wrapper routines so that the underlying code can be shared. 361 * The first two are calls from inside the test utilities when a conditional 362 * fails. The third is the user's call to dump a bignum. 363 */ 364 void test_fail_bignum_message(const char *prefix, const char *file, 365 int line, const char *type, 366 const char *left, const char *right, 367 const char *op, 368 const BIGNUM *bn1, const BIGNUM *bn2) 369 { 370 test_fail_bignum_common(prefix, file, line, type, left, right, op, bn1, bn2); 371 test_printf_stderr("\n"); 372 } 373 374 void test_fail_bignum_mono_message(const char *prefix, const char *file, 375 int line, const char *type, 376 const char *left, const char *right, 377 const char *op, const BIGNUM *bn) 378 { 379 test_fail_bignum_common(prefix, file, line, type, left, right, op, bn, bn); 380 test_printf_stderr("\n"); 381 } 382 383 void test_output_bignum(const char *name, const BIGNUM *bn) 384 { 385 if (bn == NULL || BN_is_zero(bn)) { 386 test_printf_stderr("bignum: '%s' = %s\n", name, 387 test_bignum_zero_null(bn)); 388 } else if (BN_num_bytes(bn) <= BN_OUTPUT_SIZE) { 389 unsigned char buf[BN_OUTPUT_SIZE]; 390 char out[2 * sizeof(buf) + 1]; 391 char *p = out; 392 int n = BN_bn2bin(bn, buf); 393 394 hex_convert_memory(buf, n, p, BN_OUTPUT_SIZE); 395 while (*p == '0' && *++p != '\0') 396 ; 397 test_printf_stderr("bignum: '%s' = %s0x%s\n", name, 398 BN_is_negative(bn) ? "-" : "", p); 399 } else { 400 test_fail_bignum_common("bignum", NULL, 0, NULL, NULL, NULL, name, 401 bn, bn); 402 } 403 } 404 405 /* Memory output routines */ 406 407 /* 408 * Handle zero length blocks of memory or NULL pointers to memory 409 */ 410 static void test_memory_null_empty(const unsigned char *m, char c) 411 { 412 if (m == NULL) 413 test_printf_stderr("%4s %c%s\n", "", c, "NULL"); 414 else 415 test_printf_stderr("%04x %c%s\n", 0u, c, "empty"); 416 } 417 418 /* 419 * Common code to display one or two blocks of memory. 420 */ 421 static void test_fail_memory_common(const char *prefix, const char *file, 422 int line, const char *type, 423 const char *left, const char *right, 424 const char *op, 425 const unsigned char *m1, size_t l1, 426 const unsigned char *m2, size_t l2) 427 { 428 const size_t bytes = (MAX_STRING_WIDTH - 9) / 17 * 8; 429 char b1[MAX_STRING_WIDTH + 1], b2[MAX_STRING_WIDTH + 1]; 430 char *p, bdiff[MAX_STRING_WIDTH + 1]; 431 size_t n1, n2, i; 432 unsigned int cnt = 0, diff; 433 434 test_fail_message_prefix(prefix, file, line, type, left, right, op); 435 if (m1 == NULL) 436 l1 = 0; 437 if (m2 == NULL) 438 l2 = 0; 439 if (l1 == 0 && l2 == 0) { 440 if ((m1 == NULL) == (m2 == NULL)) { 441 test_memory_null_empty(m1, ' '); 442 } else { 443 test_diff_header(left, right); 444 test_memory_null_empty(m1, '-'); 445 test_memory_null_empty(m2, '+'); 446 } 447 goto fin; 448 } 449 450 if (l1 != l2 || (m1 != m2 && memcmp(m1, m2, l1) != 0)) 451 test_diff_header(left, right); 452 453 while (l1 > 0 || l2 > 0) { 454 n1 = n2 = 0; 455 if (l1 > 0) { 456 n1 = l1 > bytes ? bytes : l1; 457 hex_convert_memory(m1, n1, b1, 8); 458 } 459 if (l2 > 0) { 460 n2 = l2 > bytes ? bytes : l2; 461 hex_convert_memory(m2, n2, b2, 8); 462 } 463 464 diff = 0; 465 i = 0; 466 p = bdiff; 467 if (n1 > 0 && n2 > 0) { 468 const size_t j = n1 < n2 ? n1 : n2; 469 470 for (; i < j; i++) { 471 if (m1[i] == m2[i]) { 472 *p++ = ' '; 473 *p++ = ' '; 474 } else { 475 *p++ = '^'; 476 *p++ = '^'; 477 diff = 1; 478 } 479 if (i % 8 == 7 && i != j - 1) 480 *p++ = ' '; 481 } 482 *p++ = '\0'; 483 } 484 485 if (n1 == n2 && !diff) { 486 test_printf_stderr("%04x: %s\n", cnt, b1); 487 } else { 488 if (cnt == 0 && (m1 == NULL || l1 == 0)) 489 test_memory_null_empty(m1, '-'); 490 else if (n1 > 0) 491 test_printf_stderr("%04x:-%s\n", cnt, b1); 492 if (cnt == 0 && (m2 == NULL || l2 == 0)) 493 test_memory_null_empty(m2, '+'); 494 else if (n2 > 0) 495 test_printf_stderr("%04x:+%s\n", cnt, b2); 496 if (diff && i > 0) 497 test_printf_stderr("%4s %s\n", "", bdiff); 498 } 499 m1 += n1; 500 m2 += n2; 501 l1 -= n1; 502 l2 -= n2; 503 cnt += bytes; 504 } 505 fin: 506 test_flush_stderr(); 507 } 508 509 /* 510 * Wrapper routines so that the underlying code can be shared. 511 * The first is the call from inside the test utilities when a conditional 512 * fails. The second is the user's call to dump memory. 513 */ 514 void test_fail_memory_message(const char *prefix, const char *file, 515 int line, const char *type, 516 const char *left, const char *right, 517 const char *op, 518 const unsigned char *m1, size_t l1, 519 const unsigned char *m2, size_t l2) 520 { 521 test_fail_memory_common(prefix, file, line, type, left, right, op, 522 m1, l1, m2, l2); 523 test_printf_stderr("\n"); 524 } 525 526 void test_output_memory(const char *name, const unsigned char *m, size_t l) 527 { 528 test_fail_memory_common("memory", NULL, 0, NULL, NULL, NULL, name, 529 m, l, m, l); 530 } 531