chiark / gitweb /
uslip/uslip.c: Be consistent about `VERB_NOUN' function naming.
[tripe] / server / bulkcrypto.c
index faaf8d71b7889536ba77cf57a8051f84a3f2e0fc..144a8301068e9f2811249bb09f5368ab88edf4bd 100644 (file)
@@ -199,6 +199,163 @@ static int v0_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq)
   return (0);
 }
 
+/*----- The implicit-IV transform -----------------------------------------*
+ *
+ * The v0 transform makes everything explicit.  There's an IV because the
+ * cipher needs an IV; there's a sequence number because replay prevention
+ * needs a sequence number.
+ *
+ * This new transform works rather differently.  We make use of a block
+ * cipher to encrypt the sequence number, and use that as the IV.  We
+ * transmit the sequence number in the clear, as before.  This reduces
+ * overhead; and it's not a significant privacy leak because the adversary
+ * can see the order in which the messages are transmitted -- i.e., the
+ * sequence numbers are almost completely predictable anyway.
+ *
+ * So, a MAC is computed over
+ *
+ *             +------+ +------+------...------+
+ *             | type | | seq  |   ciphertext  |
+ *             +------+ +------+------...------+
+ *                32       32         sz
+ *
+ * and we actually transmit the following as the cryptogram.
+ *
+ *             +---...---+------+------...------+
+ *             |   tag   | seq  |   ciphertext  |
+ *             +---...---+------+------...------+
+ *                tagsz     32         sz
+ */
+
+static int iiv_check(const algswitch *a, dstr *e)
+{
+  if (a->b->blksz < a->c->blksz) {
+    a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name,
+            "blksz-insufficient", A_END);
+    return (-1);
+  }
+  return (0);
+}
+
+static size_t iiv_overhead(const algswitch *a)
+  { return a->tagsz + SEQSZ; }
+
+#define TRACE_PRESEQ(qseq, ivsz) do { IF_TRACING(T_KEYSET, {           \
+  trace_block(T_CRYPTO, "crypto: IV derivation input", (qseq), (ivsz));        \
+}) } while (0)
+
+static int iiv_encrypt(keyset *ks, unsigned ty, buf *b, buf *bb)
+{
+  ghash *h;
+  gcipher *c = ks->out.c, *blkc = ks->out.b;
+  const octet *p = BCUR(b);
+  size_t sz = BLEFT(b);
+  octet *qmac, *qseq, *qpk;
+  uint32 oseq;
+  size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz;
+  size_t tagsz = ks->tagsz;
+  octet t[4];
+
+  /* --- Determine the ciphertext layout --- */
+
+  if (buf_ensure(bb, tagsz + SEQSZ + sz)) return (0);
+  qmac = BCUR(bb); qseq = qmac + tagsz; qpk = qseq + SEQSZ;
+  BSTEP(bb, tagsz + SEQSZ + 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) {
+    memset(buf_u, 0, blkcsz - SEQSZ);
+    memcpy(buf_u + blkcsz - SEQSZ, qseq, SEQSZ);
+    TRACE_PRESEQ(buf_u, ivsz);
+    GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz);
+    GC_SETIV(c, buf_u);
+    TRACE_IV(buf_u, ivsz);
+  }
+
+  /* --- Encrypt the packet --- */
+
+  GC_ENCRYPT(c, p, qpk, sz);
+  TRACE_CT(qpk, sz);
+
+  /* --- Compute a MAC over type, sequence number, and ciphertext --- */
+
+  if (tagsz) {
+    h = GM_INIT(ks->out.m);
+    GH_HASH(h, t, sizeof(t));
+    GH_HASH(h, qseq, SEQSZ + sz);
+    memcpy(qmac, GH_DONE(h, 0), tagsz);
+    GH_DESTROY(h);
+    TRACE_MAC(qmac, tagsz);
+  }
+
+  /* --- We're done --- */
+
+  return (0);
+}
+
+static int iiv_decrypt(keyset *ks, unsigned ty, buf *b, buf *bb, uint32 *seq)
+{
+  const octet *pmac, *pseq, *ppk;
+  size_t psz = BLEFT(b);
+  size_t sz;
+  octet *q = BCUR(bb);
+  ghash *h;
+  gcipher *c = ks->in.c, *blkc = ks->in.b;
+  size_t ivsz = GC_CLASS(c)->blksz, blkcsz = GC_CLASS(blkc)->blksz;
+  size_t tagsz = ks->tagsz;
+  octet t[4];
+
+  /* --- Break up the packet into its components --- */
+
+  if (psz < SEQSZ + tagsz) {
+    T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
+    return (KSERR_MALFORMED);
+  }
+  sz = psz - SEQSZ - tagsz;
+  pmac = BCUR(b); pseq = pmac + tagsz; ppk = pseq + SEQSZ;
+  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 + sz);
+    CHECK_MAC(h, pmac, tagsz);
+  }
+
+  /* --- Decrypt the packet --- */
+
+  if (ivsz) {
+    memset(buf_u, 0, blkcsz - SEQSZ);
+    memcpy(buf_u + blkcsz - SEQSZ, pseq, SEQSZ);
+    TRACE_PRESEQ(buf_u, ivsz);
+    GC_ENCRYPT(blkc, buf_u, buf_u, blkcsz);
+    GC_SETIV(c, buf_u);
+    TRACE_IV(buf_u, ivsz);
+  }
+  GC_DECRYPT(c, ppk, q, sz);
+
+  /* --- Finished --- */
+
+  *seq = LOAD32(pseq);
+  BSTEP(bb, sz);
+  return (0);
+}
+
 /*----- Bulk crypto transform table ---------------------------------------*/
 
 const bulkcrypto bulktab[] = {
@@ -207,6 +364,7 @@ const bulkcrypto bulktab[] = {
   { name, prim, pre##_check, pre##_overhead, pre##_encrypt, pre##_decrypt }
 
   BULK("v0", v0, BCP_CIPHER | BCP_MAC),
+  BULK("iiv", iiv, BCP_CIPHER | BCP_MAC | BCP_BLKC),
 
 #undef BULK
   { 0 }