X-Git-Url: https://git.distorted.org.uk/~mdw/tripe/blobdiff_plain/a93aacce200e0d68b614d8bfb05d9cbeba850b12..5290b9d5791238de2e19ec479087bda6e3c787e2:/server/bulkcrypto.c diff --git a/server/bulkcrypto.c b/server/bulkcrypto.c index faaf8d71..144a8301 100644 --- a/server/bulkcrypto.c +++ b/server/bulkcrypto.c @@ -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 }