/* -*-c-*-
*
- * $Id: keyset.c,v 1.3 2001/02/16 21:39:55 mdw Exp $
+ * $Id: keyset.c,v 1.5 2001/06/19 22:07:43 mdw Exp $
*
* Handling of symmetric keysets
*
/*----- Revision history --------------------------------------------------*
*
* $Log: keyset.c,v $
+ * Revision 1.5 2001/06/19 22:07:43 mdw
+ * Change the encrypted packet format to be non-malleable.
+ *
+ * Revision 1.4 2001/06/16 14:06:40 mdw
+ * Quantify collision probabilities for the stated data volume bounds.
+ *
* Revision 1.3 2001/02/16 21:39:55 mdw
* Major overhaul. Separate functions for manipulating keysets from
* functions for manipulating keyset lists. Introduce a concept of
/*----- Tunable parameters ------------------------------------------------*/
+/* --- Note on size limits --- *
+ *
+ * For a 64-bit block cipher (e.g., Blowfish), the probability of a collision
+ * occurring after 32 MB is less than %$2^{-21}$%, and the probability of a
+ * collision occurring after 64 MB is less than %$2^{-19}$%.
+ */
+
#define T_EXP MIN(60) /* Expiry time for a key */
#define T_REGEN MIN(45) /* Regeneration time for a key */
#define SZ_EXP MEG(64) /* Expiry data size for a key */
/*----- Low-level packet encryption and decryption ------------------------*/
+/* --- Encrypted data format --- *
+ *
+ * Let %$p_i$% be the %$i$%-th plaintext message. We first compute
+ *
+ * %$c_i = \mathcal{E}\textrm{-CBC}_{K_{\text{E}}}(p_i)$%
+ *
+ * as the CBC-ciphertext of %$p_i$%, and then
+ *
+ * %$\sigma_i = \mathcal{T}_{K_{\text{M}}}(i, c_i)$%
+ *
+ * as a MAC on the %%\emph{ciphertext}%%. The message sent is then the pair
+ * %$(\sigma_i, c_i)$%. This construction is provably secure in the NM-CCA
+ * sense (assuming that the cipher is IND-CPA, and the MAC is SUF-CMA)
+ * [Bellare and Namprempre].
+ *
+ * This also ensures that, assuming the key is good, we have a secure channel
+ * [Krawczyk]. Actually, [Krawczyk] shows that, if the cipher is either a
+ * simple stream cipher or a block cipher in CBC mode, we can use the MAC-
+ * then-encrypt scheme and still have a secure channel. However, I like the
+ * NM-CCA guarantee from [Bellare and Namprempre]. I'm less worried about
+ * the Horton Principle [Wagner and Schneier].
+ */
+
/* --- @doencrypt@ --- *
*
* Arguments: @keyset *ks@ = pointer to keyset to use
{
ghash *h;
gcipher *c;
- size_t ivsz;
const octet *p = BCUR(b);
size_t sz = BLEFT(b);
- octet *qiv, *qseq, *qpk;
+ octet *qmac, *qseq, *qiv, *qpk;
uint32 oseq;
size_t osz, nsz;
int rc = 0;
/* --- Allocate the required buffer space --- */
c = ks->cout;
- ivsz = c->ops->c->blksz;
- if (buf_ensure(bb, ivsz + 4 + sz))
+ if (buf_ensure(bb, MACSZ + SEQSZ + IVSZ + sz))
return (0); /* Caution! */
- qiv = BCUR(bb); qseq = qiv + ivsz; qpk = qseq + 4;
- BSTEP(bb, ivsz + 4 + sz);
+ qmac = BCUR(bb); qseq = qmac + MACSZ; qiv = qseq + SEQSZ; qpk = qiv + IVSZ;
+ BSTEP(bb, MACSZ + SEQSZ + IVSZ + sz);
- /* --- MAC and encrypt the packet --- */
+ /* --- Encrypt the packet --- */
oseq = ks->oseq++; STORE32(qseq, oseq);
- h = ks->mout->ops->init(ks->mout);
- h->ops->hash(h, qseq, 4);
- h->ops->hash(h, p, sz);
- memcpy(qiv, h->ops->done(h, 0), ivsz);
- h->ops->destroy(h);
+ rand_get(RAND_GLOBAL, qiv, IVSZ);
+ c->ops->setiv(c, qiv);
+ c->ops->encrypt(c, p, qpk, sz);
IF_TRACING(T_KEYSET, {
trace(T_KEYSET, "keyset: encrypting packet %lu using keyset %u",
(unsigned long)oseq, ks->seq);
- trace_block(T_CRYPTO, "crypto: computed MAC", qiv, ivsz);
+ trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
})
- c->ops->setiv(c, qiv);
- c->ops->encrypt(c, p, qpk, sz);
+
+ /* --- Now compute the MAC --- */
+
+ h = ks->mout->ops->init(ks->mout);
+ h->ops->hash(h, qseq, SEQSZ + IVSZ + sz);
+ memcpy(qmac, h->ops->done(h, 0), MACSZ);
+ h->ops->destroy(h);
IF_TRACING(T_KEYSET, {
- trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
+ trace_block(T_CRYPTO, "crypto: computed MAC", qmac, MACSZ);
})
/* --- Deduct the packet size from the key's data life --- */
static int dodecrypt(keyset *ks, buf *b, buf *bb, uint32 *seq)
{
- const octet *piv, *pseq, *ppk;
+ const octet *pmac, *piv, *pseq, *ppk;
size_t psz = BLEFT(b);
size_t sz;
octet *q = BCUR(bb);
T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
return (-1);
}
- sz = psz - ivsz - 4;
- piv = BCUR(b); pseq = piv + ivsz; ppk = pseq + 4;
+ sz = psz - IVSZ - SEQSZ - MACSZ;
+ pmac = BCUR(b); pseq = pmac + MACSZ; piv = pseq + SEQSZ; ppk = piv + IVSZ;
- /* --- Attempt to decrypt the packet --- */
+ /* --- Verify the MAC on the packet --- */
- c->ops->setiv(c, piv);
- c->ops->decrypt(c, ppk, q, sz);
h = ks->min->ops->init(ks->min);
- h->ops->hash(h, pseq, 4);
- h->ops->hash(h, q, sz);
+ h->ops->hash(h, pseq, SEQSZ + IVSZ + sz);
mac = h->ops->done(h, 0);
- eq = !memcmp(mac, piv, ivsz);
+ eq = !memcmp(mac, pmac, MACSZ);
IF_TRACING(T_KEYSET, {
trace(T_KEYSET, "keyset: decrypting using keyset %u", ks->seq);
- trace_block(T_CRYPTO, "crypto: computed MAC", mac, ivsz);
+ trace_block(T_CRYPTO, "crypto: computed MAC", mac, MACSZ);
})
h->ops->destroy(h);
if (!eq) {
IF_TRACING(T_KEYSET, {
trace(T_KEYSET, "keyset: decryption failed");
- trace_block(T_CRYPTO, "crypto: expected MAC", piv, ivsz);
- trace_block(T_CRYPTO, "crypto: invalid packet", q, sz);
+ trace_block(T_CRYPTO, "crypto: expected MAC", pmac, MACSZ);
})
return (-1);
}
+
+ /* --- Decrypt the packet --- */
+
+ c->ops->setiv(c, piv);
+ c->ops->decrypt(c, ppk, q, sz);
if (seq)
*seq = LOAD32(pseq);
IF_TRACING(T_KEYSET, {