## Constant-type operations.
pkginclude_HEADERS += ct.h
-libbase_la_SOURCES += ct.c
+libbase_la_SOURCES += ct.c ct-test.c
## CPU-specific dispatch.
pkginclude_HEADERS += dispatch.h
--- /dev/null
+/* -*-c-*-
+ *
+ * Utilities for verifying constant-time programming
+ *
+ * (c) 2017 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 "config.h"
+
+#include "ct.h"
+
+#ifdef HAVE_VALGRIND_H
+# include <valgrind/valgrind.h>
+# include <valgrind/memcheck.h>
+#endif
+
+/*----- Main code ---------------------------------------------------------*/
+
+/* --- @ct_poison@ --- *
+ *
+ * Arguments: @const void *p@ = pointer to a secret
+ * @size_t sz@ = size of the secret
+ *
+ * Returns: ---
+ *
+ * Use: Ordinarily, does nothing. If the process is running under
+ * the control of Valgrind's `memcheck' utility, then mark the
+ * secret as `uninitialized', so that Valgrind warns about
+ * conditional execution or memory addressing based on the value
+ * of the secret.
+ *
+ * Credit for this idea goes to Adam Langley, who described it
+ * in https://www.imperialviolet.org/2010/04/01/ctgrind.html,
+ * though this implementation doesn't require patching Valgrind.
+ */
+
+void ct_poison(const void *p, size_t sz)
+ { VALGRIND_MAKE_MEM_UNDEFINED(p, sz); }
+
+/* --- @ct_remedy@ --- *
+ *
+ * Arguments: @const void *p@ = pointer to a secret
+ * @size_t sz@ = size of the secret
+ *
+ * Returns: ---
+ *
+ * Use: Ordinarily, does nothing. If the process is running under
+ * the control of Valgrind's `memcheck' utility, then mark the
+ * secret as `initialized'. This is intended to reverse the
+ * effect of @ct_poison@ so that a test program can verify
+ * function outputs wihtout Valgrind warning.
+ */
+
+void ct_remedy(const void *p, size_t sz)
+ { VALGRIND_MAKE_MEM_DEFINED(p, sz); }
+
+/*----- That's all, folks -------------------------------------------------*/
#include <mLib/bits.h>
-/*----- Functions provided ------------------------------------------------*/
+/*----- Miscellaneous constant-time utilities -----------------------------*/
/* --- @ct_inteq@ --- *
*
extern int ct_memeq(const void */*p*/, const void */*q*/, size_t /*n*/);
+/*----- Utilities for testing ---------------------------------------------*/
+
+/* --- @ct_poison@ --- *
+ *
+ * Arguments: @const void *p@ = pointer to a secret
+ * @size_t sz@ = size of the secret
+ *
+ * Returns: ---
+ *
+ * Use: Ordinarily, does nothing. If the process is running under
+ * the control of Valgrind's `memcheck' utility, then mark the
+ * secret as `uninitialized', so that Valgrind warns about
+ * conditional execution or memory addressing based on the value
+ * of the secret.
+ *
+ * Credit for this idea goes to Adam Langley, who described it
+ * in https://www.imperialviolet.org/2010/04/01/ctgrind.html,
+ * though this implementation doesn't require patching Valgrind.
+ */
+
+extern void ct_poison(const void */*p*/, size_t /*sz*/);
+
+/* --- @ct_remedy@ --- *
+ *
+ * Arguments: @const void *p@ = pointer to a secret
+ * @size_t sz@ = size of the secret
+ *
+ * Returns: ---
+ *
+ * Use: Ordinarily, does nothing. If the process is running under
+ * the control of Valgrind's `memcheck' utility, then mark the
+ * secret as `initialized'. This is intended to reverse the
+ * effect of @ct_poison@ so that a test program can verify
+ * function outputs wihtout Valgrind warning.
+ */
+
+extern void ct_remedy(const void */*p*/, size_t /*sz*/);
+
/*----- That's all, folks -------------------------------------------------*/
#ifdef __cplusplus
dnl Memory locking support.
AC_CHECK_FUNCS([mlock])
+dnl See if we can find Valgrind's header files.
+AC_CHECK_HEADER([valgrind/memcheck.h],
+ AC_DEFINE([HAVE_VALGRIND_H], [1],
+ [Define if the Valgrind header files are available.])
+ [])
+
dnl Set the master libraries we need.
AC_SUBST([CATACOMB_LIBS])
#include <mLib/report.h>
#include <mLib/testrig.h>
+#include "ct.h"
+
static int vrf_pubkey(dstr dv[])
{
dstr dpub = DSTR_INIT;
if (dv[1].len != ED25519_PUBSZ) die(1, "bad pub length");
+ ct_poison(dv[0].buf, dv[0].len);
dstr_ensure(&dpub, ED25519_PUBSZ); dpub.len = ED25519_PUBSZ;
ed25519_pubkey((octet *)dpub.buf, dv[0].buf, dv[0].len);
+ ct_remedy(dpub.buf, dpub.len);
if (memcmp(dpub.buf, dv[1].buf, ED25519_PUBSZ) != 0) {
ok = 0;
fprintf(stderr, "failed!");
if (want->len != ED25519_SIGSZ) die(1, "bad result length");
+ ct_poison(priv->buf, priv->len);
dstr_ensure(&dsig, ED25519_SIGSZ); dsig.len = ED25519_SIGSZ;
if (phflag <= 0)
m = msg;
ed25519ctx_sign((octet *)dsig.buf, priv->buf, priv->len, K,
phflag, perso ? perso->buf : 0, perso ? perso->len : 0,
m->buf, m->len);
+ ct_remedy(dsig.buf, dsig.len);
if (memcmp(dsig.buf, want->buf, ED25519_SIGSZ) != 0) {
ok = 0;
fprintf(stderr, "failed!");
#include <mLib/report.h>
#include <mLib/testrig.h>
+#include "ct.h"
+
static int vrf_pubkey(dstr dv[])
{
dstr dpub = DSTR_INIT;
if (dv[1].len != ED448_PUBSZ) die(1, "bad pub length");
+ ct_poison(dv[0].buf, dv[0].len);
dstr_ensure(&dpub, ED448_PUBSZ); dpub.len = ED448_PUBSZ;
ed448_pubkey((octet *)dpub.buf, dv[0].buf, dv[0].len);
+ ct_remedy(dpub.buf, dpub.len);
if (memcmp(dpub.buf, dv[1].buf, ED448_PUBSZ) != 0) {
ok = 0;
fprintf(stderr, "failed!");
if (want->len != ED448_SIGSZ) die(1, "bad result length");
+ ct_poison(priv->buf, priv->len);
dstr_ensure(&dsig, ED448_SIGSZ); dsig.len = ED448_SIGSZ;
if (phflag <= 0)
m = msg;
ed448_sign((octet *)dsig.buf, priv->buf, priv->len, K,
phflag, perso ? perso->buf : 0, perso ? perso->len : 0,
m->buf, m->len);
+ ct_remedy(dsig.buf, dsig.len);
if (memcmp(dsig.buf, want->buf, ED448_SIGSZ) != 0) {
ok = 0;
fprintf(stderr, "failed!");
#include <mLib/report.h>
#include <mLib/testrig.h>
+#include "ct.h"
+
static int vrf_x25519(dstr dv[])
{
dstr dz = DSTR_INIT;
if (dv[1].len != X25519_PUBSZ) die(1, "bad public length");
if (dv[2].len != X25519_OUTSZ) die(1, "bad result length");
+ ct_poison(dv[0].buf, dv[0].len);
dstr_ensure(&dz, X25519_OUTSZ); dz.len = X25519_OUTSZ;
x25519((octet *)dz.buf,
(const octet *)dv[0].buf,
(const octet *)dv[1].buf);
+ ct_remedy(dz.buf, dz.len);
if (memcmp(dz.buf, dv[2].buf, X25519_OUTSZ) != 0) {
ok = 0;
fprintf(stderr, "failed!");
#include <mLib/str.h>
#include <mLib/testrig.h>
+#include "ct.h"
+
static int vrf_x448(dstr dv[])
{
dstr dz = DSTR_INIT;
if (dv[1].len != X448_PUBSZ) die(1, "bad public length");
if (dv[2].len != X448_OUTSZ) die(1, "bad result length");
+ ct_poison(dv[0].buf, dv[0].len);
dstr_ensure(&dz, X448_OUTSZ); dz.len = X448_OUTSZ;
x448((octet *)dz.buf,
(const octet *)dv[0].buf,
(const octet *)dv[1].buf);
+ ct_remedy(dz.buf, dz.len);
if (memcmp(dz.buf, dv[2].buf, X448_OUTSZ) != 0) {
ok = 0;
fprintf(stderr, "failed!");
#include <mLib/testrig.h>
+#include "ct.h"
#include "rijndael-ecb.h"
static int vrf_hash(dstr v[])
if (v[3].len != 16) { fprintf(stderr, "bad tag length\n"); exit(2); }
dstr_ensure(&t, 16); t.len = 16;
+ ct_poison(v[0].buf, v[0].len);
poly1305_keyinit(&k, v[0].buf, v[0].len);
for (i = 0; i < v[2].len; i++) {
for (j = i; j < v[2].len; j++) {
poly1305_hash(&ctx, v[2].buf + i, j - i);
poly1305_hash(&ctx, v[2].buf + j, v[2].len - j);
poly1305_done(&ctx, t.buf);
+ ct_remedy(t.buf, t.len);
if (memcmp(t.buf, v[3].buf, 16) != 0) {
fprintf(stderr, "failed...");
fprintf(stderr, "\n\tkey = "); type_hex.dump(&v[0], stderr);