X-Git-Url: http://www.chiark.greenend.org.uk/ucgi/~mdw/git/tripe/blobdiff_plain/410c8acf139e945dce28bbc0c8b17dcfd0815643..aa7f0bd9fd354398adddcf7f20c3715a058fcc6a:/keyset.c diff --git a/keyset.c b/keyset.c index 95c25490..4dd2c596 100644 --- a/keyset.c +++ b/keyset.c @@ -1,6 +1,6 @@ /* -*-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 * @@ -29,6 +29,9 @@ /*----- 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. * @@ -156,6 +159,8 @@ time_t ks_gen(keyset **ksroot, const void *k, size_t sz) 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); @@ -181,8 +186,9 @@ int ks_encrypt(keyset **ksroot, buf *b, buf *bb) 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; @@ -200,29 +206,32 @@ int ks_encrypt(keyset **ksroot, buf *b, buf *bb) 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 --- */ @@ -234,7 +243,7 @@ int ks_encrypt(keyset **ksroot, buf *b, buf *bb) 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); @@ -254,15 +263,22 @@ int ks_encrypt(keyset **ksroot, buf *b, buf *bb) 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; @@ -270,39 +286,72 @@ int ks_decrypt(keyset **ksroot, buf *b, buf *bb) 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 -------------------------------------------------*/