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