chiark / gitweb /
bytestring.c, catacomb/__init__.py: Compare for equality in constant time.
authorMark Wooding <mdw@distorted.org.uk>
Thu, 26 May 2016 08:26:09 +0000 (09:26 +0100)
committerMark Wooding <mdw@distorted.org.uk>
Wed, 12 Apr 2017 21:02:15 +0000 (22:02 +0100)
There's an explicit `ctstreq' function which just does what you wanted.
Also, `ByteString' objects now have a rich-compare method which always
compares for equality in constant time.  Ordering comparisons are
variable time still.

There's a little chicanery to retain the hash function from `str'.

Also add a simple `check' method to `GHash' and `Poly1305Hash' which
compares a hsah or MAC tag in constant time and returns a boolean
result.

bytestring.c
catacomb-python.h
catacomb/__init__.py

index 7dcc4065238ffbc14c7d532afa8b76be5c5520c0..4abb260cabd6e72196d63be90b500ba50884af40 100644 (file)
@@ -61,6 +61,52 @@ static PyObject *bytestring_pynew(PyTypeObject *ty,
   return (dowrap(ty, p, n));
 }
 
+static PyObject *meth_ctstreq(PyObject *me, PyObject *args)
+{
+  char *p, *q;
+  Py_ssize_t psz, qsz;
+  if (!PyArg_ParseTuple(args, "s#s#:ctstreq", &p, &psz, &q, &qsz))
+    goto end;
+  if (psz == qsz && ct_memeq(p, q, psz)) RETURN_TRUE;
+  else RETURN_FALSE;
+end:
+  return (0);
+}
+
+static PyObject *bytestring_pyrichcompare(PyObject *me,
+                                         PyObject *you, int op)
+{
+  int b;
+  void *mystr, *yourstr;
+  Py_ssize_t mylen, yourlen, minlen;
+
+  if (!PyString_Check(me) || !PyString_Check(you)) RETURN_NOTIMPL;
+  mystr = PyString_AS_STRING(me); mylen = PyString_GET_SIZE(me);
+  yourstr = PyString_AS_STRING(you); yourlen = PyString_GET_SIZE(you);
+
+  switch (op) {
+    case Py_EQ:
+      b = mylen == yourlen && ct_memeq(mystr, yourstr, mylen);
+      break;
+    case Py_NE:
+      b = mylen != yourlen || !ct_memeq(mystr, yourstr, mylen);
+      break;
+    default:
+      minlen = mylen < yourlen ? mylen : yourlen;
+      b = memcmp(mystr, yourstr, minlen);
+      if (!b) b = mylen < yourlen ? -1 : mylen > yourlen ? +1 : 0;
+      switch (op) {
+       case Py_LT: b = b <  0; break;
+       case Py_LE: b = b <= 0; break;
+       case Py_GE: b = b >= 0; break;
+       case Py_GT: b = b >  0; break;
+       default: abort();
+      }
+  }
+  if (b) RETURN_TRUE;
+  else RETURN_FALSE;
+}
+
 #define BINOP(name, op)                                                        \
   static PyObject *bytestring_py##name(PyObject *x, PyObject *y) {     \
     const void *xv, *yv;                                               \
@@ -158,7 +204,7 @@ static PyTypeObject bytestring_pytype_skel = {
 
   0,                                   /* @tp_traverse@ */
   0,                                   /* @tp_clear@ */
-  0,                                   /* @tp_richcompare@ */
+  bytestring_pyrichcompare,            /* @tp_richcompare@ */
   0,                                   /* @tp_weaklistoffset@ */
   0,                                   /* @tp_iter@ */
   0,                                   /* @tp_iternext@ */
@@ -179,10 +225,18 @@ static PyTypeObject bytestring_pytype_skel = {
 
 /*----- Initialization ----------------------------------------------------*/
 
+static PyMethodDef methods[] = {
+#define METHNAME(func) meth_##func
+  METH  (ctstreq,              "ctstreq(S, T) -> BOOL")
+#undef METHNAME
+  { 0 }
+};
+
 #define string_pytype &PyString_Type
 void bytestring_pyinit(void)
 {
   INITTYPE(bytestring, string);
+  addmethods(methods);
 }
 
 void bytestring_pyinsert(PyObject *mod)
index 77c7eae4ab878c689ff309e75fc181bf7e52d2b2..f1e6365cc66d8b613033d56077d5e5cba4212b99 100644 (file)
@@ -46,6 +46,7 @@
 #include <mLib/unihash.h>
 
 #include <catacomb/buf.h>
+#include <catacomb/ct.h>
 
 #include <catacomb/grand.h>
 #include <catacomb/rand.h>
index 120ef111d967e3bc368daf9584a1ff7b285b06fd..6745bcee016c68df54016eafea601441a0aec7d3 100644 (file)
@@ -94,8 +94,19 @@ class _tmp:
   def __repr__(me):
     return 'bytes(%r)' % hex(me)
 _augment(ByteString, _tmp)
+ByteString.__hash__ = str.__hash__
 bytes = ByteString.fromhex
 
+###--------------------------------------------------------------------------
+### Hashing.
+
+class _tmp:
+  def check(me, h):
+    hh = me.done()
+    return ctstreq(h, hh)
+_augment(GHash, _tmp)
+_augment(Poly1305Hash, _tmp)
+
 ###--------------------------------------------------------------------------
 ### Multiprecision integers and binary polynomials.