}
#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.
#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;
for (;;) {
- /* Clear out the key state. */
- k_init(&k);
-
/* Wait for something to happen. */
FD_ZERO(&fdin);
FD_SET(sk, &fdin);
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. */
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);