/* -*-c-*-
*
- * $Id: keyset.c,v 1.1 2001/02/03 20:26:37 mdw Exp $
+ * $Id: keyset.c,v 1.2 2001/02/05 19:53:23 mdw Exp $
*
* Handling of symmetric keysets
*
/*----- Revision history --------------------------------------------------*
*
* $Log: keyset.c,v $
+ * Revision 1.2 2001/02/05 19:53:23 mdw
+ * Add sequence number protection.
+ *
* Revision 1.1 2001/02/03 20:26:37 mdw
* Initial checkin.
*
T( ks->seq = seq++; )
ks->t_exp = now + KEY_EXPTIME;
ks->sz_exp = KEY_EXPSZ;
+ ks->oseq = ks->iseq = 0;
+ ks->iwin = 0;
ks->next = *ksroot;
*ksroot = ks;
BURN(buf);
gcipher *c;
size_t ivsz;
const octet *p = BCUR(b);
- octet *q = BCUR(bb);
size_t sz = BLEFT(b);
+ octet *qiv, *qseq, *qpk;
+ uint32 oseq;
size_t osz, nsz;
int rc = 0;
ks = ks->next;
}
- /* --- MAC and encrypt the packet --- */
+ /* --- Allocate the required buffer space --- */
c = ks->c;
ivsz = c->ops->c->blksz;
- if (buf_ensure(bb, ivsz + sz))
- return (0);
+ if (buf_ensure(bb, ivsz + 4 + sz)) return (0);
+ qiv = BCUR(bb); qseq = qiv + ivsz; qpk = qseq + 4;
+ BSTEP(bb, ivsz + 4 + sz);
+
+ /* --- MAC and encrypt the packet --- */
+
+ oseq = ks->oseq++; STORE32(qseq, oseq);
h = ks->m->ops->init(ks->m);
+ h->ops->hash(h, qseq, 4);
h->ops->hash(h, p, sz);
- h->ops->done(h, q);
+ memcpy(qiv, h->ops->done(h, 0), ivsz);
+ h->ops->destroy(h);
IF_TRACING(T_KEYSET, {
- trace(T_KEYSET, "keyset: encrypting using keyset %u", ks->seq);
- trace_block(T_CRYPTO, "crypto: computed MAC", q, ivsz);
+ trace(T_KEYSET, "keyset: encrypting packet %lu using keyset %u",
+ (unsigned long)oseq, ks->seq);
+ trace_block(T_CRYPTO, "crypto: computed MAC", qiv, ivsz);
})
- c->ops->setiv(c, q);
- h->ops->destroy(h);
-
- if (buf_ensure(bb, sz))
- return (0);
- c->ops->encrypt(c, p, q + ivsz, sz);
+ c->ops->setiv(c, qiv);
+ c->ops->encrypt(c, p, qpk, sz);
IF_TRACING(T_KEYSET, {
- trace_block(T_CRYPTO, "crypto: encrypted packet", q + ivsz, sz);
+ trace_block(T_CRYPTO, "crypto: encrypted packet", qpk, sz);
})
- BSTEP(bb, ivsz + sz);
/* --- Deduct the packet size from the key's data life --- */
if (osz >= KEY_REGENSZ && nsz < KEY_REGENSZ) {
T( trace(T_KEYSET, "keyset: keyset %u data regen limit exceeded -- "
"forcing exchange", ks->seq); )
- rc = -1;
+ rc = 1;
}
ks->sz_exp = nsz;
return (rc);
int ks_decrypt(keyset **ksroot, buf *b, buf *bb)
{
time_t now = time(0);
- const octet *pp = BCUR(b);
- const octet *p;
- size_t sz = BLEFT(b);
+ const octet *piv, *pseq, *ppk;
+ size_t psz = BLEFT(b);
+ size_t sz;
octet *q = BCUR(bb);
+ uint32 iseq;
+ unsigned seqbit;
keyset *ks;
+ /* --- Allocate space in the output buffer --- */
+
T( trace(T_KEYSET, "keyset: attempting to decrypt packet"); )
- if (buf_ensure(bb, sz))
+ if (buf_ensure(bb, psz))
return (-1);
+
+ /* --- Try all of the valid keys --- */
+
for (ks = *ksroot; ks; ks = ks->next) {
ghash *h;
gcipher *c = ks->c;
octet *mac;
int eq;
+ /* --- Break up the packet into its components --- */
+
if (!KEYOK(ks, now))
continue;
- if (sz < ivsz) {
+ if (psz < ivsz + 4) {
T( trace(T_KEYSET, "keyset: block too small for keyset %u", ks->seq); )
continue;
}
- p = pp + ivsz;
- c->ops->setiv(c, pp);
- c->ops->decrypt(c, p, q, sz - ivsz);
+ sz = psz - ivsz - 4;
+ piv = BCUR(b); pseq = piv + ivsz; ppk = pseq + 4;
+
+ /* --- Attempt to decrypt the packet --- */
+
+ c->ops->setiv(c, piv);
+ c->ops->decrypt(c, ppk, q, sz);
h = ks->m->ops->init(ks->m);
- h->ops->hash(h, q, sz - ivsz);
+ h->ops->hash(h, pseq, 4);
+ h->ops->hash(h, q, sz);
mac = h->ops->done(h, 0);
- eq = !memcmp(mac, pp, ivsz);
+ eq = !memcmp(mac, piv, ivsz);
IF_TRACING(T_KEYSET, {
trace(T_KEYSET, "keyset: decrypting using keyset %u", ks->seq);
trace_block(T_CRYPTO, "crypto: computed MAC", mac, ivsz);
})
h->ops->destroy(h);
if (eq) {
- BSTEP(bb, sz - ivsz);
- IF_TRACING(T_KEYSET, {
- trace(T_KEYSET, "keyset: decrypted OK");
- trace_block(T_CRYPTO, "crypto: decrypted packet", q, sz - ivsz);
- })
- return (0);
+ BSTEP(bb, sz);
+ goto match;
}
IF_TRACING(T_KEYSET, {
trace(T_KEYSET, "keyset: decryption failed");
- trace_block(T_CRYPTO, "crypto: expected MAC", pp, ivsz);
+ trace_block(T_CRYPTO, "crypto: expected MAC", piv, ivsz);
+ trace_block(T_CRYPTO, "crypto: invalid packet", q, sz);
})
}
T( trace(T_KEYSET, "keyset: no matching keys"); )
return (-1);
+
+ /* --- We've found a match, so check the sequence number --- */
+
+match:
+ iseq = LOAD32(pseq);
+ IF_TRACING(T_KEYSET, {
+ trace(T_KEYSET, "keyset: decrypted OK (sequence = %lu)",
+ (unsigned long)iseq);
+ trace_block(T_CRYPTO, "crypto: decrypted packet", q, sz);
+ })
+ if (iseq < ks->iseq) {
+ a_warn("received packet has old sequence number (possible replay)");
+ return (-1);
+ }
+ if (iseq >= ks->iseq + KS_SEQWINSZ) {
+ uint32 n = iseq - (ks->iseq + KS_SEQWINSZ - 1);
+ if (n < KS_SEQWINSZ)
+ ks->iwin >>= n;
+ else
+ ks->iwin = 0;
+ ks->iseq += n;
+ }
+ seqbit = 1 << (iseq - ks->iseq);
+ if (ks->iwin & seqbit) {
+ a_warn("received packet repeats old sequence number");
+ return (-1);
+ }
+ ks->iwin |= seqbit;
+ return (0);
}
/*----- That's all, folks -------------------------------------------------*/