From 7f5ea7d35faabd2ad2c93a42c57eb00d3f14e0aa Mon Sep 17 00:00:00 2001 From: Mark Wooding Date: Wed, 18 Jun 2014 21:52:35 +0100 Subject: [PATCH] udpkey.c: Refactor protocol reply logic. Also add a protocol summary comment. --- udpkey.c | 402 +++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 224 insertions(+), 178 deletions(-) diff --git a/udpkey.c b/udpkey.c index ca337cc..b3dca9b 100644 --- a/udpkey.c +++ b/udpkey.c @@ -469,6 +469,19 @@ static void debug_ge(const char *what, group *g, ge *X) } #endif +/*----- Protocol summary --------------------------------------------------* + * + * * Request + * memz KEYTAG tag of wanted secret + * ge U public vector + * + * * Response + * ge V public vector: V = v P + * ge W encrypted clue: W = R - Y = r P - v U + * mem[TAGSZ] TAG MAC tag on ciphertext + * mem[KSZ] CT secret, encrypted with Z = r X + */ + /*----- Listening for requests --------------------------------------------*/ /* Rate limiting parameters. @@ -482,34 +495,220 @@ static void debug_ge(const char *what, group *g, ge *X) #define RATE_REFILL 10 /* Credits per second. */ #define RATE_CREDIT 1000 /* Initial credit. */ +static time_t now; + +static int fetch_key(const char *tag, struct sockaddr_in *sin, + key **ky, struct kinfo *k) +{ + dstr d = DSTR_INIT, dd = DSTR_INIT; + key_data **kkd; + char *p, *q; + const char *pp; + struct in_addr in; + int ch, mlen, rc = -1; + + /* Find the key. */ + kfupdate(); + if (key_qtag(kf, tag, &d, ky, &kkd)) { + complain(LOG_WARNING, "unknown key tag `%s' from %s:%d", + tag, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + goto done; + } + + /* And make sure that it has the right shape. */ + if (((*ky)->k->e & KF_ENCMASK) != KENC_BINARY) { + complain(LOG_ERR, "key %s is not plain binary data", d.buf); + goto done; + } + + /* Find the list of clients, and look up the caller's address in the + * list. Entries have the form ADDRESS[/LEN][=TAG] and are separated by + * `;'. + */ + if ((pp = key_getattr(kf, *ky, "clients")) == 0) { + complain(LOG_WARNING, + "key %s requested from %s:%d has no `clients' attribute", + d.buf, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + goto done; + } + dstr_puts(&dd, pp); + p = dd.buf; + while (*p) { + q = p; + while (isdigit((unsigned char)*q) || *q == '.') q++; + ch = *q; *q++ = 0; + if (!inet_aton(p, &in)) goto skip; + if (ch != '/') + mlen = 32; + else { + p = q; + while (isdigit((unsigned char)*q)) q++; + ch = *q; *q++ = 0; + mlen = atoi(p); + } + if (((sin->sin_addr.s_addr ^ in.s_addr) & + htonl(0xffffffff << (32 - mlen))) == 0) + goto match; + skip: + if (!ch) break; + p = q; + while (*p && *p != ';') p++; + if (*p) p++; + } + complain(LOG_WARNING, "access to key %s denied to %s:%d", + d.buf, inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + goto done; + +match: + /* Build a tag name for the caller's KEM key, either from the client + * match or the source address. + */ + if (ch != '=') { + DRESET(&dd); + dstr_puts(&dd, "client-"); + dstr_puts(&dd, inet_ntoa(sin->sin_addr)); + p = dd.buf; + } else { + p = q; + while (*q && *q != ';') q++; + if (*q == ';') *q++ = 0; + } + + /* Report the match. */ + complain(LOG_NOTICE, "client %s:%d (`%s') requests key %s", + inet_ntoa(sin->sin_addr), ntohs(sin->sin_port), p, d.buf); + + /* Load the KEM key. */ + if (loadkey(p, k, 0)) goto done; + D( debug_ge("X", k.g, k.X); ) + + /* All complete. */ + rc = 0; + +done: + /* Clean everything up. */ + dstr_destroy(&d); + dstr_destroy(&dd); + if (rc) k_free(k); + return (rc); +} + +static int respond_v0(buf *bin, buf *bout, struct sockaddr_in *sin) +{ + ge *R = 0, *U = 0, *V = 0, *W = 0, *Y = 0, *Z = 0; + mp *r = MP_NEW, *v = MP_NEW; + octet *kk, *t, *tt; + char *p; + size_t sz; + ghash *h = 0; + gmac *m = 0; + gcipher *c = 0; + struct kinfo k; + key *ky; + size_t ksz; + int rc = -1; + + /* Clear out the key state. */ + k_init(&k); + + /* Extract the key tag name. */ + if ((p = buf_getmemz(bin, &sz)) == 0) { + complain(LOG_WARNING, "invalid key tag from %s:%d", + inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + goto done; + } + + /* Find the client's key and check that it's allowed. */ + if (fetch_key(p, sin, &ky, &k)) goto done; + + /* Read the caller's ephemeral key. */ + R = G_CREATE(k.g); W = G_CREATE(k.g); + U = G_CREATE(k.g); V = G_CREATE(k.g); + Y = G_CREATE(k.g); Z = G_CREATE(k.g); + if (G_FROMBUF(k.g, bin, U)) { + complain(LOG_WARNING, "failed to read ephemeral vector from %s:%d", + inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + goto done; + } + D( debug_ge("U", k.g, U); ) + if (BLEFT(bin)) { + complain(LOG_WARNING, "trailing junk in request from %s:%d", + inet_ntoa(sin->sin_addr), ntohs(sin->sin_port)); + goto done; + } + + /* Ephemeral Diffie--Hellman. Choose v in GF(q) at random; compute + * V = v P and -Y = (-v) U. + */ + v = mprand_range(v, k.g->r, &rand_global, 0); + G_EXP(k.g, V, k.g->g, v); + D( debug_mp("v", v); debug_ge("V", k.g, V); ) + v = mp_sub(v, k.g->r, v); + G_EXP(k.g, Y, U, v); + D( debug_ge("-Y", k.g, Y); ) + + /* DLIES. Choose r in GF(q) at random; compute R = r P and Z = r X. Mask + * the clue R as W = R - Y. (Doing the subtraction here makes life easier + * at the other end, since we can determine -Y by negating v whereas the + * recipient must subtract vectors which may be less efficient.) + */ + r = mprand_range(r, k.g->r, &rand_global, 0); + G_EXP(k.g, R, k.g->g, r); + D( debug_mp("r", r); debug_ge("R", k.g, R); ) + G_EXP(k.g, Z, k.X, r); + G_MUL(k.g, W, R, Y); + D( debug_ge("Z", k.g, Z); debug_ge("W", k.g, W); ) + + /* Derive encryption and integrity keys. */ + derive(&k, R, Z, "cipher", k.cc->name, k.cc->keysz, &kk, &ksz); + c = GC_INIT(k.cc, kk, ksz); + derive(&k, R, Z, "mac", k.mc->name, k.mc->keysz, &kk, &ksz); + m = GM_KEY(k.mc, kk, ksz); + + /* Build the ciphertext and compute a MAC tag over it. */ + rc = 0; + if (G_TOBUF(k.g, bout, V) || + G_TOBUF(k.g, bout, W)) + goto done; + if ((t = buf_get(bout, k.tagsz)) == 0) goto done; + sz = ky->k->u.k.sz; + if (BENSURE(bout, sz)) goto done; + GC_ENCRYPT(c, ky->k->u.k.k, BCUR(bout), sz); + h = GM_INIT(m); + GH_HASH(h, BCUR(bout), sz); + tt = GH_DONE(h, 0); memcpy(t, tt, k.tagsz); + BSTEP(bout, sz); + +done: + /* Clear everything up and go home. */ + if (R) G_DESTROY(k.g, R); + if (U) G_DESTROY(k.g, U); + if (V) G_DESTROY(k.g, V); + if (W) G_DESTROY(k.g, W); + if (Y) G_DESTROY(k.g, Y); + if (Z) G_DESTROY(k.g, Z); + if (c) GC_DESTROY(c); + if (m) GM_DESTROY(m); + if (h) GH_DESTROY(h); + if (r) MP_DROP(r); + if (v) MP_DROP(v); + k_free(&k); + return (rc); +} + static int dolisten(int argc, char *argv[]) { int sk; - char *p, *q, ch; - const char *pp; + char *p; char *aspec; ssize_t n; - size_t sz; fd_set fdin; struct sockaddr_in sin; - struct in_addr in; - int mlen; socklen_t len; buf bin, bout; - dstr d = DSTR_INIT, dd = DSTR_INIT; FILE *fp = 0; - key *ky; - key_data **kkd; - mp *r = MP_NEW, *v = MP_NEW; - ge *R = 0, *U = 0, *V = 0, *W = 0, *Y = 0, *Z = 0; - ghash *h = 0; - gmac *m = 0; - gcipher *c = 0; - octet *kk, *t, *tt; - size_t ksz; - struct kinfo k; unsigned bucket = 0, toks; - time_t last = 0, now; + time_t last = 0; /* Set up the socket address. */ sin.sin_family = AF_INET; @@ -542,9 +741,6 @@ static int dolisten(int argc, char *argv[]) for (;;) { - /* Clear out the key state. */ - k_init(&k); - /* Wait for something to happen. */ FD_ZERO(&fdin); FD_SET(sk, &fdin); @@ -558,7 +754,7 @@ static int dolisten(int argc, char *argv[]) if (n < 0) { if (errno != EAGAIN && errno != EINTR) complain(LOG_ERR, "unexpected receive error: %s", strerror(errno)); - goto again; + continue; } /* Refill the bucket, and see whether we should reject this packet. */ @@ -570,182 +766,32 @@ static int dolisten(int argc, char *argv[]) last = now; if (bucket > RATE_CREDIT && grand_range(&rand_global, bucket - RATE_CREDIT)) - goto again; + continue; bucket++; /* Set up the input buffer for parsing the request. */ buf_init(&bin, ibuf, n); - - /* Extract the key tag name. */ - if ((p = buf_getmemz(&bin, &sz)) == 0) { - complain(LOG_WARNING, "invalid key tag from %s:%d", - inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - goto again; - } - - /* Find the key. */ - kfupdate(); - if (key_qtag(kf, p, &d, &ky, &kkd)) { - complain(LOG_WARNING, "unknown key tag `%s' from %s:%d", - p, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - goto again; - } - - /* And make sure that it has the right shape. */ - if ((ky->k->e & KF_ENCMASK) != KENC_BINARY) { - complain(LOG_ERR, "key %s is not plain binary data", d.buf); - goto again; - } - - /* Find the list of clients, and look up the caller's address in the - * list. Entries have the form ADDRESS[/LEN][=TAG] and are separated by - * `;'. - */ - if ((pp = key_getattr(kf, ky, "clients")) == 0) { - complain(LOG_WARNING, - "key %s requested from %s:%d has no `clients' attribute", - d.buf, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - goto again; - } - dstr_puts(&dd, pp); - p = dd.buf; - while (*p) { - q = p; - while (isdigit((unsigned char)*q) || *q == '.') q++; - ch = *q; *q++ = 0; - if (!inet_aton(p, &in)) goto skip; - if (ch != '/') - mlen = 32; - else { - p = q; - while (isdigit((unsigned char)*q)) q++; - ch = *q; *q++ = 0; - mlen = atoi(p); - } - if (((sin.sin_addr.s_addr ^ in.s_addr) & - (0xffffffff << (32 - mlen))) == 0) - goto match; - skip: - if (!ch) break; - p = q; - while (*p && *p != ';') p++; - if (*p) p++; - } - complain(LOG_WARNING, "access to key %s denied to %s:%d", - d.buf, inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - goto again; - - match: - /* Build a tag name for the caller's KEM key, either from the client - * match or the source address. - */ - if (ch != '=') { - DRESET(&dd); - dstr_puts(&dd, "client-"); - dstr_puts(&dd, inet_ntoa(sin.sin_addr)); - p = dd.buf; - } else { - p = q; - while (*q && *q != ';') q++; - if (*q == ';') *q++ = 0; - } - - /* Report the match. */ - complain(LOG_NOTICE, "client %s:%d (`%s') requests key %s", - inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), p, d.buf); - - /* Load the KEM key. */ - if (loadkey(p, &k, 0)) goto again; - D( debug_ge("X", k.g, k.X); ) - - /* Read the caller's ephemeral key. */ - R = G_CREATE(k.g); W = G_CREATE(k.g); - U = G_CREATE(k.g); V = G_CREATE(k.g); - Y = G_CREATE(k.g); Z = G_CREATE(k.g); - if (G_FROMBUF(k.g, &bin, U)) { - complain(LOG_WARNING, "failed to read ephemeral vector from %s:%d", - inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - goto again; - } - D( debug_ge("U", k.g, U); ) - if (BLEFT(&bin)) { - complain(LOG_WARNING, "trailing junk in request from %s:%d", - inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - goto again; - } - - /* Ephemeral Diffie--Hellman. Choose v in GF(q) at random; compute - * V = v P and -Y = (-v) U. - */ - v = mprand_range(v, k.g->r, &rand_global, 0); - G_EXP(k.g, V, k.g->g, v); - D( debug_mp("v", v); debug_ge("V", k.g, V); ) - v = mp_sub(v, k.g->r, v); - G_EXP(k.g, Y, U, v); - D( debug_ge("-Y", k.g, Y); ) - - /* DLIES. Choose r in GF(q) at random; compute R = r P and Z = r X. - * Mask the clue R as W = R - Y. (Doing the subtraction here makes life - * easier at the other end, since we can determine -Y by negating v - * whereas the recipient must subtract vectors which may be less - * efficient.) - */ - r = mprand_range(r, k.g->r, &rand_global, 0); - G_EXP(k.g, R, k.g->g, r); - D( debug_mp("r", r); debug_ge("R", k.g, R); ) - G_EXP(k.g, Z, k.X, r); - G_MUL(k.g, W, R, Y); - D( debug_ge("Z", k.g, Z); debug_ge("W", k.g, W); ) - - /* Derive encryption and integrity keys. */ - derive(&k, R, Z, "cipher", k.cc->name, k.cc->keysz, &kk, &ksz); - c = GC_INIT(k.cc, kk, ksz); - derive(&k, R, Z, "mac", k.mc->name, k.mc->keysz, &kk, &ksz); - m = GM_KEY(k.mc, kk, ksz); - - /* Build the ciphertext and compute a MAC tag over it. */ buf_init(&bout, obuf, sizeof(obuf)); - if (G_TOBUF(k.g, &bout, V) || - G_TOBUF(k.g, &bout, W)) - goto bad; - if ((t = buf_get(&bout, k.tagsz)) == 0) goto bad; - sz = ky->k->u.k.sz; - if (BENSURE(&bout, sz)) goto bad; - GC_ENCRYPT(c, ky->k->u.k.k, BCUR(&bout), sz); - h = GM_INIT(m); - GH_HASH(h, BCUR(&bout), sz); - tt = GH_DONE(h, 0); memcpy(t, tt, k.tagsz); - BSTEP(&bout, sz); + + /* Handle the client's message. */ + if (respond_v0(&bin, &bout, &sin)) continue; /* Send the reply packet back to the caller. */ + if (!BOK(&bout)) goto bad; if (sendto(sk, BBASE(&bout), BLEN(&bout), 0, (struct sockaddr *)&sin, len) < 0) { complain(LOG_ERR, "failed to send response to %s:%d: %s", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port), strerror(errno)); - goto again; + continue; } - goto again; + continue; bad: /* Report a problem building the reply. */ complain(LOG_ERR, "failed to construct response to %s:%d", inet_ntoa(sin.sin_addr), ntohs(sin.sin_port)); - - again: - /* Free stuff for the next iteration. */ - DRESET(&d); DRESET(&dd); - if (R) { G_DESTROY(k.g, R); R = 0; } - if (U) { G_DESTROY(k.g, U); U = 0; } - if (V) { G_DESTROY(k.g, V); V = 0; } - if (W) { G_DESTROY(k.g, W); W = 0; } - if (Y) { G_DESTROY(k.g, Y); Y = 0; } - if (Z) { G_DESTROY(k.g, Z); Z = 0; } - if (c) { GC_DESTROY(c); c = 0; } - if (m) { GM_DESTROY(m); m = 0; } - if (h) { GH_DESTROY(h); h = 0; } - k_free(&k); } return (-1); -- 2.11.0