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[] = {
{ 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 }