TEST_LIBS = libsymm.la
+noinst_LTLIBRARIES += libsymmtest.la
+libsymmtest_la_SOURCES =
+libsymmtest_la_CFLAGS = $(AM_CFLAGS) -DSRCDIR=\"$(srcdir)\"
+TEST_LIBS += libsymmtest.la
+
VPATH += $(srcdir)/modes
###--------------------------------------------------------------------------
pkginclude_HEADERS += $(GENMODES_H)
$(GENMODES_H): modes/gen-stamp
+## Additional test machinery.
+libsymmtest_la_SOURCES += modes-test.c modes-test.h
+
###--------------------------------------------------------------------------
### Autogenerated stub headers.
## Run the test programs.
TESTS += $(SYMM_TESTS)
EXTRA_DIST += $(SYMM_TEST_FILES)
+EXTRA_DIST += $(REGRESSION_TEST_FILES)
-## A piece of sample text for round-trip testing encryption modes.
-EXTRA_DIST += daftstory.h
+t/modes/%.regress:
+ $(MAKE) modes/$*.t && \
+ mkdir -p $(srcdir)/t/modes/ && \
+ modes/$*.t -o$(srcdir)/$@.new && \
+ mv $(srcdir)/$@.new $(srcdir)/$@
## Clean the debris from the `modes' subdirectory.
CLEANFILES += modes/*.to modes/*.t$(EXEEXT)
#ifdef TEST_RIG
-#include <stdio.h>
-
-#include "daftstory.h"
+#include "modes-test.h"
/* --- @CBC_TEST@ --- *
*
#define CBC_TESTX(PRE, pre, name, fname) \
\
-/* --- Initial plaintext for the test --- */ \
- \
-static const octet text[] = TEXT; \
+static pre##_ctx key; \
+static pre##_cbcctx ctx; \
\
-/* --- Key and IV to use --- */ \
+static void pre##_cbc_test_setup(const octet *k, size_t ksz) \
+ { pre##_init(&key, k, ksz); pre##_cbcsetkey(&ctx, &key); } \
\
-static const octet key[] = KEY; \
-static const octet iv[] = IV; \
+static void pre##_cbc_test_reset(const octet *iv) \
+ { pre##_cbcsetiv(&ctx, iv); } \
\
-/* --- Buffers for encryption and decryption output --- */ \
+static void pre##_cbc_test_enc(const octet *s, octet *d, size_t sz) \
+ { pre##_cbcencrypt(&ctx, s, d, sz); } \
\
-static octet ct[sizeof(text)]; \
-static octet pt[sizeof(text)]; \
+static void pre##_cbc_test_dec(const octet *s, octet *d, size_t sz) \
+ { pre##_cbcdecrypt(&ctx, s, d, sz); } \
\
-static void hexdump(const octet *p, size_t sz, size_t off) \
+int main(int argc, char *argv[]) \
{ \
- const octet *q = p + sz; \
- for (sz = 0; p < q; p++, sz++) { \
- printf("%02x", *p); \
- if ((off + sz + 1) % PRE##_BLKSZ == 0) \
- putchar(':'); \
- } \
-} \
- \
-int main(void) \
-{ \
- size_t sz = 0, rest; \
- pre##_cbcctx ctx; \
- pre##_ctx k; \
- int status = 0; \
- int done = 0; \
- \
- size_t keysz = PRE##_KEYSZ ? \
- PRE##_KEYSZ : strlen((const char *)key); \
- \
- fputs(name "-cbc: ", stdout); \
- \
- pre##_init(&k, key, keysz); \
- pre##_cbcsetkey(&ctx, &k); \
- \
- while (sz <= sizeof(text)) { \
- rest = sizeof(text) - sz; \
- memcpy(ct, text, sizeof(text)); \
- pre##_cbcsetiv(&ctx, iv); \
- pre##_cbcencrypt(&ctx, ct, ct, sz); \
- pre##_cbcencrypt(&ctx, ct + sz, ct + sz, rest); \
- memcpy(pt, ct, sizeof(text)); \
- pre##_cbcsetiv(&ctx, iv); \
- pre##_cbcdecrypt(&ctx, pt, pt, sz); \
- pre##_cbcdecrypt(&ctx, pt + sz, pt + sz, rest); \
- if (memcmp(pt, text, sizeof(text)) == 0) { \
- done++; \
- if (sizeof(text) < 40 || done % 8 == 0) \
- fputc('.', stdout); \
- if (done % 480 == 0) \
- fputs("\n\t", stdout); \
- fflush(stdout); \
- } else { \
- printf("\nError (sz = %lu)\n", (unsigned long)sz); \
- status = 1; \
- printf("\tplaintext = "); hexdump(text, sz, 0); \
- printf(", "); hexdump(text + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\tciphertext = "); hexdump(ct, sz, 0); \
- printf(", "); hexdump(ct + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\trecovered text = "); hexdump(pt, sz, 0); \
- printf(", "); hexdump(pt + sz, rest, sz); \
- fputc('\n', stdout); \
- fputc('\n', stdout); \
- } \
- if (sz < 63) \
- sz++; \
- else \
- sz += 9; \
- } \
- \
- fputs(status ? " failed\n" : " ok\n", stdout); \
- return (status); \
+ return test_encmode(fname "-cbc", PRE##_KEYSZ, PRE##_BLKSZ, \
+ 1, TEMF_REFALIGN, \
+ pre##_cbc_test_setup, pre##_cbc_test_reset, \
+ pre##_cbc_test_enc, pre##_cbc_test_dec, \
+ argc, argv); \
}
#else
#ifdef TEST_RIG
-#include <stdio.h>
-
-#include "daftstory.h"
+#include "modes-test.h"
/* --- @CFB_TEST@ --- *
*
#define CFB_TESTX(PRE, pre, name, fname) \
\
-/* --- Initial plaintext for the test --- */ \
- \
-static const octet text[] = TEXT; \
+static pre##_ctx key; \
+static pre##_cfbctx ctx; \
\
-/* --- Key and IV to use --- */ \
+static void pre##_cfb_test_setup(const octet *k, size_t ksz) \
+ { pre##_init(&key, k, ksz); pre##_cfbsetkey(&ctx, &key); } \
\
-static const octet key[] = KEY; \
-static const octet iv[] = IV; \
+static void pre##_cfb_test_reset(const octet *iv) \
+ { pre##_cfbsetiv(&ctx, iv); } \
\
-/* --- Buffers for encryption and decryption output --- */ \
+static void pre##_cfb_test_enc(const octet *s, octet *d, size_t sz) \
+ { pre##_cfbencrypt(&ctx, s, d, sz); } \
\
-static octet ct[sizeof(text)]; \
-static octet pt[sizeof(text)]; \
+static void pre##_cfb_test_dec(const octet *s, octet *d, size_t sz) \
+ { pre##_cfbdecrypt(&ctx, s, d, sz); } \
\
-static void hexdump(const octet *p, size_t sz, size_t off) \
+int main(int argc, char *argv[]) \
{ \
- const octet *q = p + sz; \
- for (sz = 0; p < q; p++, sz++) { \
- printf("%02x", *p); \
- if ((off + sz + 1) % PRE##_BLKSZ == 0) \
- putchar(':'); \
- } \
-} \
- \
-int main(void) \
-{ \
- size_t sz = 0, rest; \
- pre##_cfbctx ctx; \
- int status = 0; \
- int done = 0; \
- pre##_ctx k; \
- \
- size_t keysz = PRE##_KEYSZ ? \
- PRE##_KEYSZ : strlen((const char *)key); \
- \
- fputs(name "-cfb: ", stdout); \
- \
- pre##_init(&k, key, keysz); \
- pre##_cfbsetkey(&ctx, &k); \
- \
- while (sz <= sizeof(text)) { \
- rest = sizeof(text) - sz; \
- memcpy(ct, text, sizeof(text)); \
- pre##_cfbsetiv(&ctx, iv); \
- pre##_cfbencrypt(&ctx, ct, ct, sz); \
- pre##_cfbencrypt(&ctx, ct + sz, ct + sz, rest); \
- memcpy(pt, ct, sizeof(text)); \
- pre##_cfbsetiv(&ctx, iv); \
- pre##_cfbdecrypt(&ctx, pt, pt, rest); \
- pre##_cfbdecrypt(&ctx, pt + rest, pt + rest, sz); \
- if (memcmp(pt, text, sizeof(text)) == 0) { \
- done++; \
- if (sizeof(text) < 40 || done % 8 == 0) \
- fputc('.', stdout); \
- if (done % 480 == 0) \
- fputs("\n\t", stdout); \
- fflush(stdout); \
- } else { \
- printf("\nError (sz = %lu)\n", (unsigned long)sz); \
- status = 1; \
- printf("\tplaintext = "); hexdump(text, sz, 0); \
- printf(", "); hexdump(text + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\tciphertext = "); hexdump(ct, sz, 0); \
- printf(", "); hexdump(ct + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\trecovered text = "); hexdump(pt, sz, 0); \
- printf(", "); hexdump(pt + sz, rest, sz); \
- fputc('\n', stdout); \
- fputc('\n', stdout); \
- } \
- if (sz < 63) \
- sz++; \
- else \
- sz += 9; \
- } \
- \
- fputs(status ? " failed\n" : " ok\n", stdout); \
- return (status); \
+ return test_encmode(fname "-cfb", PRE##_KEYSZ, PRE##_BLKSZ, 1, 0, \
+ pre##_cfb_test_setup, pre##_cfb_test_reset, \
+ pre##_cfb_test_enc, pre##_cfb_test_dec, \
+ argc, argv); \
}
#else
#ifdef TEST_RIG
-#include <stdio.h>
-
-#include "daftstory.h"
+#include "modes-test.h"
/* --- @COUNTER_TEST@ --- *
*
#define COUNTER_TESTX(PRE, pre, name, fname) \
\
-/* --- Initial plaintext for the test --- */ \
- \
-static const octet text[] = TEXT; \
- \
-/* --- Key and IV to use --- */ \
+static pre##_ctx key; \
+static pre##_counterctx ctx; \
\
-static const octet key[] = KEY; \
-static const octet iv[] = IV; \
+static void pre##_counter_test_setup(const octet *k, size_t ksz) \
+ { pre##_init(&key, k, ksz); pre##_countersetkey(&ctx, &key); } \
\
-/* --- Buffers for encryption and decryption output --- */ \
+static void pre##_counter_test_reset(const octet *iv) \
+ { pre##_countersetiv(&ctx, iv); } \
\
-static octet ct[sizeof(text)]; \
-static octet pt[sizeof(text)]; \
- \
-static void hexdump(const octet *p, size_t sz, size_t off) \
-{ \
- const octet *q = p + sz; \
- for (sz = 0; p < q; p++, sz++) { \
- printf("%02x", *p); \
- if ((off + sz + 1) % PRE##_BLKSZ == 0) \
- putchar(':'); \
- } \
-} \
+static void pre##_counter_test_enc(const octet *s, octet *d, size_t sz) \
+ { pre##_counterencrypt(&ctx, s, d, sz); } \
\
-int main(void) \
+int main(int argc, char *argv[]) \
{ \
- size_t sz = 0, rest; \
- pre##_counterctx ctx; \
- int status = 0; \
- int done = 0; \
- pre##_ctx k; \
- \
- size_t keysz = PRE##_KEYSZ ? \
- PRE##_KEYSZ : strlen((const char *)key); \
- \
- fputs(name "-counter: ", stdout); \
- \
- pre##_init(&k, key, keysz); \
- pre##_countersetkey(&ctx, &k); \
- \
- while (sz <= sizeof(text)) { \
- rest = sizeof(text) - sz; \
- memcpy(ct, text, sizeof(text)); \
- pre##_countersetiv(&ctx, iv); \
- pre##_counterencrypt(&ctx, ct, ct, sz); \
- pre##_counterencrypt(&ctx, ct + sz, ct + sz, rest); \
- memcpy(pt, ct, sizeof(text)); \
- pre##_countersetiv(&ctx, iv); \
- pre##_counterencrypt(&ctx, pt, pt, rest); \
- pre##_counterencrypt(&ctx, pt + rest, pt + rest, sz); \
- if (memcmp(pt, text, sizeof(text)) == 0) { \
- done++; \
- if (sizeof(text) < 40 || done % 8 == 0) \
- fputc('.', stdout); \
- if (done % 480 == 0) \
- fputs("\n\t", stdout); \
- fflush(stdout); \
- } else { \
- printf("\nError (sz = %lu)\n", (unsigned long)sz); \
- status = 1; \
- printf("\tplaintext = "); hexdump(text, sz, 0); \
- printf(", "); hexdump(text + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\tciphertext = "); hexdump(ct, sz, 0); \
- printf(", "); hexdump(ct + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\trecovered text = "); hexdump(pt, sz, 0); \
- printf(", "); hexdump(pt + sz, rest, sz); \
- fputc('\n', stdout); \
- fputc('\n', stdout); \
- } \
- if (sz < 63) \
- sz++; \
- else \
- sz += 9; \
- } \
- \
- fputs(status ? " failed\n" : " ok\n", stdout); \
- return (status); \
+ return test_encmode(fname "-counter", PRE##_KEYSZ, PRE##_BLKSZ, 1, 0, \
+ pre##_counter_test_setup, pre##_counter_test_reset, \
+ pre##_counter_test_enc, pre##_counter_test_enc, \
+ argc, argv); \
}
#else
+++ /dev/null
-/* -*-c-*-
- *
- * Daft story for use in test encryptions
- *
- * (c) 1999 Straylight/Edgeware
- */
-
-/*----- Licensing notice --------------------------------------------------*
- *
- * This file is part of Catacomb.
- *
- * Catacomb is free software; you can redistribute it and/or modify
- * it under the terms of the GNU Library General Public License as
- * published by the Free Software Foundation; either version 2 of the
- * License, or (at your option) any later version.
- *
- * Catacomb is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU Library General Public License for more details.
- *
- * You should have received a copy of the GNU Library General Public
- * License along with Catacomb; if not, write to the Free
- * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
- * MA 02111-1307, USA.
- */
-
-#ifndef CATACOMB_DAFTSTORY_H
-#define CATACOMB_DAFTSTORY_H
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-/*----- Macros ------------------------------------------------------------*/
-
-/* --- Don't ask --- */
-
-#ifdef SMALL_TEST
-# define TEXT "A small piece of text for testing encryption."
-#else
-# define STORY "\
-Once upon a time there were a beautiful princess, a slightly nutty wizard,\n\
-and a watermelon. Now, the watermelon had decided that it probably wasn't\n\
-going to get very far with the princess unless it did something pretty\n\
-drastic. So it asked the wizard to turn it into a handsome prince.\n\
-\n\
-At least, this is the way that the wizard viewed the situation. He might\n\
-have just hallucinated it all; those mushrooms had looked ever so nice.\n\
-\n\
-Back to the point. The watermelon had expressed its desire not to be a\n\
-watermelon any more. And the wizard was probably tripping something quite\n\
-powerful. He hunted around a bit for his staff, and mumbled something\n\
-that film directors would think of as sounding appropriately arcane and\n\
-mystical (but was, in fact, just the ingredients list for an ancient\n\
-remedy for athlete's foot) and *pop*. Cooked watermelon. Yuk.\n\
-\n\
-Later in the year, the princess tripped over the hem of her dress, fell\n\
-down a spiral staircase, and died. The king ordered dressmakers to attach\n\
-safety warnings to long dresses.\n\
-\n\
-And the wizard? Who cares?\n\
-"
-# define TEXT STORY STORY
-#endif
-
-#define KEY "Penguins rule OK, rhubarb cauliflower"
-#define IV "EdgewareCatacomb, parsley, sage, rosemary and thyme"
-
-/*----- That's all, folks -------------------------------------------------*/
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif
#ifdef TEST_RIG
-#include <stdio.h>
-
-#include "daftstory.h"
+#include "modes-test.h"
/* --- @ECB_TEST@ --- *
*
#define ECB_TESTX(PRE, pre, name, fname) \
\
-/* --- Initial plaintext for the test --- */ \
- \
-static const octet text[] = TEXT; \
+static pre##_ctx key; \
+static pre##_ecbctx ctx; \
\
-/* --- Key and IV to use --- */ \
+static void pre##_ecb_test_setup(const octet *k, size_t ksz) \
+ { pre##_init(&key, k, ksz); pre##_ecbsetkey(&ctx, &key); } \
\
-static const octet key[] = KEY; \
-static const octet iv[] = IV; \
+static void pre##_ecb_test_reset(const octet *iv) \
+ { ; } \
\
-/* --- Buffers for encryption and decryption output --- */ \
+static void pre##_ecb_test_enc(const octet *s, octet *d, size_t sz) \
+ { pre##_ecbencrypt(&ctx, s, d, sz); } \
\
-static octet ct[sizeof(text)]; \
-static octet pt[sizeof(text)]; \
+static void pre##_ecb_test_dec(const octet *s, octet *d, size_t sz) \
+ { pre##_ecbdecrypt(&ctx, s, d, sz); } \
\
-static void hexdump(const octet *p, size_t sz, size_t off) \
+int main(int argc, char *argv[]) \
{ \
- const octet *q = p + sz; \
- for (sz = 0; p < q; p++, sz++) { \
- printf("%02x", *p); \
- if ((off + sz + 1) % PRE##_BLKSZ == 0) \
- putchar(':'); \
- } \
-} \
- \
-int main(void) \
-{ \
- size_t sz = 0, rest; \
- pre##_ecbctx ctx; \
- int status = 0; \
- int done = 0; \
- \
- size_t keysz = PRE##_KEYSZ ? \
- PRE##_KEYSZ : strlen((const char *)key); \
- \
- fputs(name "-ecb: ", stdout); \
- \
- pre##_ecbinit(&ctx, key, keysz, iv); \
- \
- while (sz <= sizeof(text)) { \
- rest = sizeof(text) - sz; \
- if ((sz != 0 && sz < PRE##_BLKSZ) || \
- (rest != 0 && rest < PRE##_BLKSZ)) \
- goto next; \
- memcpy(ct, text, sizeof(text)); \
- pre##_ecbencrypt(&ctx, ct, ct, sz); \
- pre##_ecbencrypt(&ctx, ct + sz, ct + sz, rest); \
- memcpy(pt, ct, sizeof(text)); \
- pre##_ecbdecrypt(&ctx, pt, pt, sz); \
- pre##_ecbdecrypt(&ctx, pt + sz, pt + sz, rest); \
- if (memcmp(pt, text, sizeof(text)) == 0) { \
- done++; \
- if (sizeof(text) < 40 || done % 8 == 0) \
- fputc('.', stdout); \
- if (done % 480 == 0) \
- fputs("\n\t", stdout); \
- fflush(stdout); \
- } else { \
- printf("\nError (sz = %lu)\n", (unsigned long)sz); \
- status = 1; \
- printf("\tplaintext = "); hexdump(text, sz, 0); \
- printf(", "); hexdump(text + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\tciphertext = "); hexdump(ct, sz, 0); \
- printf(", "); hexdump(ct + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\trecovered text = "); hexdump(pt, sz, 0); \
- printf(", "); hexdump(pt + sz, rest, sz); \
- fputc('\n', stdout); \
- fputc('\n', stdout); \
- } \
- next: \
- if (sz < 63) \
- sz++; \
- else \
- sz += 9; \
- } \
- \
- fputs(status ? " failed\n" : " ok\n", stdout); \
- return (status); \
+ return test_encmode(fname "-ecb", PRE##_KEYSZ, PRE##_BLKSZ, \
+ PRE##_BLKSZ, TEMF_REFALIGN, \
+ pre##_ecb_test_setup, pre##_ecb_test_reset, \
+ pre##_ecb_test_enc, pre##_ecb_test_dec, \
+ argc, argv); \
}
#else
#ifdef TEST_RIG
-#include <stdio.h>
-
-#include "daftstory.h"
+#include "modes-test.h"
/* --- @MGF_TEST@ --- *
*
#define MGF_TESTX(PRE, pre, name, fname) \
\
-/* --- Initial plaintext for the test --- */ \
- \
-static const octet text[] = TEXT; \
- \
-/* --- Key and IV to use --- */ \
+static pre##_mgfctx ctx; \
\
-static const octet key[] = KEY; \
+static void pre##_mgf_test_setup(const octet *k, size_t ksz) \
+ { pre##_mgfinit(&ctx, k, ksz); } \
\
-/* --- Buffers for encryption and decryption output --- */ \
+static void pre##_mgf_test_reset(const octet *iv) \
+ { pre##_mgfsetindex(&ctx, 0); } \
\
-static octet ct[sizeof(text)]; \
-static octet pt[sizeof(text)]; \
- \
-static void hexdump(const octet *p, size_t sz) \
-{ \
- const octet *q = p + sz; \
- for (sz = 0; p < q; p++, sz++) { \
- printf("%02x", *p); \
- if ((sz + 1) % PRE##_HASHSZ == 0) \
- putchar(':'); \
- } \
-} \
+static void pre##_mgf_test_enc(const octet *s, octet *d, size_t sz) \
+ { pre##_mgfencrypt(&ctx, s, d, sz); } \
\
-int main(void) \
+int main(int argc, char *argv[]) \
{ \
- size_t sz = 0, rest; \
- pre##_mgfctx ctx; \
- int status = 0; \
- int done = 0; \
- \
- size_t keysz = strlen((const char *)key); \
- \
- fputs(name "-mgf: ", stdout); \
- \
- pre##_mgfinit(&ctx, key, keysz); \
- \
- while (sz <= sizeof(text)) { \
- rest = sizeof(text) - sz; \
- memcpy(ct, text, sizeof(text)); \
- pre##_mgfsetindex(&ctx, 0); \
- pre##_mgfencrypt(&ctx, ct, ct, sz); \
- pre##_mgfencrypt(&ctx, ct + sz, ct + sz, rest); \
- memcpy(pt, ct, sizeof(text)); \
- pre##_mgfsetindex(&ctx, 0); \
- pre##_mgfencrypt(&ctx, pt, pt, rest); \
- pre##_mgfencrypt(&ctx, pt + rest, pt + rest, sz); \
- if (memcmp(pt, text, sizeof(text)) == 0) { \
- done++; \
- if (sizeof(text) < 40 || done % 8 == 0) \
- fputc('.', stdout); \
- if (done % 480 == 0) \
- fputs("\n\t", stdout); \
- fflush(stdout); \
- } else { \
- printf("\nError (sz = %lu)\n", (unsigned long)sz); \
- status = 1; \
- printf("\tplaintext = "); hexdump(text, sz); \
- printf(", "); hexdump(text + sz, rest); \
- fputc('\n', stdout); \
- printf("\tciphertext = "); hexdump(ct, sz); \
- printf(", "); hexdump(ct + sz, rest); \
- fputc('\n', stdout); \
- printf("\trecovered text = "); hexdump(pt, sz); \
- printf(", "); hexdump(pt + sz, rest); \
- fputc('\n', stdout); \
- fputc('\n', stdout); \
- } \
- if (sz < 63) \
- sz++; \
- else \
- sz += 9; \
- } \
- \
- fputs(status ? " failed\n" : " ok\n", stdout); \
- return (status); \
+ return test_encmode(fname "-mgf", 0, PRE##_HASHSZ, 1, 0, \
+ pre##_mgf_test_setup, pre##_mgf_test_reset, \
+ pre##_mgf_test_enc, pre##_mgf_test_enc, \
+ argc, argv); \
}
#else
--- /dev/null
+/* -*-c-*-
+ *
+ * Common code for testing encryption modes
+ *
+ * (c) 2018 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Catacomb is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <unistd.h>
+
+#include <mLib/alloc.h>
+#include <mLib/bits.h>
+#include <mLib/dstr.h>
+#include <mLib/quis.h>
+#include <mLib/report.h>
+
+#include "modes-test.h"
+
+/*----- The reference data ------------------------------------------------*/
+
+#ifdef SMALL_TEST
+static const octet text[] = "A small piece of text for testing encryption.";
+#else
+#define STORY "\
+Once upon a time there were a beautiful princess, a slightly nutty wizard,\n\
+and a watermelon. Now, the watermelon had decided that it probably wasn't\n\
+going to get very far with the princess unless it did something pretty\n\
+drastic. So it asked the wizard to turn it into a handsome prince.\n\
+\n\
+At least, this is the way that the wizard viewed the situation. He might\n\
+have just hallucinated it all; those mushrooms had looked ever so nice.\n\
+\n\
+Back to the point. The watermelon had expressed its desire not to be a\n\
+watermelon any more. And the wizard was probably tripping something quite\n\
+powerful. He hunted around a bit for his staff, and mumbled something\n\
+that film directors would think of as sounding appropriately arcane and\n\
+mystical (but was, in fact, just the ingredients list for an ancient\n\
+remedy for athlete's foot) and *pop*. Cooked watermelon. Yuk.\n\
+\n\
+Later in the year, the princess tripped over the hem of her dress, fell\n\
+down a spiral staircase, and died. The king ordered dressmakers to attach\n\
+safety warnings to long dresses.\n\
+\n\
+And the wizard? Who cares?\n\
+"
+static const octet text[] = STORY STORY;
+#endif
+
+#define TEXTSZ (sizeof(text))
+
+static const octet key[] = "Penguins rule OK, rhubarb cauliflower",
+ iv[] = "EdgewareCatacomb, parsley, sage, rosemary and thyme";
+
+/*----- Static variables --------------------------------------------------*/
+
+/* Encryption buffers, for ciphertext, recovered plaintext, and consistency
+ * reference.
+ */
+static octet ct[TEXTSZ], pt[TEXTSZ], ref[TEXTSZ];
+
+/* A resizeable buffer for verifying regression data. */
+static octet *t = 0; size_t tsz = 0;
+
+/*----- Diagnostic utilities ----------------------------------------------*/
+
+/* Print the @sz@-byte buffer @p@, beginning at offset @off@ within some
+ * larger buffer, marking block boundaries every @blksz@ bytes.
+ */
+static void hexdump(const octet *p, size_t sz, size_t off, size_t blksz)
+{
+ const octet *q = p + sz;
+ for (sz = 0; p < q; p++, sz++) {
+ printf("%02x", *p);
+ if ((off + sz + 1)%blksz == 0) putchar(':');
+ }
+}
+
+/* Print the buffer @p@, labelling it as @what@, splitting it into three
+ * pieces of sizes @sz0@, @sz1@, and @sz2@ respectively. Block boundaries
+ * every @blksz@ bytes are shown consistency, independent of the split
+ * positions.
+ */
+static void dump_split(const char *what, size_t blksz, const octet *p,
+ size_t sz0, size_t sz1, size_t sz2)
+{
+ printf("\t%-16s = ", what);
+ hexdump(p, sz0, 0, blksz);
+ if (sz1) { printf(", "); hexdump(p + sz0, sz1, sz0, blksz); }
+ if (sz2) { printf(", "); hexdump(p + sz0 + sz1, sz2, sz0 + sz1, blksz); }
+ fputc('\n', stdout);
+}
+
+/*----- Regression-data utilities -----------------------------------------*/
+
+/* Regression modes. We can @CHECK@ existing data, @RECORD@ new data, or
+ * @IGNORE@ the regression testing entirely.
+ */
+enum { IGNORE, RECORD, CHECK };
+
+/* Read or write regression data from/to @fp@ according to @rmode@. The data
+ * item is described as @what@ in diagnostic messages, and consists of @sz@
+ * bytes beginning at @p@.
+ *
+ * If @rmode@ is @IGNORE@, then this function does nothing; if @rmode@ is
+ * @RECORD@, then it writes @p@ to the output file with some framing; and if
+ * @rmode@ is @CHECK@ then it checks that the next chunk of data from the
+ * file matches @p@.
+ *
+ * Returns zero if all is well or @-1@ on a mismatch; I/O errors are fatal.
+ *
+ * Framing is trivial and consists of a 4-byte big-endian non-inclusive
+ * length prepended to each buffer. No padding is written to maintain
+ * alignment.
+ */
+static int regress_data(int rmode, FILE *fp, const char *what,
+ const void *p, size_t sz)
+{
+ octet b[4];
+ size_t psz;
+
+ switch (rmode) {
+ case IGNORE:
+ return (0);
+ case RECORD:
+ STORE32(b, sz);
+ if (!fwrite(b, 4, 1, fp) || !fwrite(p, sz, 1, fp))
+ die(1, "failed to write %s: %s", what, strerror(errno));
+ return (0);
+ case CHECK:
+ if (!fread(b, 4, 1, fp))
+ die(1, "failed to read %s length: %s", what,
+ ferror(fp) ? strerror(errno) : "unexpected eof");
+ psz = LOAD32(b);
+ if (psz != sz)
+ die(1, "incorrect %s length (%lu /= %lu; sync failure?)",
+ what, (unsigned long)psz, (unsigned long)sz);
+ if (tsz < sz) { xfree(t); t = xmalloc(sz); tsz = sz; }
+ if (!fread(t, sz, 1, fp))
+ die(1, "failed to read %s: %s", what,
+ ferror(fp) ? strerror(errno) : "unexpected eof");
+ if (memcmp(p, t, sz) != 0) return (-1);
+ return (0);
+ default:
+ abort();
+ }
+}
+
+/* Read or write framing data from/to @fp@ according to @rmode@. The framing
+ * item is describd as @what@ in diagnostic messages, and consists of @sz@
+ * bytes beginning at @p@.
+ *
+ * Framing data is used to verify that a recorded regression-data file is
+ * still appropriate for use. A fatal error is reported on any kind of
+ * failure.
+ */
+static void regress_framing(int rmode, FILE *fp, const char *what,
+ const void *p, size_t sz)
+{
+ if (regress_data(rmode, fp, what, p, sz))
+ die(1, "regression framing mismatch for %s (bug, or wrong file)", what);
+}
+
+/* Read or write crypto data from/to @fp@ according to @rmode@. The data
+ * item is describd as @what@ in diagnostic messages, and consists of the
+ * bytes beginning at @p@. For the purposes of diagnostics, this buffer has
+ * been notionally split into three pieces, with sizes @sz0@, @sz1@, and
+ * @sz2@, respectively.
+ *
+ * If al is well, return zero. If the crypto data doesn't match the recorded
+ * regression data, then report the mismatch, showing the way in which the
+ * buffer is split, and return -1. I/O errors are fatal.
+ */
+static int regress_crypto(int rmode, FILE *fp, const char *what, size_t blksz,
+ const void *p, size_t sz0, size_t sz1, size_t sz2)
+{
+ int rc;
+
+ rc = regress_data(rmode, fp, what, p, sz0 + sz1 + sz2);
+ if (rc) {
+ printf("\nRegression mismatch (split = %lu/%lu/%lu)\n",
+ (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2);
+ dump_split("plaintext", blksz, text, sz0, sz1, sz2);
+ dump_split("expected ct", blksz, t, sz0, sz1, sz2);
+ dump_split("computed ct", blksz, p, sz0, sz1, sz2);
+ fputc('\n', stdout);
+ }
+ return (rc);
+}
+
+/*----- Selecting fragment sizes ------------------------------------------*/
+
+/* Return codes from @step@. */
+enum { STEP, LIMIT, RESET };
+
+/* Update @*sz_inout@ the next largest suitable fragment size, up to a
+ * maximum of @max@.
+ *
+ * If the new size is still smaller than the maximum, then return @STEP@. If
+ * the size is maximal, then return @LIMIT@. If the size was previously
+ * maximal already, then return @RESET@.
+ *
+ * The sizes here are selected powers of two, and powers of two plus or minus
+ * 1, with the objective of testing how internal buffering is affected when
+ * the cursor is misaligned and realigned with block boundaries.
+ */
+static int step(size_t *sz_inout, size_t max)
+{
+ size_t i;
+
+ static size_t steps[] = { 1, 7, 8, 9, 15, 16, 17,
+ 63, 64, 65, 255, 256, 257 };
+
+ if (*sz_inout == max) return (RESET);
+ for (i = 0; i < N(steps); i++)
+ if (steps[i] > *sz_inout) {
+ if (steps[i] < max) { *sz_inout = steps[i]; return (STEP); }
+ else break;
+ }
+ *sz_inout = max; return (LIMIT);
+}
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @test_encmode@ --- *
+ *
+ * Arguments: @const char *name@ = name of the encryption scheme; used to
+ * find the regression-test filename
+ * @size_t ksz@ = key size to use, or zero for `don't care'
+ * @size_t blksz@ = block size
+ * @size_t minsz@ = smallest acceptable buffer size, or 1
+ * @unsigned f@ = various additional flags
+ * @setupfn *setup@ = key-setup function
+ * @resetfn *reset@ = state-reset function
+ * @encfn *enc@ = encryption function
+ * @decfn *dec@ = decryption function
+ * @int argc@ = number of command-line arguments
+ * @char *argv@ = pointer to command-line argument vector
+ *
+ * Returns: Zero on success, nonzero to report failure.
+ *
+ * Use: Tests an encryption mode which doesn't have any more formal
+ * test vectors.
+ *
+ * The @name@ is used firstly in diagnostic output and secondly
+ * to form the default filename to use for regression-test data,
+ * as `.../symm/t/modes/NAME.regress'.
+ *
+ * The key size @ksz@ is simply passed on back to the @setup@
+ * function, unless the caller passes in zero, in which case
+ * @test_encmode@ chooses a key size for itself.
+ *
+ * The block size @blksz@ is used in failure reports, to draw
+ * attention to the block structure in the various buffers,
+ * which may assist with diagnosis. It's also used to determine
+ * when to apply a consistency check: see below regarding the
+ * @TEMF_REFALIGN@ flag.
+ *
+ * The minimum buffer size @minsz@ expresses a limitation on the
+ * provided @enc@ and @dec@ functions, that they don't work on
+ * inputs smaller than @minsz@; accordingly, @test_encmode@ will
+ * not test such small sizes. This should be 1 if the mode has
+ * no limitation.
+ *
+ * The flags @f@ influence testing in various ways explained
+ * below.
+ *
+ * The caller-provided functions are assumed to act on some
+ * global but hidden state,
+ *
+ * * @setup@ is (currently, at least) called only once, with
+ * the key @k@ and its chosen size @ksz@.
+ *
+ * * @reset@ is called at the start of each encryption or
+ * decryption operation, to program in the initialization
+ * vector to use. Currently, the same IV is used in all of
+ * the tests, but this might not always be the case.
+ *
+ * * @enc@ is called to encrypt a source buffer @s@ and write
+ * the ciphertext to a destination @d@; @sz@ is the common
+ * size of these buffers.
+ *
+ * * @dec@ is called to decrypt a source buffer @s@ and write
+ * the recovered plaintext to a destination @d@; @sz@ is the
+ * common size of these buffers.
+ *
+ * Finally, @int argc@ and @char *argv@ are the command-line
+ * arguments provided to @main@; @test_encmode@ parses these and
+ * alters its behaviour accordingly.
+ *
+ * Currently, @test_encmode@'s tests are built around a single,
+ * fairly large, fixed message. In each test step, the message
+ * is split into a number of fragments which are encrypted and
+ * decrypted in turn.
+ *
+ * The following tests are performed.
+ *
+ * * The fundamental `round-trip' test, which verifies that
+ * the message can be encrypted and then decrypted
+ * successfully, if the same fragment boundaries are used in
+ * both cases.
+ *
+ * * A `consistency' test. Some modes, such as CFB, OFB, and
+ * counter, are `resumable': encryption operations are
+ * insensitive to the position of fragment boundaries, so a
+ * single message can be broken into fragments without
+ * affecting the result. If @TEMF_REFALIGN@ is clear then
+ * the mode under test is verified to have this property.
+ * If @TEMF_REFALIGN' is set, a weaker property is verified:
+ * that encryption is insensitive to the position of
+ * /block-aligned/ fragment boundaries only.
+ *
+ * * A `regression' test, which verifies that the code
+ * produces the same ciphertext as a previous version. By
+ * setting command-line arguments appropriately, a test
+ * program can be told to record ciphertexts in a (binary)
+ * data file. Usually, instead, the program will read the
+ * recorded ciphertexts back and verify that it produces the
+ * same data. For resumable modes, it's only necessary to
+ * record single ciphertext, since all the other ciphertexts
+ * must be equal by consistency; otherwise all non-block-
+ * aligned splits are recorded separately.
+ */
+
+int test_encmode(const char *name,
+ size_t ksz, size_t blksz, size_t minsz, unsigned f,
+ setupfn *setup, resetfn *reset, encfn *enc, encfn *dec,
+ int argc, char *argv[])
+{
+ int ok = 1, refp = 0, i;
+ size_t sz0, sz1, sz2;
+ const char spinner[] = "/-\\|";
+ int rmode = CHECK, spin = isatty(STDOUT_FILENO) ? 0 : -1;
+ int regr;
+ const char *rname = 0, *p;
+ FILE *fp;
+ dstr d = DSTR_INIT;
+
+ ego(argv[0]);
+
+ /* Parse the command-line options. */
+ p = 0; i = 1;
+ for (;;) {
+
+ /* Read the next argument. */
+ if (!p || !*p) {
+ if (i >= argc) break;
+ p = argv[i++];
+ if (strcmp(p, "--") == 0) break;
+ if (p[0] != '-' || p[1] == 0) { i--; break; }
+ p++;
+ }
+
+ /* Interpret an option. */
+ switch (*p++) {
+ case 'h':
+ printf("%s test driver\n"
+ "Usage: %s [-i] [-o|-f FILENAME]\n", QUIS, QUIS);
+ exit(0);
+ case 'i':
+ rmode = IGNORE;
+ break;
+ case 'o':
+ if (!*p) {
+ if (i >= argc) die(1, "option `-o' expects an argument");
+ p = argv[i++];
+ }
+ rmode = RECORD; rname = p; p = 0;
+ break;
+ case 'f':
+ if (!*p) {
+ if (i >= argc) die(1, "option `-f' expects an argument");
+ p = argv[i++];
+ }
+ rmode = CHECK; rname = p; p = 0;
+ break;
+ default:
+ die(1, "option `-%c' unknown", p[-1]);
+ }
+ }
+
+ /* Check there's nothing else left. */
+ if (i < argc) die(1, "trailing junk on command line");
+
+ /* Open the regression-data file. */
+ if (rmode == IGNORE)
+ fp = 0;
+ else {
+ if (!rname) {
+ DRESET(&d); dstr_putf(&d, SRCDIR"/t/modes/%s.regress", name);
+ rname = xstrdup(d.buf);
+ }
+ fp = fopen(rname, rmode == RECORD ? "wb" : "rb");
+ if (!fp)
+ die(1, "failed to open `%s' for %s: %s", rname,
+ rmode == RECORD ? "writing" : "reading", strerror(errno));
+ }
+
+ /* Write a header describing the file, to trap misuse for the wrong mode,
+ * and changes in the text.
+ */
+ DRESET(&d);
+ dstr_putf(&d, "mode=%s, text=%lu", name, (unsigned long)TEXTSZ);
+ regress_framing(rmode, fp, "header", d.buf, d.len);
+
+ /* Start things up. */
+ printf("%s: ", name);
+ setup(key, ksz ? ksz: sizeof(key));
+
+ /* Work through various sizes of up to three fragments. The middle
+ * fragment is the important one, since it can be misaligned or not at
+ * either end.
+ */
+ sz0 = sz1 = minsz;
+ for (;;) {
+
+ /* If output is to a terminal then display a spinner to keep humans
+ * amused.
+ */
+ if (spin >= 0) {
+ printf("\r%s: [%c]\b\b", name, spinner[spin]); fflush(stdout);
+ spin = (spin + 1)&3;
+ }
+
+ /* Prepare for the test. */
+ sz2 = TEXTSZ - sz1 - sz0;
+ ok = 1;
+
+ /* Encrypt the three fragments. */
+ reset(iv);
+ enc(text, ct, sz0);
+ if (sz1) {
+ memcpy(ct + sz0, text + sz0, sz1);
+ enc(ct + sz0, ct + sz0, sz1);
+ }
+ if (sz2)
+ enc(text + sz0 + sz1, ct + sz0 + sz1, sz2);
+
+ /* Try to check consistency. We can't do this if (a) the mode is
+ * non-resumable and the fragments sizes are misaligned, or (b) this is
+ * our first pass through and we don't have a consistency reference yet.
+ *
+ * Also, decide whether to deploy the regression test, which we do if and
+ * only if we can't compare against the consistency reference.
+ */
+ regr = 0;
+ if ((f&TEMF_REFALIGN) && (sz0%blksz || sz1%blksz)) regr = 1;
+ else if (!refp) { memcpy(ref, ct, TEXTSZ); regr = 1; refp = 1; }
+ else if (memcmp(ref, ct, TEXTSZ) != 0) {
+ ok = 0;
+ printf("\nConsistency failure (split = %lu/%lu/%lu)\n",
+ (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2);
+ dump_split("plaintext", blksz, text, sz0, sz1, sz2);
+ dump_split("reference", blksz, ref, sz0, sz1, sz2);
+ dump_split("ciphertext", blksz, ct, sz0, sz1, sz2);
+ fputc('\n', stdout);
+ }
+
+ /* If we need the regression test then do that. Write a framing record
+ * to avoid confusion if the policy changes.
+ */
+ if (regr) {
+ DRESET(&d);
+ dstr_putf(&d, "split = %lu/%lu/%lu",
+ (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2);
+ regress_framing(rmode, fp, "split", d.buf, d.len);
+ if (regress_crypto(rmode, fp, "regress", blksz, ct, sz0, sz1, sz2))
+ ok = 0;
+ }
+
+ /* Finally, decrypt and check that the round-trip works. */
+ reset(iv);
+ dec(ct, pt, sz0);
+ if (sz1) {
+ memcpy(pt + sz0, ct + sz0, sz1);
+ dec(pt + sz0, pt + sz0, sz1);
+ }
+ if (sz2)
+ dec(ct + sz0 + sz1, pt + sz0 + sz1, sz2);
+ if (memcmp(text, pt, TEXTSZ) != 0) {
+ ok = 0;
+ printf("\nRound-trip failure (split = %lu/%lu/%lu)\n",
+ (unsigned long)sz0, (unsigned long)sz1, (unsigned long)sz2);
+ dump_split("plaintext", blksz, text, sz0, sz1, sz2);
+ dump_split("ciphertext", blksz, ct, sz0, sz1, sz2);
+ dump_split("recovered", blksz, pt, sz0, sz1, sz2);
+ fputc('\n', stdout);
+ }
+
+ /* Update the fragment sizes. */
+ if (!sz1) break;
+ if (step(&sz1, TEXTSZ - sz0) == RESET) {
+ if (step(&sz0, TEXTSZ) == LIMIT) sz1 = 0;
+ else sz1 = minsz;
+ }
+ }
+
+ /* Close the regression data file. */
+ if (fp && (ferror(fp) || fclose(fp)))
+ die(1, "error closing `%s': %s", rname, strerror(errno));
+
+ /* Finish off the eyecandy spinner. */
+ if (spin >= 0) printf("\r%s: [%c] ", name, ok ? '*' : 'X');
+
+ /* Summarize the test result. */
+ if (ok) printf("ok\n");
+ else printf("failed\n");
+
+ /* And we're done. */
+ dstr_destroy(&d);
+ return (!ok);
+}
+
+/*----- That's all, folks -------------------------------------------------*/
--- /dev/null
+/* -*-c-*-
+ *
+ * Common testing for encryption modes
+ *
+ * (c) 2018 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Catacomb.
+ *
+ * Catacomb is free software: you can redistribute it and/or modify it
+ * under the terms of the GNU Library General Public License as published
+ * by the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * Catacomb is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with Catacomb. If not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifndef CATACOMB_MODES_TEST_H
+#define CATACOMB_MODES_TEST_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/*----- Header files ------------------------------------------------------*/
+
+#include <mLib/bits.h>
+
+/*----- Data structures ---------------------------------------------------*/
+
+/* Functions used by `test_encmode' below. */
+typedef void setupfn(const octet */*k*/, size_t /*ksz*/);
+typedef void resetfn(const octet */*iv*/);
+typedef void encfn(const octet */*s*/, octet */*d*/, size_t /*sz*/);
+
+/*----- Functions provided ------------------------------------------------*/
+
+#define TEMF_REFALIGN 1u /* misalignment of pieces affects
+ * the encryption state
+ */
+
+/* --- @test_encmode@ --- *
+ *
+ * Arguments: @const char *name@ = name of the encryption scheme; used to
+ * find the regression-test filename
+ * @size_t ksz@ = key size to use, or zero for `don't care'
+ * @size_t blksz@ = block size
+ * @size_t minsz@ = smallest acceptable buffer size, or 1
+ * @unsigned f@ = various additional flags
+ * @setupfn *setup@ = key-setup function
+ * @resetfn *reset@ = state-reset function
+ * @encfn *enc@ = encryption function
+ * @decfn *dec@ = decryption function
+ * @int argc@ = number of command-line arguments
+ * @char *argv@ = pointer to command-line argument vector
+ *
+ * Returns: Zero on success, nonzero to report failure.
+ *
+ * Use: Tests an encryption mode which doesn't have any more formal
+ * test vectors.
+ *
+ * The @name@ is used firstly in diagnostic output and secondly
+ * to form the default filename to use for regression-test data,
+ * as `.../symm/t/modes/NAME.regress'.
+ *
+ * The key size @ksz@ is simply passed on back to the @setup@
+ * function, unless the caller passes in zero, in which case
+ * @test_encmode@ chooses a key size for itself.
+ *
+ * The block size @blksz@ is used in failure reports, to draw
+ * attention to the block structure in the various buffers,
+ * which may assist with diagnosis. It's also used to determine
+ * when to apply a consistency check: see below regarding the
+ * @TEMF_REFALIGN@ flag.
+ *
+ * The minimum buffer size @minsz@ expresses a limitation on the
+ * provided @enc@ and @dec@ functions, that they don't work on
+ * inputs smaller than @minsz@; accordingly, @test_encmode@ will
+ * not test such small sizes. This should be 1 if the mode has
+ * no limitation.
+ *
+ * The flags @f@ influence testing in various ways explained
+ * below.
+ *
+ * The caller-provided functions are assumed to act on some
+ * global but hidden state,
+ *
+ * * @setup@ is (currently, at least) called only once, with
+ * the key @k@ and its chosen size @ksz@.
+ *
+ * * @reset@ is called at the start of each encryption or
+ * decryption operation, to program in the initialization
+ * vector to use. Currently, the same IV is used in all of
+ * the tests, but this might not always be the case.
+ *
+ * * @enc@ is called to encrypt a source buffer @s@ and write
+ * the ciphertext to a destination @d@; @sz@ is the common
+ * size of these buffers.
+ *
+ * * @dec@ is called to decrypt a source buffer @s@ and write
+ * the recovered plaintext to a destination @d@; @sz@ is the
+ * common size of these buffers.
+ *
+ * Finally, @int argc@ and @char *argv@ are the command-line
+ * arguments provided to @main@; @test_encmode@ parses these and
+ * alters its behaviour accordingly.
+ *
+ * Currently, @test_encmode@'s tests are built around a single,
+ * fairly large, fixed message. In each test step, the message
+ * is split into a number of fragments which are encrypted and
+ * decrypted in turn.
+ *
+ * The following tests are performed.
+ *
+ * * The fundamental `round-trip' test, which verifies that
+ * the message can be encrypted and then decrypted
+ * successfully, if the same fragment boundaries are used in
+ * both cases.
+ *
+ * * A `consistency' test. Some modes, such as CFB, OFB, and
+ * counter, are `resumable': encryption operations are
+ * insensitive to the position of fragment boundaries, so a
+ * single message can be broken into fragments without
+ * affecting the result. If @TEMF_REFALIGN@ is clear then
+ * the mode under test is verified to have this property.
+ * If @TEMF_REFALIGN' is set, a weaker property is verified:
+ * that encryption is insensitive to the position of
+ * /block-aligned/ fragment boundaries only.
+ *
+ * * A `regression' test, which verifies that the code
+ * produces the same ciphertext as a previous version. By
+ * setting command-line arguments appropriately, a test
+ * program can be told to record ciphertexts in a (binary)
+ * data file. Usually, instead, the program will read the
+ * recorded ciphertexts back and verify that it produces the
+ * same data. For resumable modes, it's only necessary to
+ * record single ciphertext, since all the other ciphertexts
+ * must be equal by consistency; otherwise all non-block-
+ * aligned splits are recorded separately.
+ */
+
+extern int test_encmode(const char */*name*/,
+ size_t /*ksz*/, size_t /*blksz*/,
+ size_t /*minsz */, unsigned /*f*/,
+ setupfn */*setup*/, resetfn */*reset*/,
+ encfn */*enc*/, encfn */*dec*/,
+ int /*argc*/, char */*argv*/[]);
+
+/*----- That's all, folks -------------------------------------------------*/
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
%repeat
SYMM_TEST_FILES += t/@{hash:f}
%end
+
+## Regression-test files.
+REGRESSION_TEST_FILES =
+%repeat
+REGRESSION_TEST_FILES += t/modes/@{blkc:f}-@{blkcciphermode}.regress
+%end
+%repeat
+REGRESSION_TEST_FILES += t/modes/@{hash:f}-@{hashciphermode}.regress
+%end
#ifdef TEST_RIG
-#include <stdio.h>
-
-#include "daftstory.h"
+#include "modes-test.h"
/* --- @OFB_TEST@ --- *
*
#define OFB_TESTX(PRE, pre, name, fname) \
\
-/* --- Initial plaintext for the test --- */ \
- \
-static const octet text[] = TEXT; \
- \
-/* --- Key and IV to use --- */ \
+static pre##_ctx key; \
+static pre##_ofbctx ctx; \
\
-static const octet key[] = KEY; \
-static const octet iv[] = IV; \
+static void pre##_ofb_test_setup(const octet *k, size_t ksz) \
+ { pre##_init(&key, k, ksz); pre##_ofbsetkey(&ctx, &key); } \
\
-/* --- Buffers for encryption and decryption output --- */ \
+static void pre##_ofb_test_reset(const octet *iv) \
+ { pre##_ofbsetiv(&ctx, iv); } \
\
-static octet ct[sizeof(text)]; \
-static octet pt[sizeof(text)]; \
- \
-static void hexdump(const octet *p, size_t sz, size_t off) \
-{ \
- const octet *q = p + sz; \
- for (sz = 0; p < q; p++, sz++) { \
- printf("%02x", *p); \
- if ((off + sz + 1) % PRE##_BLKSZ == 0) \
- putchar(':'); \
- } \
-} \
+static void pre##_ofb_test_enc(const octet *s, octet *d, size_t sz) \
+ { pre##_ofbencrypt(&ctx, s, d, sz); } \
\
-int main(void) \
+int main(int argc, char *argv[]) \
{ \
- size_t sz = 0, rest; \
- pre##_ofbctx ctx; \
- int status = 0; \
- int done = 0; \
- pre##_ctx k; \
- \
- size_t keysz = PRE##_KEYSZ ? \
- PRE##_KEYSZ : strlen((const char *)key); \
- \
- fputs(name "-ofb: ", stdout); \
- \
- pre##_init(&k, key, keysz); \
- pre##_ofbsetkey(&ctx, &k); \
- \
- while (sz <= sizeof(text)) { \
- rest = sizeof(text) - sz; \
- memcpy(ct, text, sizeof(text)); \
- pre##_ofbsetiv(&ctx, iv); \
- pre##_ofbencrypt(&ctx, ct, ct, sz); \
- pre##_ofbencrypt(&ctx, ct + sz, ct + sz, rest); \
- memcpy(pt, ct, sizeof(text)); \
- pre##_ofbsetiv(&ctx, iv); \
- pre##_ofbencrypt(&ctx, pt, pt, rest); \
- pre##_ofbencrypt(&ctx, pt + rest, pt + rest, sz); \
- if (memcmp(pt, text, sizeof(text)) == 0) { \
- done++; \
- if (sizeof(text) < 40 || done % 8 == 0) \
- fputc('.', stdout); \
- if (done % 480 == 0) \
- fputs("\n\t", stdout); \
- fflush(stdout); \
- } else { \
- printf("\nError (sz = %lu)\n", (unsigned long)sz); \
- status = 1; \
- printf("\tplaintext = "); hexdump(text, sz, 0); \
- printf(", "); hexdump(text + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\tciphertext = "); hexdump(ct, sz, 0); \
- printf(", "); hexdump(ct + sz, rest, sz); \
- fputc('\n', stdout); \
- printf("\trecovered text = "); hexdump(pt, sz, 0); \
- printf(", "); hexdump(pt + sz, rest, sz); \
- fputc('\n', stdout); \
- fputc('\n', stdout); \
- } \
- if (sz < 63) \
- sz++; \
- else \
- sz += 9; \
- } \
- \
- fputs(status ? " failed\n" : " ok\n", stdout); \
- return (status); \
+ return test_encmode(fname "-ofb", PRE##_KEYSZ, PRE##_BLKSZ, 1, 0, \
+ pre##_ofb_test_setup, pre##_ofb_test_reset, \
+ pre##_ofb_test_enc, pre##_ofb_test_enc, \
+ argc, argv); \
}
#else