| 1 | /* -*-c-*- |
| 2 | * |
| 3 | * Common code for testing encryption modes |
| 4 | * |
| 5 | * (c) 2018 Straylight/Edgeware |
| 6 | */ |
| 7 | |
| 8 | /*----- Licensing notice --------------------------------------------------* |
| 9 | * |
| 10 | * This file is part of Catacomb. |
| 11 | * |
| 12 | * Catacomb is free software: you can redistribute it and/or modify it |
| 13 | * under the terms of the GNU Library General Public License as published |
| 14 | * by the Free Software Foundation; either version 2 of the License, or |
| 15 | * (at your option) any later version. |
| 16 | * |
| 17 | * Catacomb is distributed in the hope that it will be useful, but |
| 18 | * WITHOUT ANY WARRANTY; without even the implied warranty of |
| 19 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| 20 | * Library General Public License for more details. |
| 21 | * |
| 22 | * You should have received a copy of the GNU Library General Public |
| 23 | * License along with Catacomb. If not, write to the Free Software |
| 24 | * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, |
| 25 | * USA. |
| 26 | */ |
| 27 | |
| 28 | /*----- Header files ------------------------------------------------------*/ |
| 29 | |
| 30 | #include <errno.h> |
| 31 | #include <stdio.h> |
| 32 | #include <stdlib.h> |
| 33 | #include <string.h> |
| 34 | |
| 35 | #include <unistd.h> |
| 36 | |
| 37 | #include <mLib/alloc.h> |
| 38 | #include <mLib/bits.h> |
| 39 | #include <mLib/dstr.h> |
| 40 | #include <mLib/quis.h> |
| 41 | #include <mLib/report.h> |
| 42 | |
| 43 | #include "modes-test.h" |
| 44 | |
| 45 | /*----- The reference data ------------------------------------------------*/ |
| 46 | |
| 47 | #ifdef SMALL_TEST |
| 48 | static const octet text[] = "A small piece of text for testing encryption."; |
| 49 | #else |
| 50 | #define STORY "\ |
| 51 | Once upon a time there were a beautiful princess, a slightly nutty wizard,\n\ |
| 52 | and a watermelon. Now, the watermelon had decided that it probably wasn't\n\ |
| 53 | going to get very far with the princess unless it did something pretty\n\ |
| 54 | drastic. So it asked the wizard to turn it into a handsome prince.\n\ |
| 55 | \n\ |
| 56 | At least, this is the way that the wizard viewed the situation. He might\n\ |
| 57 | have just hallucinated it all; those mushrooms had looked ever so nice.\n\ |
| 58 | \n\ |
| 59 | Back to the point. The watermelon had expressed its desire not to be a\n\ |
| 60 | watermelon any more. And the wizard was probably tripping something quite\n\ |
| 61 | powerful. He hunted around a bit for his staff, and mumbled something\n\ |
| 62 | that film directors would think of as sounding appropriately arcane and\n\ |
| 63 | mystical (but was, in fact, just the ingredients list for an ancient\n\ |
| 64 | remedy for athlete's foot) and *pop*. Cooked watermelon. Yuk.\n\ |
| 65 | \n\ |
| 66 | Later in the year, the princess tripped over the hem of her dress, fell\n\ |
| 67 | down a spiral staircase, and died. The king ordered dressmakers to attach\n\ |
| 68 | safety warnings to long dresses.\n\ |
| 69 | \n\ |
| 70 | And the wizard? Who cares?\n\ |
| 71 | " |
| 72 | static const octet text[] = STORY STORY; |
| 73 | #endif |
| 74 | |
| 75 | #define TEXTSZ (sizeof(text)) |
| 76 | |
| 77 | static const octet key[] = "Penguins rule OK, rhubarb cauliflower", |
| 78 | iv[] = "EdgewareCatacomb, parsley, sage, rosemary and thyme"; |
| 79 | |
| 80 | /*----- Static variables --------------------------------------------------*/ |
| 81 | |
| 82 | /* Encryption buffers, for ciphertext, recovered plaintext, and consistency |
| 83 | * reference. |
| 84 | */ |
| 85 | static octet ct[TEXTSZ], pt[TEXTSZ], ref[TEXTSZ]; |
| 86 | |
| 87 | /* A resizeable buffer for verifying regression data. */ |
| 88 | static octet *t = 0; size_t tsz = 0; |
| 89 | |
| 90 | /*----- Diagnostic utilities ----------------------------------------------*/ |
| 91 | |
| 92 | /* Print the @sz@-byte buffer @p@, beginning at offset @off@ within some |
| 93 | * larger buffer, marking block boundaries every @blksz@ bytes. |
| 94 | */ |
| 95 | static void hexdump(const octet *p, size_t sz, size_t off, size_t blksz) |
| 96 | { |
| 97 | const octet *q = p + sz; |
| 98 | for (sz = 0; p < q; p++, sz++) { |
| 99 | printf("%02x", *p); |
| 100 | if ((off + sz + 1)%blksz == 0) putchar(':'); |
| 101 | } |
| 102 | } |
| 103 | |
| 104 | /* Print the buffer @p@, labelling it as @what@, splitting it into three |
| 105 | * pieces of sizes @sz0@, @sz1@, and @sz2@ respectively. Block boundaries |
| 106 | * every @blksz@ bytes are shown consistency, independent of the split |
| 107 | * positions. |
| 108 | */ |
| 109 | static void dump_split(const char *what, size_t blksz, const octet *p, |
| 110 | size_t sz0, size_t sz1, size_t sz2) |
| 111 | { |
| 112 | printf("\t%-16s = ", what); |
| 113 | hexdump(p, sz0, 0, blksz); |
| 114 | if (sz1) { printf(", "); hexdump(p + sz0, sz1, sz0, blksz); } |
| 115 | if (sz2) { printf(", "); hexdump(p + sz0 + sz1, sz2, sz0 + sz1, blksz); } |
| 116 | fputc('\n', stdout); |
| 117 | } |
| 118 | |
| 119 | /*----- Regression-data utilities -----------------------------------------*/ |
| 120 | |
| 121 | /* Regression modes. We can @CHECK@ existing data, @RECORD@ new data, or |
| 122 | * @IGNORE@ the regression testing entirely. |
| 123 | */ |
| 124 | enum { IGNORE, RECORD, CHECK }; |
| 125 | |
| 126 | /* Read or write regression data from/to @fp@ according to @rmode@. The data |
| 127 | * item is described as @what@ in diagnostic messages, and consists of @sz@ |
| 128 | * bytes beginning at @p@. |
| 129 | * |
| 130 | * If @rmode@ is @IGNORE@, then this function does nothing; if @rmode@ is |
| 131 | * @RECORD@, then it writes @p@ to the output file with some framing; and if |
| 132 | * @rmode@ is @CHECK@ then it checks that the next chunk of data from the |
| 133 | * file matches @p@. |
| 134 | * |
| 135 | * Returns zero if all is well or @-1@ on a mismatch; I/O errors are fatal. |
| 136 | * |
| 137 | * Framing is trivial and consists of a 4-byte big-endian non-inclusive |
| 138 | * length prepended to each buffer. No padding is written to maintain |
| 139 | * alignment. |
| 140 | */ |
| 141 | static int regress_data(int rmode, FILE *fp, const char *what, |
| 142 | const void *p, size_t sz) |
| 143 | { |
| 144 | octet b[4]; |
| 145 | size_t psz; |
| 146 | |
| 147 | switch (rmode) { |
| 148 | case IGNORE: |
| 149 | return (0); |
| 150 | case RECORD: |
| 151 | STORE32(b, sz); |
| 152 | if (!fwrite(b, 4, 1, fp) || !fwrite(p, sz, 1, fp)) |
| 153 | die(1, "failed to write %s: %s", what, strerror(errno)); |
| 154 | return (0); |
| 155 | case CHECK: |
| 156 | if (!fread(b, 4, 1, fp)) |
| 157 | die(1, "failed to read %s length: %s", what, |
| 158 | ferror(fp) ? strerror(errno) : "unexpected eof"); |
| 159 | psz = LOAD32(b); |
| 160 | if (psz != sz) |
| 161 | die(1, "incorrect %s length (%lu /= %lu; sync failure?)", |
| 162 | what, (unsigned long)psz, (unsigned long)sz); |
| 163 | if (tsz < sz) { xfree(t); t = xmalloc(sz); tsz = sz; } |
| 164 | if (!fread(t, sz, 1, fp)) |
| 165 | die(1, "failed to read %s: %s", what, |
| 166 | ferror(fp) ? strerror(errno) : "unexpected eof"); |
| 167 | if (memcmp(p, t, sz) != 0) return (-1); |
| 168 | return (0); |
| 169 | default: |
| 170 | abort(); |
| 171 | } |
| 172 | } |
| 173 | |
| 174 | /* Read or write framing data from/to @fp@ according to @rmode@. The framing |
| 175 | * item is describd as @what@ in diagnostic messages, and consists of @sz@ |
| 176 | * bytes beginning at @p@. |
| 177 | * |
| 178 | * Framing data is used to verify that a recorded regression-data file is |
| 179 | * still appropriate for use. A fatal error is reported on any kind of |
| 180 | * failure. |
| 181 | */ |
| 182 | static void regress_framing(int rmode, FILE *fp, const char *what, |
| 183 | const void *p, size_t sz) |
| 184 | { |
| 185 | if (regress_data(rmode, fp, what, p, sz)) |
| 186 | die(1, "regression framing mismatch for %s (bug, or wrong file)", what); |
| 187 | } |
| 188 | |
| 189 | /* Read or write crypto data from/to @fp@ according to @rmode@. The data |
| 190 | * item is describd as @what@ in diagnostic messages, and consists of the |
| 191 | * bytes beginning at @p@. For the purposes of diagnostics, this buffer has |
| 192 | * been notionally split into three pieces, with sizes @sz0@, @sz1@, and |
| 193 | * @sz2@, respectively. |
| 194 | * |
| 195 | * If al is well, return zero. If the crypto data doesn't match the recorded |
| 196 | * regression data, then report the mismatch, showing the way in which the |
| 197 | * buffer is split, and return -1. I/O errors are fatal. |
| 198 | */ |
| 199 | static int regress_crypto(int rmode, FILE *fp, const char *what, size_t blksz, |
| 200 | const void *p, size_t sz0, size_t sz1, size_t sz2) |
| 201 | { |
| 202 | int rc; |
| 203 | |
| 204 | rc = regress_data(rmode, fp, what, p, sz0 + sz1 + sz2); |
| 205 | if (rc) { |
| 206 | printf("\nRegression mismatch (split = %lu/%lu/%lu)\n", |
| 207 | (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2); |
| 208 | dump_split("plaintext", blksz, text, sz0, sz1, sz2); |
| 209 | dump_split("expected ct", blksz, t, sz0, sz1, sz2); |
| 210 | dump_split("computed ct", blksz, p, sz0, sz1, sz2); |
| 211 | fputc('\n', stdout); |
| 212 | } |
| 213 | return (rc); |
| 214 | } |
| 215 | |
| 216 | /*----- Selecting fragment sizes ------------------------------------------*/ |
| 217 | |
| 218 | /* Return codes from @step@. */ |
| 219 | enum { STEP, LIMIT, RESET }; |
| 220 | |
| 221 | /* Update @*sz_inout@ the next largest suitable fragment size, up to a |
| 222 | * maximum of @max@. |
| 223 | * |
| 224 | * If the new size is still smaller than the maximum, then return @STEP@. If |
| 225 | * the size is maximal, then return @LIMIT@. If the size was previously |
| 226 | * maximal already, then return @RESET@. |
| 227 | * |
| 228 | * The sizes here are selected powers of two, and powers of two plus or minus |
| 229 | * 1, with the objective of testing how internal buffering is affected when |
| 230 | * the cursor is misaligned and realigned with block boundaries. |
| 231 | */ |
| 232 | static int step(size_t *sz_inout, size_t max) |
| 233 | { |
| 234 | size_t i; |
| 235 | |
| 236 | static size_t steps[] = { 1, 7, 8, 9, 15, 16, 17, |
| 237 | 63, 64, 65, 255, 256, 257 }; |
| 238 | |
| 239 | if (*sz_inout == max) return (RESET); |
| 240 | for (i = 0; i < N(steps); i++) |
| 241 | if (steps[i] > *sz_inout) { |
| 242 | if (steps[i] < max) { *sz_inout = steps[i]; return (STEP); } |
| 243 | else break; |
| 244 | } |
| 245 | *sz_inout = max; return (LIMIT); |
| 246 | } |
| 247 | |
| 248 | /*----- Main code ---------------------------------------------------------*/ |
| 249 | |
| 250 | /* --- @test_encmode@ --- * |
| 251 | * |
| 252 | * Arguments: @const char *name@ = name of the encryption scheme; used to |
| 253 | * find the regression-test filename |
| 254 | * @size_t ksz@ = key size to use, or zero for `don't care' |
| 255 | * @size_t blksz@ = block size |
| 256 | * @size_t minsz@ = smallest acceptable buffer size, or 1 |
| 257 | * @unsigned f@ = various additional flags |
| 258 | * @setupfn *setup@ = key-setup function |
| 259 | * @resetfn *reset@ = state-reset function |
| 260 | * @encfn *enc@ = encryption function |
| 261 | * @decfn *dec@ = decryption function |
| 262 | * @int argc@ = number of command-line arguments |
| 263 | * @char *argv@ = pointer to command-line argument vector |
| 264 | * |
| 265 | * Returns: Zero on success, nonzero to report failure. |
| 266 | * |
| 267 | * Use: Tests an encryption mode which doesn't have any more formal |
| 268 | * test vectors. |
| 269 | * |
| 270 | * The @name@ is used firstly in diagnostic output and secondly |
| 271 | * to form the default filename to use for regression-test data, |
| 272 | * as `.../symm/t/modes/NAME.regress'. |
| 273 | * |
| 274 | * The key size @ksz@ is simply passed on back to the @setup@ |
| 275 | * function, unless the caller passes in zero, in which case |
| 276 | * @test_encmode@ chooses a key size for itself. |
| 277 | * |
| 278 | * The block size @blksz@ is used in failure reports, to draw |
| 279 | * attention to the block structure in the various buffers, |
| 280 | * which may assist with diagnosis. It's also used to determine |
| 281 | * when to apply a consistency check: see below regarding the |
| 282 | * @TEMF_REFALIGN@ flag. |
| 283 | * |
| 284 | * The minimum buffer size @minsz@ expresses a limitation on the |
| 285 | * provided @enc@ and @dec@ functions, that they don't work on |
| 286 | * inputs smaller than @minsz@; accordingly, @test_encmode@ will |
| 287 | * not test such small sizes. This should be 1 if the mode has |
| 288 | * no limitation. |
| 289 | * |
| 290 | * The flags @f@ influence testing in various ways explained |
| 291 | * below. |
| 292 | * |
| 293 | * The caller-provided functions are assumed to act on some |
| 294 | * global but hidden state, |
| 295 | * |
| 296 | * * @setup@ is (currently, at least) called only once, with |
| 297 | * the key @k@ and its chosen size @ksz@. |
| 298 | * |
| 299 | * * @reset@ is called at the start of each encryption or |
| 300 | * decryption operation, to program in the initialization |
| 301 | * vector to use. Currently, the same IV is used in all of |
| 302 | * the tests, but this might not always be the case. |
| 303 | * |
| 304 | * * @enc@ is called to encrypt a source buffer @s@ and write |
| 305 | * the ciphertext to a destination @d@; @sz@ is the common |
| 306 | * size of these buffers. @d@ might be null, to discard |
| 307 | * output; @s@ might be null, to process all-zero input. |
| 308 | * |
| 309 | * * @dec@ is called to decrypt a source buffer @s@ and write |
| 310 | * the recovered plaintext to a destination @d@; @sz@ is the |
| 311 | * common size of these buffers. |
| 312 | * |
| 313 | * Finally, @int argc@ and @char *argv@ are the command-line |
| 314 | * arguments provided to @main@; @test_encmode@ parses these and |
| 315 | * alters its behaviour accordingly. |
| 316 | * |
| 317 | * Currently, @test_encmode@'s tests are built around a single, |
| 318 | * fairly large, fixed message. In each test step, the message |
| 319 | * is split into a number of fragments which are encrypted and |
| 320 | * decrypted in turn. |
| 321 | * |
| 322 | * The following tests are performed. |
| 323 | * |
| 324 | * * The fundamental `round-trip' test, which verifies that |
| 325 | * the message can be encrypted and then decrypted |
| 326 | * successfully, if the same fragment boundaries are used in |
| 327 | * both cases. |
| 328 | * |
| 329 | * * A `consistency' test. Some modes, such as CFB, OFB, and |
| 330 | * counter, are `resumable': encryption operations are |
| 331 | * insensitive to the position of fragment boundaries, so a |
| 332 | * single message can be broken into fragments without |
| 333 | * affecting the result. If @TEMF_REFALIGN@ is clear then |
| 334 | * the mode under test is verified to have this property. |
| 335 | * If @TEMF_REFALIGN' is set, a weaker property is verified: |
| 336 | * that encryption is insensitive to the position of |
| 337 | * /block-aligned/ fragment boundaries only. |
| 338 | * |
| 339 | * * A `regression' test, which verifies that the code |
| 340 | * produces the same ciphertext as a previous version. By |
| 341 | * setting command-line arguments appropriately, a test |
| 342 | * program can be told to record ciphertexts in a (binary) |
| 343 | * data file. Usually, instead, the program will read the |
| 344 | * recorded ciphertexts back and verify that it produces the |
| 345 | * same data. For resumable modes, it's only necessary to |
| 346 | * record single ciphertext, since all the other ciphertexts |
| 347 | * must be equal by consistency; otherwise all non-block- |
| 348 | * aligned splits are recorded separately. |
| 349 | */ |
| 350 | |
| 351 | int test_encmode(const char *name, |
| 352 | size_t ksz, size_t blksz, size_t minsz, unsigned f, |
| 353 | setupfn *setup, resetfn *reset, encfn *enc, encfn *dec, |
| 354 | int argc, char *argv[]) |
| 355 | { |
| 356 | int ok = 1, refp = 0, i; |
| 357 | size_t sz0, sz1, sz2; |
| 358 | const char spinner[] = "/-\\|"; |
| 359 | int rmode = CHECK, spin = isatty(STDOUT_FILENO) ? 0 : -1; |
| 360 | int regr; |
| 361 | const char *rname = 0, *p; |
| 362 | FILE *fp; |
| 363 | dstr d = DSTR_INIT; |
| 364 | |
| 365 | ego(argv[0]); |
| 366 | |
| 367 | /* Parse the command-line options. */ |
| 368 | p = 0; i = 1; |
| 369 | for (;;) { |
| 370 | |
| 371 | /* Read the next argument. */ |
| 372 | if (!p || !*p) { |
| 373 | if (i >= argc) break; |
| 374 | p = argv[i++]; |
| 375 | if (strcmp(p, "--") == 0) break; |
| 376 | if (p[0] != '-' || p[1] == 0) { i--; break; } |
| 377 | p++; |
| 378 | } |
| 379 | |
| 380 | /* Interpret an option. */ |
| 381 | switch (*p++) { |
| 382 | case 'h': |
| 383 | printf("%s test driver\n" |
| 384 | "Usage: %s [-i] [-o|-f FILENAME]\n", QUIS, QUIS); |
| 385 | exit(0); |
| 386 | case 'i': |
| 387 | rmode = IGNORE; |
| 388 | break; |
| 389 | case 'o': |
| 390 | if (!*p) { |
| 391 | if (i >= argc) die(1, "option `-o' expects an argument"); |
| 392 | p = argv[i++]; |
| 393 | } |
| 394 | rmode = RECORD; rname = p; p = 0; |
| 395 | break; |
| 396 | case 'f': |
| 397 | if (!*p) { |
| 398 | if (i >= argc) die(1, "option `-f' expects an argument"); |
| 399 | p = argv[i++]; |
| 400 | } |
| 401 | rmode = CHECK; rname = p; p = 0; |
| 402 | break; |
| 403 | default: |
| 404 | die(1, "option `-%c' unknown", p[-1]); |
| 405 | } |
| 406 | } |
| 407 | |
| 408 | /* Check there's nothing else left. */ |
| 409 | if (i < argc) die(1, "trailing junk on command line"); |
| 410 | |
| 411 | /* Open the regression-data file. */ |
| 412 | if (rmode == IGNORE) |
| 413 | fp = 0; |
| 414 | else { |
| 415 | if (!rname) { |
| 416 | DRESET(&d); dstr_putf(&d, SRCDIR"/t/modes/%s.regress", name); |
| 417 | rname = xstrdup(d.buf); |
| 418 | } |
| 419 | fp = fopen(rname, rmode == RECORD ? "wb" : "rb"); |
| 420 | if (!fp) |
| 421 | die(1, "failed to open `%s' for %s: %s", rname, |
| 422 | rmode == RECORD ? "writing" : "reading", strerror(errno)); |
| 423 | } |
| 424 | |
| 425 | /* Write a header describing the file, to trap misuse for the wrong mode, |
| 426 | * and changes in the text. |
| 427 | */ |
| 428 | DRESET(&d); |
| 429 | dstr_putf(&d, "mode=%s, text=%lu", name, (unsigned long)TEXTSZ); |
| 430 | regress_framing(rmode, fp, "header", d.buf, d.len); |
| 431 | |
| 432 | /* Start things up. */ |
| 433 | printf("%s: ", name); |
| 434 | setup(key, ksz ? ksz: sizeof(key)); |
| 435 | |
| 436 | /* Work through various sizes of up to three fragments. The middle |
| 437 | * fragment is the important one, since it can be misaligned or not at |
| 438 | * either end. |
| 439 | */ |
| 440 | sz0 = sz1 = minsz; |
| 441 | for (;;) { |
| 442 | |
| 443 | /* If output is to a terminal then display a spinner to keep humans |
| 444 | * amused. |
| 445 | */ |
| 446 | if (spin >= 0) { |
| 447 | printf("\r%s: [%c]\b\b", name, spinner[spin]); fflush(stdout); |
| 448 | spin = (spin + 1)&3; |
| 449 | } |
| 450 | |
| 451 | /* Prepare for the test. */ |
| 452 | sz2 = TEXTSZ - sz1 - sz0; |
| 453 | ok = 1; |
| 454 | |
| 455 | /* Encrypt the last fragment first, to check discarding behaviour. */ |
| 456 | if (sz2) { |
| 457 | reset(iv); |
| 458 | enc(text, 0, sz0); |
| 459 | enc(text + sz0, 0, sz1); |
| 460 | enc(text + sz0 + sz1, ct + sz0 + sz1, sz2); |
| 461 | } |
| 462 | |
| 463 | /* Encrypt the first two fragments. */ |
| 464 | reset(iv); |
| 465 | enc(text, ct, sz0); |
| 466 | if (sz1) { |
| 467 | memcpy(ct + sz0, text + sz0, sz1); |
| 468 | enc(ct + sz0, ct + sz0, sz1); |
| 469 | } |
| 470 | |
| 471 | /* Try to check consistency. We can't do this if (a) the mode is |
| 472 | * non-resumable and the fragments sizes are misaligned, or (b) this is |
| 473 | * our first pass through and we don't have a consistency reference yet. |
| 474 | * |
| 475 | * Also, decide whether to deploy the regression test, which we do if and |
| 476 | * only if we can't compare against the consistency reference. |
| 477 | */ |
| 478 | regr = 0; |
| 479 | if ((f&TEMF_REFALIGN) && (sz0%blksz || sz1%blksz)) regr = 1; |
| 480 | else if (!refp) { memcpy(ref, ct, TEXTSZ); regr = 1; refp = 1; } |
| 481 | else if (memcmp(ref, ct, TEXTSZ) != 0) { |
| 482 | ok = 0; |
| 483 | printf("\nConsistency failure (split = %lu/%lu/%lu)\n", |
| 484 | (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2); |
| 485 | dump_split("plaintext", blksz, text, sz0, sz1, sz2); |
| 486 | dump_split("reference", blksz, ref, sz0, sz1, sz2); |
| 487 | dump_split("ciphertext", blksz, ct, sz0, sz1, sz2); |
| 488 | fputc('\n', stdout); |
| 489 | } |
| 490 | |
| 491 | /* If we need the regression test then do that. Write a framing record |
| 492 | * to avoid confusion if the policy changes. |
| 493 | */ |
| 494 | if (regr) { |
| 495 | DRESET(&d); |
| 496 | dstr_putf(&d, "split = %lu/%lu/%lu", |
| 497 | (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2); |
| 498 | regress_framing(rmode, fp, "split", d.buf, d.len); |
| 499 | if (regress_crypto(rmode, fp, "regress", blksz, ct, sz0, sz1, sz2)) |
| 500 | ok = 0; |
| 501 | } |
| 502 | |
| 503 | /* Finally, decrypt and check that the round-trip works. */ |
| 504 | reset(iv); |
| 505 | dec(ct, pt, sz0); |
| 506 | if (sz1) { |
| 507 | memcpy(pt + sz0, ct + sz0, sz1); |
| 508 | dec(pt + sz0, pt + sz0, sz1); |
| 509 | } |
| 510 | if (sz2) |
| 511 | dec(ct + sz0 + sz1, pt + sz0 + sz1, sz2); |
| 512 | if (memcmp(text, pt, TEXTSZ) != 0) { |
| 513 | ok = 0; |
| 514 | printf("\nRound-trip failure (split = %lu/%lu/%lu)\n", |
| 515 | (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2); |
| 516 | dump_split("plaintext", blksz, text, sz0, sz1, sz2); |
| 517 | dump_split("ciphertext", blksz, ct, sz0, sz1, sz2); |
| 518 | dump_split("recovered", blksz, pt, sz0, sz1, sz2); |
| 519 | fputc('\n', stdout); |
| 520 | } |
| 521 | |
| 522 | /* Update the fragment sizes. */ |
| 523 | if (!sz1) break; |
| 524 | if (step(&sz1, TEXTSZ - sz0) == RESET) { |
| 525 | if (step(&sz0, TEXTSZ) == LIMIT) sz1 = 0; |
| 526 | else sz1 = minsz; |
| 527 | } |
| 528 | } |
| 529 | |
| 530 | /* Close the regression data file. */ |
| 531 | if (fp && (ferror(fp) || fclose(fp))) |
| 532 | die(1, "error closing `%s': %s", rname, strerror(errno)); |
| 533 | |
| 534 | /* Finish off the eyecandy spinner. */ |
| 535 | if (spin >= 0) printf("\r%s: [%c] ", name, ok ? '*' : 'X'); |
| 536 | |
| 537 | /* Summarize the test result. */ |
| 538 | if (ok) printf("ok\n"); |
| 539 | else printf("failed\n"); |
| 540 | |
| 541 | /* And we're done. */ |
| 542 | dstr_destroy(&d); |
| 543 | return (!ok); |
| 544 | } |
| 545 | |
| 546 | /*----- That's all, folks -------------------------------------------------*/ |