chiark / gitweb /
server/: Prepare an interface for multiple bulk-crypto transforms.
[tripe] / server / bulkcrypto.c
diff --git a/server/bulkcrypto.c b/server/bulkcrypto.c
new file mode 100644 (file)
index 0000000..faaf8d7
--- /dev/null
@@ -0,0 +1,215 @@
+/* -*-c-*-
+ *
+ * Bulk crypto transformations
+ *
+ * (c) 2014 Straylight/Edgeware
+ */
+
+/*----- Licensing notice --------------------------------------------------*
+ *
+ * This file is part of Trivial IP Encryption (TrIPE).
+ *
+ * TrIPE is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * TrIPE 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with TrIPE; if not, write to the Free Software Foundation,
+ * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ */
+
+/*----- Header files ------------------------------------------------------*/
+
+#include "tripe.h"
+
+/*----- Utilities ---------------------------------------------------------*/
+
+#define SEQSZ 4                                /* Size of sequence number packet */
+
+#define TRACE_IV(qiv, ivsz) do { IF_TRACING(T_KEYSET, {                        \
+  trace_block(T_CRYPTO, "crypto: initialization vector",               \
+             (qiv), (ivsz));                                           \
+}) } while (0)
+
+#define TRACE_CT(qpk, sz) do { IF_TRACING(T_KEYSET, {                  \
+  trace_block(T_CRYPTO, "crypto: encrypted packet", (qpk), (sz));      \
+}) } while (0)
+
+#define TRACE_MAC(qmac, tagsz) do { IF_TRACING(T_KEYSET, {             \
+  trace_block(T_CRYPTO, "crypto: computed MAC", (qmac), (tagsz));      \
+}) } while (0)
+
+#define CHECK_MAC(h, pmac, tagsz) do {                                 \
+  ghash *_h = (h);                                                     \
+  const octet *_pmac = (pmac);                                         \
+  size_t _tagsz = (tagsz);                                             \
+  octet *_mac = GH_DONE(_h, 0);                                                \
+  int _eq = ct_memeq(_mac, _pmac, _tagsz);                             \
+  TRACE_MAC(_mac, _tagsz);                                             \
+  GH_DESTROY(_h);                                                      \
+  if (!_eq) {                                                          \
+    IF_TRACING(T_KEYSET, {                                             \
+      trace(T_KEYSET, "keyset: incorrect MAC: decryption failed");     \
+      trace_block(T_CRYPTO, "crypto: expected MAC", _pmac, _tagsz);    \
+    })                                                                 \
+    return (KSERR_DECRYPT);                                            \
+  }                                                                    \
+} while (0)
+
+/*----- The original transform --------------------------------------------*
+ *
+ * We generate a random initialization vector (if the cipher needs one).  We
+ * encrypt the input message with the cipher, and format the type, sequence
+ * number, IV, and ciphertext as follows.
+ *
+ *             +------+ +------+---...---+------...------+
+ *             | type | | seq  |   iv    |   ciphertext  |
+ *             +------+ +------+---...---+------...------+
+ *                32       32     blksz         sz
+ *
+ * All of this is fed into the MAC to compute a tag.  The type is not
+ * transmitted: the other end knows what type of message it expects, and the
+ * type is only here to prevent us from being confused because some other
+ * kind of ciphertext has been substituted.  The tag is prepended to the
+ * remainder, to yield the finished cryptogram, as follows.
+ *
+ *             +---...---+------+---...---+------...------+
+ *             |   tag   | seq  |   iv    |   ciphertext  |
+ *             +---...---+------+---...---+------...------+
+ *                tagsz     32     blksz         sz
+ *
+ * Decryption: checks the overall size, verifies the tag, then decrypts the
+ * ciphertext and extracts the sequence number.
+ */
+
+static int v0_check(const algswitch *a, dstr *e)
+  { return (0); }
+
+static size_t v0_overhead(const algswitch *a)
+  { return a->tagsz + SEQSZ + a->c->blksz; }
+
+static int v0_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb)
+{
+  ghash *h;
+  gcipher *c = ks->out.c;
+  const octet *p = BCUR(b);
+  size_t sz = BLEFT(b);
+  octet *qmac, *qseq, *qiv, *qpk;
+  uint32 oseq;
+  size_t ivsz = GC_CLASS(c)->blksz;
+  size_t tagsz = ks->tagsz;
+  octet t[4];
+
+  /* --- Determine the ciphertext layout --- */
+
+  if (buf_ensure(bb, tagsz + SEQSZ + ivsz + sz)) return (0);
+  qmac = BCUR(bb); qseq = qmac + tagsz; qiv = qseq + SEQSZ; qpk = qiv + ivsz;
+  BSTEP(bb, tagsz + SEQSZ + ivsz + sz);
+
+  /* --- Store the type --- *
+   *
+   * This isn't transmitted, but it's covered by the MAC.
+   */
+
+  STORE32(t, ty);
+
+  /* --- Store the sequence number --- */
+
+  oseq = ks->oseq++;
+  STORE32(qseq, oseq);
+
+  /* --- Establish an initialization vector if necessary --- */
+
+  if (ivsz) {
+    rand_get(RAND_GLOBAL, qiv, ivsz);
+    GC_SETIV(c, qiv);
+    TRACE_IV(qiv, ivsz);
+  }
+
+  /* --- Encrypt the packet --- */
+
+  GC_ENCRYPT(c, p, qpk, sz);
+  TRACE_CT(qpk, sz);
+
+  /* --- Compute a MAC over type, sequence number, IV, and ciphertext --- */
+
+  if (tagsz) {
+    h = GM_INIT(ks->out.m);
+    GH_HASH(h, t, sizeof(t));
+    GH_HASH(h, qseq, SEQSZ + ivsz + sz);
+    memcpy(qmac, GH_DONE(h, 0), tagsz);
+    GH_DESTROY(h);
+    TRACE_MAC(qmac, tagsz);
+  }
+
+  /* --- We're done --- */
+
+  return (0);
+}
+
+static int v0_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq)
+{
+  const octet *pmac, *piv, *pseq, *ppk;
+  size_t psz = BLEFT(b);
+  size_t sz;
+  octet *q = BCUR(bb);
+  ghash *h;
+  gcipher *c = ks->in.c;
+  size_t ivsz = GC_CLASS(c)->blksz;
+  size_t tagsz = ks->tagsz;
+  octet t[4];
+
+  /* --- Break up the packet into its components --- */
+
+  if (psz < ivsz + SEQSZ + tagsz) {
+    T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
+    return (KSERR_MALFORMED);
+  }
+  sz = psz - ivsz - SEQSZ - tagsz;
+  pmac = BCUR(b); pseq = pmac + tagsz; piv = pseq + SEQSZ; ppk = piv + ivsz;
+  STORE32(t, ty);
+
+  /* --- Verify the MAC on the packet --- */
+
+  if (tagsz) {
+    h = GM_INIT(ks->in.m);
+    GH_HASH(h, t, sizeof(t));
+    GH_HASH(h, pseq, SEQSZ + ivsz + sz);
+    CHECK_MAC(h, pmac, tagsz);
+  }
+
+  /* --- Decrypt the packet --- */
+
+  if (ivsz) {
+    TRACE_IV(piv, ivsz);
+    GC_SETIV(c, piv);
+  }
+  GC_DECRYPT(c, ppk, q, sz);
+
+  /* --- Finished --- */
+
+  *seq = LOAD32(pseq);
+  BSTEP(bb, sz);
+  return (0);
+}
+
+/*----- Bulk crypto transform table ---------------------------------------*/
+
+const bulkcrypto bulktab[] = {
+
+#define BULK(name, pre, prim)                                          \
+  { name, prim, pre##_check, pre##_overhead, pre##_encrypt, pre##_decrypt }
+
+  BULK("v0", v0, BCP_CIPHER | BCP_MAC),
+
+#undef BULK
+  { 0 }
+};
+
+/*----- That's all, folks -------------------------------------------------*/