"mac-tagsz=%lu", (unsigned long)algs->tagsz,
A_END);
}
+ if (algs->b) {
+ a_info(a,
+ "blkc=%.*s", strlen(algs->b->name) - 4, algs->b->name,
+ "blkc-keysz=%lu", (unsigned long)algs->bksz,
+ "blkc-blksz=%lu", (unsigned long)algs->b->blksz,
+ A_END);
+ }
a_ok(a);
}
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 }
const char *p;
const bulkcrypto *bulk;
char *q, *qq;
- dstr d = DSTR_INIT;
+ dstr d = DSTR_INIT, dd = DSTR_INIT;
int rc = -1;
/* --- Hash function --- */
}
}
+ /* --- Block cipher for miscellaneous use --- */
+
+ if (!(a->bulk->prim & BCP_BLKC))
+ a->b = 0;
+ else {
+ if ((p = key_getattr(kf, k, "blkc")) == 0) {
+ dstr_reset(&dd);
+ dstr_puts(&dd, a->c ? a->c->name : "rijndael-");
+ if ((q = strrchr(dd.buf, '-')) != 0) *q = 0;
+ p = dd.buf;
+ }
+ dstr_reset(&d);
+ dstr_putf(&d, "%s-ecb", p);
+ if ((a->b = gcipher_byname(d.buf)) == 0) {
+ a_format(e, "unknown-blkc", "%s", p, A_END);
+ goto done;
+ }
+ }
+
/* --- Message authentication for bulk data --- */
if (!(a->bulk->prim & BCP_MAC)) {
rc = 0;
done:
dstr_destroy(&d);
+ dstr_destroy(&dd);
return (rc);
}
A_END);
return (-1);
}
+ if (a->b && (a->bksz = keysz(a->hashsz, a->b->keysz)) == 0) {
+ a_format(e, "blkc", "%.*s", strlen(a->b->name) - 4, a->b->name,
+ "no-key-size", "%lu", (unsigned long)a->hashsz,
+ A_END);
+ return (-1);
+ }
/* --- Derive the data limit --- */
{
const algswitch *a = &kdx->algs, *aa = &kdy->algs;
- return (group_samep(kdx->g, kdy->g) && a->c == aa->c &&
+ return (group_samep(kdx->g, kdy->g) &&
+ a->c == aa->c && a->b == aa->b &&
a->mgf == aa->mgf && a->h == aa->h &&
a->m == aa->m && a->tagsz == aa->tagsz);
}
SETKEY("encryption", c, GC_INIT);
SETKEY("integrity", m, GM_KEY);
+ SETKEY("blkc", b, GC_INIT);
#undef SETKEY
}
AT_KEYWORDS([comm])
export TRIPE_SLIPIF=USLIP
-for p in alice bob; do (mkdir $p; cd $p; SETUPDIR([alpha])); done
-
-WITH_2TRIPES([alice], [bob], [-nslip], [-talice], [-tbob], [
- ESTABLISH([alice], [not-alice], [-key alice],
- [bob], [bob], [])
-])
+for k in alpha beta-new; do
+ for p in alice bob; do (
+ rm -rf $p; mkdir $p; cd $p; SETUPDIR([$k])
+ ); done
+ WITH_2TRIPES([alice], [bob], [-nslip], [-talice], [-tbob], [
+ ESTABLISH([alice], [not-alice], [-key alice],
+ [bob], [bob], [])
+ ])
+done
AT_CLEANUP
AT_DATA([algs-beta-new], [dnl
kx-group=ec kx-group-order-bits=161 kx-group-elt-bits=320
hash=rmd160 mgf=rmd160-mgf hash-sz=20
-bulk-transform=v0 bulk-overhead=22
+bulk-transform=iiv bulk-overhead=14
cipher=blowfish-cbc cipher-keysz=20 cipher-blksz=8
cipher-data-limit=67108864
mac=rmd160-hmac mac-keysz=20 mac-tagsz=10
+blkc=blowfish blkc-keysz=20 blkc-blksz=8
])
cp algs-alpha expout; AT_CHECK([TRIPECTL -dalice ALGS],, [expout])
.TP
.B mac-tagsz
The length of the message authentication tag, in octets.
+.TP
+.B blkc
+The block cipher in use, e.g.,
+.BR blowfish .
+.TP
+.B blkc-keysz
+The length of key used by the block cipher, in octets.
+.TP
+.B blkc-blksz
+The block size of the block cipher.
.PP
The various sizes are useful, for example, when computing the MTU for a
tunnel interface. If
overridden by setting attributes on your private key, as follows.
.TP
.B bulk
-Names the bulk-crypto transform to use. Currently the only choice is
-.BR v0 .
+Names the bulk-crypto transform to use. See below.
+.TP
+.B blkc
+Names a block cipher, used by some bulk-crypto transforms (e.g.,
+.BR iiv ). The default is to use the block cipher underlying the chosen
+.BR cipher ,
+if any.
.TP
.B cipher
Names the symmetric encryption scheme to use. The default is
A `mask-generation function', used in the key-exchange. The default is
.IB hash \-mgf
and there's no good reason to change it.
+.PP
+The available bulk-crypto transforms are as follows.
+.TP
+.B v0
+Originally this was the only transform available. It's a standard
+generic composition of a CPA-secure symmetric encryption scheme with a
+MAC; initialization vectors for symmetric encryption are chosen at
+random and included explicitly in the cryptogram.
+.TP
+.B iiv
+A newer `implicit-IV' transform. Rather than having an explicit random
+IV, the IV is computed from the sequence number using a block cipher.
+This has two advantages over the
+.B v0
+transform. Firstly, it adds less overhead to encrypted messages
+(because the IV no longer needs to be sent explicitly). Secondly, and
+more significantly, the transform is entirely deterministic, so (a) it
+doesn't need the (possibly slow) random number generator, and (b) it
+closes a kleptographic channel, over which a compromised implementation
+could leak secret information to a third party.
.SS "Using SLIP interfaces"
Though not for the faint of heart, it is possible to get
.B tripe
#define BCP_CIPHER 1
#define BCP_MAC 2
+#define BCP_BLKC 4
struct algswitch {
const gchash *h; /* Hash function */
const struct bulkcrypto *bulk; /* Bulk crypto transformation */
const gccipher *c; /* Symmetric encryption scheme */
const gcmac *m; /* Message authentication code */
+ const gccipher *b; /* Block cipher */
size_t hashsz; /* Hash output size */
size_t tagsz; /* Length to truncate MAC tags */
size_t expsz; /* Size of data to process */
- size_t cksz, mksz; /* Key lengths for @c@ and @m@ */
+ size_t cksz, mksz, bksz; /* Key lengths for things */
};
typedef struct kdata {
struct ksdir {
gcipher *c; /* Keyset cipher for encryption */
gmac *m; /* Keyset MAC for integrity */
+ gcipher *b; /* Block cipher, just in case */
} in, out;
uint32 oseq; /* Outbound sequence number */
seqwin iseq; /* Inbound sequence number */
-000f1070:tripe-ec-param struct:[curve=string,shared:secp160r1] forever forever -
-63803f04:tripe-ec:carol struct:[p=ec,public:0x42a11d34a1e19a69222a67c6ec29ff1d421cb021,0xc7d35a6dc9c330a09ee8e30680397b8bf51c29de,private=struct:[x=integer,private,burn:964893088925854502228477969935127891578649410999],curve=string,shared:secp160r1] forever forever -
-4045911b:tripe-ec:bob struct:[p=ec,public:0xcfc0b74fe1c4f30ced726bb70fbe4592ed8456ba,0x351d4dcbc200ccd3f3b6f4ecc112ecbf2043402d,private=struct:[x=integer,private,burn:333869955870933965311262832506583263321304092611],curve=string,shared:secp160r1] forever forever -
-e1a55f0e:tripe-ec:alice struct:[p=ec,public:0xce3bdc518066042b19083e2d27b0ded1915cb6b9,0xadef0e2006072f7bf038ef608e039a47e5767070,private=struct:[x=integer,private,burn:184161721501402669384082492238940360070520035996],curve=string,shared:secp160r1] forever forever -
+000f1070:tripe-ec-param struct:[curve=string,shared:secp160r1] forever forever bulk=iiv
+63803f04:tripe-ec:carol struct:[p=ec,public:0x42a11d34a1e19a69222a67c6ec29ff1d421cb021,0xc7d35a6dc9c330a09ee8e30680397b8bf51c29de,private=struct:[x=integer,private,burn:964893088925854502228477969935127891578649410999],curve=string,shared:secp160r1] forever forever bulk=iiv
+4045911b:tripe-ec:bob struct:[p=ec,public:0xcfc0b74fe1c4f30ced726bb70fbe4592ed8456ba,0x351d4dcbc200ccd3f3b6f4ecc112ecbf2043402d,private=struct:[x=integer,private,burn:333869955870933965311262832506583263321304092611],curve=string,shared:secp160r1] forever forever bulk=iiv
+e1a55f0e:tripe-ec:alice struct:[p=ec,public:0xce3bdc518066042b19083e2d27b0ded1915cb6b9,0xadef0e2006072f7bf038ef608e039a47e5767070,private=struct:[x=integer,private,burn:184161721501402669384082492238940360070520035996],curve=string,shared:secp160r1] forever forever bulk=iiv