+/* -*-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 -------------------------------------------------*/