+typedef struct gencomp_algs {
+ const gccipher *c; size_t cksz;
+ const gcmac *m; size_t mksz; size_t tagsz;
+} gencomp_algs;
+
+typedef struct gencomp_chal {
+ bulkchal _b;
+ gmac *m; size_t tagsz;
+} gencomp_chal;
+
+static int gencomp_getalgs(gencomp_algs *a, const algswitch *asw,
+ dstr *e, key_file *kf, key *k)
+{
+ const char *p;
+ char *q, *qq;
+ unsigned long n;
+ dstr d = DSTR_INIT;
+ int rc = -1;
+
+ /* --- Symmetric encryption --- */
+
+ if ((p = key_getattr(kf, k, "cipher")) == 0) p = "blowfish-cbc";
+ if ((a->c = gcipher_byname(p)) == 0) {
+ a_format(e, "unknown-cipher", "%s", p, A_END);
+ goto done;
+ }
+
+ /* --- Message authentication --- */
+
+ if ((p = key_getattr(kf, k, "mac")) != 0) {
+ dstr_reset(&d);
+ dstr_puts(&d, p);
+ if ((q = strchr(d.buf, '/')) != 0)
+ *q++ = 0;
+ if ((a->m = gmac_byname(d.buf)) == 0) {
+ a_format(e, "unknown-mac", "%s", d.buf, A_END);
+ goto done;
+ }
+ if (!q)
+ a->tagsz = a->m->hashsz;
+ else {
+ n = strtoul(q, &qq, 0);
+ if (*qq) {
+ a_format(e, "bad-tag-length-string", "%s", q, A_END);
+ goto done;
+ }
+ if (n%8 || n/8 > a->m->hashsz) {
+ a_format(e, "bad-tag-length", "%lu", n, A_END);
+ goto done;
+ }
+ a->tagsz = n/8;
+ }
+ } else {
+ dstr_reset(&d);
+ dstr_putf(&d, "%s-hmac", asw->h->name);
+ if ((a->m = gmac_byname(d.buf)) == 0) {
+ a_format(e, "no-hmac-for-hash", "%s", asw->h->name, A_END);
+ goto done;
+ }
+ a->tagsz = asw->h->hashsz/2;
+ }
+
+ rc = 0;
+done:
+ dstr_destroy(&d);
+ return (rc);
+}
+
+#ifndef NTRACE
+static void gencomp_tracealgs(const gencomp_algs *a)
+{
+ trace(T_CRYPTO, "crypto: cipher = %s", a->c->name);
+ trace(T_CRYPTO, "crypto: mac = %s/%lu",
+ a->m->name, (unsigned long)a->tagsz * 8);
+}
+#endif
+
+static int gencomp_checkalgs(gencomp_algs *a, const algswitch *asw, dstr *e)
+{
+ /* --- Derive the key sizes --- *
+ *
+ * Must ensure that we have non-empty keys. This isn't ideal, but it
+ * provides a handy sanity check. Also must be based on a 64- or 128-bit
+ * block cipher or we can't do the data expiry properly.
+ */
+
+ if ((a->cksz = keysz(asw->hashsz, a->c->keysz)) == 0) {
+ a_format(e, "cipher", "%s", a->c->name,
+ "no-key-size", "%lu", (unsigned long)asw->hashsz,
+ A_END);
+ return (-1);
+ }
+ if ((a->mksz = keysz(asw->hashsz, a->m->keysz)) == 0) {
+ a_format(e, "mac", "%s", a->m->name,
+ "no-key-size", "%lu", (unsigned long)asw->hashsz,
+ A_END);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static void gencomp_alginfo(const gencomp_algs *a, admin *adm)
+{
+ a_info(adm,
+ "cipher=%s", a->c->name,
+ "cipher-keysz=%lu", (unsigned long)a->cksz,
+ "cipher-blksz=%lu", (unsigned long)a->c->blksz,
+ A_END);
+ a_info(adm,
+ "mac=%s", a->m->name,
+ "mac-keysz=%lu", (unsigned long)a->mksz,
+ "mac-tagsz=%lu", (unsigned long)a->tagsz,
+ A_END);
+}
+
+static int gencomp_samealgsp(const gencomp_algs *a, const gencomp_algs *aa)
+{
+ return (a->c == aa->c &&
+ a->m == aa->m && a->tagsz == aa->tagsz);
+}
+
+static size_t gencomp_expsz(const gencomp_algs *a)
+ { return (a->c->blksz < 16 ? MEG(64) : MEG(2048)); }
+
+static bulkchal *gencomp_genchal(const gencomp_algs *a)
+{
+ gencomp_chal *gc = CREATE(gencomp_chal);
+
+ rand_get(RAND_GLOBAL, buf_t, a->mksz);
+ gc->m = GM_KEY(a->m, buf_t, a->mksz);
+ gc->_b.tagsz = a->tagsz;
+ IF_TRACING(T_CHAL, {
+ trace(T_CHAL, "chal: generated new challenge key");
+ trace_block(T_CRYPTO, "chal: new key", buf_t, a->mksz);
+ })
+ return (&gc->_b);
+}
+
+static int gencomp_chaltag(bulkchal *bc, const void *m, size_t msz, void *t)
+{
+ gencomp_chal *gc = (gencomp_chal *)bc;
+ ghash *h = GM_INIT(gc->m);
+
+ GH_HASH(h, m, msz);
+ memcpy(t, GH_DONE(h, 0), bc->tagsz);
+ GH_DESTROY(h);
+ return (0);
+}
+
+static int gencomp_chalvrf(bulkchal *bc, const void *m, size_t msz,
+ const void *t)
+{
+ gencomp_chal *gc = (gencomp_chal *)bc;
+ ghash *h = GM_INIT(gc->m);
+ int ok;
+
+ GH_HASH(h, m, msz);
+ ok = ct_memeq(GH_DONE(h, 0), t, gc->_b.tagsz);
+ GH_DESTROY(h);
+ return (ok ? 0 : -1);
+}
+
+static void gencomp_freechal(bulkchal *bc)
+ { gencomp_chal *gc = (gencomp_chal *)bc; GM_DESTROY(gc->m); DESTROY(gc); }
+