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> | |
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 | |
6a61f346 MW |
306 | * size of these buffers. @d@ might be null, to discard |
307 | * output; @s@ might be null, to process all-zero input. | |
57f459eb MW |
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 | ||
6a61f346 MW |
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. */ | |
57f459eb MW |
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 | } | |
57f459eb MW |
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 -------------------------------------------------*/ |